Linux input子系统编程、分析与模板

输入设备都有共性:中断驱动+字符IO,基于分层的思想,Linux内核将这些设备的公有的部分提取出来,基于cdev提供接口,设计了输入子系统,所有使用输入子系统构建的设备都使用主设备号13,同时输入子系统也支持自动创建设备文件,这些文件采用阻塞的IO读写方式,被创建在"/dev/input/"下。如下图所示。内核中的输入子系统自底向上分为设备驱动层,输入核心层,事件处理层。由于每种输入的设备上报的事件都各有不同,所以为了应用层能够很好识别上报的事件,内核中也为应用层封装了标准的接口来描述一个事件,这些接口在"/include/upai/linux/input"中。

  • 设备驱动层是具体硬件相关的实现,也是驱动开发中主要完成的部分,
  • 输入核心层主要提供一些API供设备驱动层调用,通过这些API设备驱动层上报的数据就可以传递到事件处理层,
  • 事件处理层负责创建设备文件以及将上报的事件传递到用户空间, 

input的使用

input对象描述了一个输入设备,包括它可能上报的事件,这些事件使用位图来描述,内核提供的相应的工具帮助我们构建一个input对象,大家可以参考内核文档"Documentation/input/input-programming.txt",里面对于input子系统的使用有详细的描述。


  1. //input设备对象 
  2.  struct input_dev { 
  3.          const char *name; 
  4.          unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; 
  5.          unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; 
  6.          unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; 
  7.          unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; 
  8.          unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; 
  9.          unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; 
  10.          unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; 
  11.          unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; 
  12.          unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; 
  13.   
  14.          unsigned long key[BITS_TO_LONGS(KEY_CNT)]; 
  15.          unsigned long led[BITS_TO_LONGS(LED_CNT)]; 
  16.          unsigned long snd[BITS_TO_LONGS(SND_CNT)]; 
  17.          unsigned long sw[BITS_TO_LONGS(SW_CNT)]; 
  18.   
  19.          struct input_handle __rcu *grab; 
  20.   
  21.          struct device dev; 
  22.   
  23.          struct list_head        h_list; 
  24.          struct list_head        node; 
  25.  }; 

struct input_dev

--122--> 这个name不是设备名,input子系统的设备名在子系统源码中指定的,不是这。

--129--> 设备支持的输入事件位图,EV_KEY,EV_REL, etc

--130--> 对于按键事件,设备支持的输入子事件位图

--132--> 对于相对坐标事件,设备支持的相对坐标子事件位图

--133--> 对于绝对坐标事件,设备支持的绝对坐标子事件位图

--134--> 混杂设备的支持的子事件位图

--180-->表示这是一个device。

--182-->h_list是用来链接相关handle的链表

--183-->node用来链接其他input_dev的链表

分配/释放


  1. //drivers/input/input.c 
  2. //创建一个input对象 
  3.  
  4. struct input_dev *input_allocate_device(void);//释放一个input对象 
  5.  
  6. void input_free_device(struct input_dev *dev); 

初始化

初始化一个input对象是使用input子系统编写驱动的主要工作,内核在头文件"include/uapi/linux/input.h"中规定了一些常见输入设备的常见的输入事件,这些宏和数组就是我们初始化input对象的工具。这些宏同时用在用户空间的事件解析和驱动的事件注册,可以看作是驱动和用户空间的通信协议,所以理解其中的意义十分重要。在input子系统中,每一个事件的发生都使用事件(type)->子事件(code)->值(value)三级来描述,比如,按键事件->按键F1子事件->按键F1子事件触发的值是高电平1。注意,事件和子事件和值是相辅相成的,只有注册了事件EV_KEY,才可以注册子事件BTN_0,也只有这样做才是有意义的。

下面就是内核约定的事件类型,对应应用层的事件对象的type域

下面这些是按键子事件的类型,可以看到对PC键值的定义

除了对常用的事件进行描述,内核同样提供了工具将这些事件正确的填充到input对象中描述事件的位图中。


  1. //第一种//这种方式非常适合同时注册多个事件 
  2.  
  3. button_dev->evbit[0] = BIT_MASK(EV_KEY);             
  4. button_dev->keybit[BIT_WORD(BTN_0|BTN_1)] = BIT_MASK(BTN_0|BTN_1);   

注册/注销

初始化好了一个input对象,接下来就需要将其注册到内核


  1. //注册input对象到内核 
  2. int input_register_device(struct input_dev *dev); 
  3. //从内核注销一个input对象 
  4. void input_unregister_device(struct input_dev *dev); 

驱动层报告事件

在合适的时机(由于输入最终是中断表示的,所以通常在驱动的中断处理函数中)驱动可以将注册好的事件上报,且可以同时上报多个事件,下面是内核提供的API


  1. //上报指定的事件+子事件+值 
  2. void input_event( 
  3.    struct input_dev *dev,unsigned int type,unsigned int code,int value);//上报键值 
  4.    void input_report_key(struct input_dev *dev,unsigned int code,int value);//上报绝对坐标 
  5.    void input_report_abs(struct input_dev *dev,unsigned int code,int value);//报告同步事件 
  6.    void input_report_rel(struct input_dev *dev,unsigned int code,int value);//同步所有的上报 
  7.    void input_sync(struct input_dev *dev); 

上报事件有2点需要注意:

  1. report函数们并不会真的上报,只是准备上报,sync才会真的将刚刚report的事件真的上报搭input核心
  2. input核心会进行裁决再上报的事件处理层,所以对于按键事件,一定要先报1再报0(或者反过来),不能只report 1或0, 这样核心会认为是一个事件被误触发了多次而只上报一次,虽然我们真的按下了多次。

应用层解析

事件处理层最终会将驱动sync一次时所有report的事件组织成一个struct input_value[]的形式上报到应用层,在应用层从相应的设备文件中获取上报的事件的时候,需要注意:

  1. 收到数组元素的数量会比底层多一个空元素,类似于写of_device_id[]时最后的空元素,这点应用层在解析的时候需要注意。
  2. 事件处理层并不会缓存收到的事件,如果有新的事件到来,即使旧的事件没有被读取,也会被覆盖,所以应用程序需要及时读取。

前文已经说过,"include/uapi/linux/input.h"中的宏是应用层和驱动层共用的通信协议,所以应用层在解析收到的struct input_value对象的时候,只需要"include <linux/input.h>"即可使用其中的宏。


  1. /* 
  2.  * The event structure itself 
  3.  */ 
  4.  
  5. struct input_event { 
  6.     struct timeval time; 
  7.     __u16 type; 
  8.     __u16 code; 
  9.     __s32 value; 
  10. };  

input分析

上文已经说过,input子系统使用三层结构来实现驱动事件到应用层的传递。具体的,这三个层次每一个层次都由一条结构体链表组成,在设备驱动层,核心结构体是input_dev;在input核心层,是input_handle;在事件处理层,是input_handler。内核通过链表和指针将三者结合到一起,最终实现了input_dev和input_handler的多对多的映射关系,这种关系可用下图简单描述。

模板

下面的这个模板首先使用input子系统上报按键事件,然后在应用层读取。

input按键设备驱动


  1.            key@26{ 
  2.                       compatible = "xj4412,key"; 
  3.                       interrupt-parent = <&gpx1>; 
  4.                       interrupts = <2 2>; 
  5.            }; 
  6. };  

  1. static struct input_dev *button_dev; 
  2. static int button_irq; 
  3. static int irqflags; 
  4. static irqreturn_t button_interrupt(int irq, void *dummy){ 
  5.     input_report_key(button_dev, BTN_0, 0); 
  6.     input_report_key(button_dev, BTN_0, 1); 
  7.     input_sync(button_dev);    return IRQ_HANDLED; 
  8. }  
  9. static int button_init(void){ 
  10.     request_irq(button_irq, button_interrupt,irqflags, "button", NULL)) ; 
  11.      
  12.     button_dev = input_allocate_device(); 
  13.     button_dev->name = "button"; 
  14.     button_dev->evbit[0] = BIT_MASK(EV_KEY); 
  15.     button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); 
  16.      
  17.     input_register_device(button_dev);    return 0; 
  18. static int button_exit(void){ 
  19.     input_free_device(button_dev); 
  20.     free_irq(button_irq, button_interrupt);    return 0;    
  21. static int key_probe(struct platform_device *pdev){ 
  22.     struct resource *irq_res; 
  23.     irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 
  24.     if(irq_res){ 
  25.         button_irq = irq_res->start; 
  26.         irqflags = irq_res->flags & IRQF_TRIGGER_MASK; 
  27.     }else{ 
  28.          return -EINVAL;      
  29.     }    return button_init(); 
  30. static int key_remove(struct platform_device *dev){ 
  31.     return button_exit(); 
  32. struct of_device_id of_tbl[] = { 
  33.     {.compatible = "xj4412,key",}, 
  34.     {}, 
  35. }; 
  36. MODULE_DEVICE_TABLE(of, of_tbl);struct platform_driver key_drv = { 
  37.     .probe = key_probe, 
  38.     .remove = key_remove, 
  39.     .driver.name = "keydrv", 
  40.     .driver.of_match_table = of_tbl, 
  41. }; 
  42. module_platform_driver_register(key_drv); 
  43. MODULE_LICENSE("GPL");  

应用层获取键值


  1. #include <linux/input.h> 
  2. struct input_event {    struct timeval time; 
  3.     unsigned short type; 
  4.     unsigned short code;    int value; 
  5. }; 
  6. int main(int argc, char * const argv[]){ 
  7.     int fd = 0; 
  8.     struct input_event event[3] = {0};      //3!!!,驱动上传了2个事件,第三个用来装空元素  
  9.     int ret = 0; 
  10.     fd = open(argv[1],O_RDONLY); 
  11.     while(1){ 
  12.         ret = read(fd,&event,sizeof(event)); 
  13.         printf("ret:%d,val0:%d,val1:%d,val12:%d\n",ret,event[0].value,event[1].value,event[2].value);          //2!!!,最后一个是空 
  14.         sleep(1); 
  15.     } 
  16.     return 0; 

本文作者:佚名

来源:51CTO

时间: 2024-11-03 13:03:05

Linux input子系统编程、分析与模板的相关文章

Linux input子系统分析

 输入输出是用户和产品交互的手段,因此输入驱动开发在Linux驱动开发中很常见.同时,input子系统的分层架构思想在Linux驱动设计中极具代表性和先进性,因此对Linux input子系统进行深入分析很有意义. 一.input子系统知识点 完整的input子系统分析包括以下几方面: 1) 软件层次 2) 输入子系统分层(input_handler,input_core, input_device) 3) 输入设备(TS)驱动开发 4) evdev handler分析 5) input设备模型

Android驱动之 Linux Input子系统之TP——A/B(Slot)协议【转】

转自:http://www.thinksaas.cn/topics/0/646/646797.html 将A/B协议这部分单独拿出来说一方面是因为这部分内容是比较容易忽视的,周围大多数用到input子系统的开发人员也不甚理解:另一方面是由于这部分知识一旦扩展到TP(触摸屏Touch Panel)的多点触摸就要与Middleware/Framework一起结合起来看才能完全掌握,复杂性所在.这里的Middleware/Framework是针对android来说的,本人从事android这几个层次的

linux内核input子系统解析【转】

作者:刘洪涛,华清远见嵌入式学院讲师.    转自:http://blog.csdn.net/hongtao_liu/article/details/5679171     Android.X windows.qt等众多应用对于linux系统中键盘.鼠标.触摸屏等输入设备的支持都通过.或越来越倾向于标准的input输入子系统.     因为input子系统已经完成了字符驱动的文件操作接口,所以编写驱动的核心工作是完成input系统留出的接口,工作量不大.但如果你想更灵活的应用它,就需要好好的分析

手把手教你写Linux设备驱动---input子系统(一)--input事件应用程序的读写实现(基于友善之臂4412开发板)

     这节,我们来说下input子系统,什么是input子系统? input子系统就是输入子系统.      输入子系统是 Linux内核用于管理各种输入设备 (键盘,鼠标,遥控杆,书写板等等 )的部分,用户通过输入子系统进行内核,命令行,图形接口之间的交换.输入子系统在内核里实现,因为设备经常要通过特定的硬件接口被访问 (例如串口, ps/2, usb等等 ),这些硬件接口由内核保护和管理.内核给用户导出一套固定的硬件无关的 input API,供用户空间程序使用. 输入子系统分为三块:

Linux下c++编程中,STL模板的使用。

问题描述 Linux下c++编程中,STL模板的使用. Linux下c++编程,使用STL模板,为什么只识别#include 而不识别#include ? 程序中只要有#include 就会报无数多个错! 解决方案 难道c++标准模板库STL中没有包含queue,你可以去根目录去看看include头文件中有没有queue.h 解决方案二: STL头文件目录是否被include进项目中,如果自己写makefile的话,查看下INCLUDE的目录,如果IDE的话就查看下项目设置中的include选项

手把手教你写Linux设备驱动---input子系统(四)--电容屏驱动ft5x06编写(一)(基于友善之臂4412开发板)

这一节,我们将从零开始写tiny4412的触摸屏驱动ft5x06,写这节博客之前,先了解下需要什么知识: 1.i2c驱动相关的知识 2.输入子系统 3.中断 4.工作队列 关于i2c驱动相关的知识,在后期的博文里会专门写几篇博文来进行总结,这里就不再说i2c相关的知识,我们先知道怎么用就行了. 首先,我在ts.h构造了一个ts_info_st结构体,用来存放触摸屏的中断线,x坐标,y坐标,压力值. 用ts_st构造了该触摸屏的设备结构体. 我们还是直接看点实际的东西,上代码: ts.h #ifn

手把手教你写Linux设备驱动---input子系统(二)--按键驱动实现(一)(基于友善之臂4412开发板)

在上一节里,我们用一个应用程序实现了鼠标的控制,并控制鼠标用相对位移不断的画一个正方形,感觉非常有意思,这一节,我们将通过一个简单按键实例来真正的实现一个input设备驱动程序. http://blog.csdn.net/morixinguan/article/details/69808832 在写Input驱动之前,我们要了解下这个结构体,在此,我们要包含相应的头文件: #include <linux/input.h> 我们在这个头文件中找到了以下结构体,它就是input设备的核心: //用

手把手教你写Linux设备驱动---input子系统(三)--电容屏事件坐标读取(基于友善之臂4412开发板)

前面我们学习了鼠标是如何如何通过应用程序来读取事件和坐标值的,后面也写了一个简单的input系统的按键驱动程序. 博文如下,讲的内容非常清楚,给小白来入手当然是非常容易的: http://blog.csdn.net/morixinguan/article/details/69808832 这节,我们来学习一下触摸屏事件获取,然后上一个基于4412开发板ft5x0x型号的x,y坐标值读取,后面我们将从零开始实现这款触摸屏的驱动程序: 首先,我们要明白一个概念,触摸屏在input系统中是一类什么事件

Linux输入子系统

 Linux输入子系统(Input Subsystem)         Linux 的输入子系统不仅支持鼠标.键盘等常规输入设备,而且还支持蜂鸣器.触摸屏等设备.本章将对 Linux 输 入子系统进行详细的分析. 一    前言                  输入子系统又叫 input 子系统.其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现 给应用程序.                                           二   设备驱动层