加入收藏 | 设为首页 | 会员中心 | 我要投稿 温州站长网 (https://www.0577zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 交互 > 正文

driver: Linux设备模型之input子系统详解

发布时间:2016-10-30 22:36:16 所属栏目:交互 来源:站长网
导读:副标题#e# linux输入子系统 本节从整体上讲解了输入子系统的框架结构。有助于读者从整体上认识linux的输入子系统。在陷入代码分析的过程中,通过本节的知识能够找准方向,明白原理。 本节重点: 输入子系统的框架结构 各层对应内核中的文件位置 输入子系统

EV_MAX    0x1f    事件类型最大个数和提供位掩码支持

由表1可知,设备所能表示的事件种类,一个设备可以选择一个或多个事件类型上报给输入子系统。

Linux输入子系统提供了设备驱动层上报输入事件的函数,在include/linux/input.h中:

voidinput_report_key(struct input_dev *dev, unsigned int code, int value);      //上报按键事件

voidinput_report_rel(struct input_dev *dev, unsigned int code, int value);       //上报相对坐标事件

voidinput_report_abs(struct input_dev *dev, unsigned int code, int value);              //上报绝对坐标事件

……

当提交输入设备产生的输入事件之后,需要调用下面的函数来通知输入子系统,以处理设备产生的完整事件:


[cpp] view plaincopy
  1. void input_sync(struct input_dev *dev);  


2    输入设备驱动的简单案例

在Linux内核文档的documentation/input下,有一个input-programming.txt文件,讲解了编写输入设备驱动程序的核心步骤。

提供的案例代码描述了一个button设备,产生的事件通过BUTTON_PORT引脚获取,当有按下/释放发生时,BUTTON_IRQ被触发,以下是驱动的源代码:


[cpp] view plaincopy
  1. #include                                                                                                           
  2.  #include   
  3.  #include   
  4.   
  5.  #include   
  6.  #include   
  7.   
  8.  static struct input_dev *button_dev;  
  9.   
  10.  static void button_interrupt(int irq, void*dummy, struct pt_regs *fp)  
  11.  {  
  12.         input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);  
  13.         input_sync(button_dev);  
  14.  }        
  15.   
  16.  static int __init button_init(void)  
  17.  {  
  18.         int error;  
  19.           
  20.         if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button",NULL)) {  
  21.                  printk(KERN_ERR"button.c: Can't allocate irq %dn", button_irq);  
  22.                  return -EBUSY;  
  23.         }        
  24.           
  25.          button_dev = input_allocate_device();  
  26.         if (!button_dev) {  
  27.                  printk(KERN_ERR"button.c: Not enough memoryn");  
  28.                  error = -ENOMEM;  
  29.                  goto err_free_irq;  
  30.         }  
  31.   
  32.         button_dev->evbit[0] = BIT(EV_KEY);  
  33.         button_dev->keybit[LONG(BTN_0)] = BIT(BTN_0);  
  34.   
  35.         error = input_register_device(button_dev);  
  36.         if (error) {  
  37.                  printk(KERN_ERR"button.c: Failed to register devicen");  
  38.                  goto err_free_dev;  
  39.         }  
  40.   
  41.         return 0;  
  42.   
  43.  err_free_dev:  
  44.         input_free_device(button_dev);  
  45.  err_free_irq:  
  46.         free_irq(BUTTON_IRQ, button_interrupt);  
  47.         return error;  
  48.  }  
  49.   
  50.  static void __exit button_exit(void)  
  51.  {  
  52.        input_unregister_device(button_dev);  
  53.         free_irq(BUTTON_IRQ, button_interrupt);  
  54. }  
  55.   
  56. module_init(button_init);  
  57. module_exit(button_exit);  

编写基于输入子系统的设备驱动程序需要包含,因为它包含了输入子系统的接口和所有的宏定义,这些内容在编写输入设备驱动程序时需要用到。

button_init函数说明:

当模块加载(insmod)或内核引导过程中,button_init函数会被调用。首先做的工作是获取能够正确控制硬件设备的硬件资源(例如内存、IO内存、中断和DMA),在代码中BUTTON_IRQ作为BUTTON设备的中断资源,通过request_irq()函数被申请注册。当有按键按下/释放时,调用button_interrupt()中断处理函数获取按键值BUTTON_PORT(BUTTON设备的I/O资源)。

那么输入子系统怎么能够知道这个设备为输入设备呢?通过第8行为设备定义一个用于描述一个输入设备对象。


[cpp] view plaincopy
  1. static struct input_dev *button_dev;  


定义了button_dev之后,如何通知输入子系统有新的输入设备了呢?或者说如何把一个新的输入设备加入到输入子系统中呢?可以通过输入子系统核心层input.c中提供的函数分配一个输入设备,在代码的第25行。


[cpp] view plaincopy
  1. button_dev= input_allocate_device();  


有了输入设备的描述,当事件产生时,输入子系统怎么能够知道设备产生的事件类型呢?通过32和33行的代码。


[cpp] view plaincopy
  1. button_dev->evbit[0]= BIT(EV_KEY);  
  2. button_dev->keybit[LONG(BTN_0)]= BIT(BTN_0);  


其中evbit和keybit成员分别代表设备产生的事件类型和上报的按键值。其中输入子系统的一些位操作NBITS、BIT、LONG经常被用到:


[cpp] view plaincopy
  1. #defineNBITS(x) (((x)/BITS_PER_LONG)+1)                 //通过位x获取数组的长度  
  2. #defineBIT(x)       (1UL<<((x)%BITS_PER_LONG))       //返回位x在数组中的位域  
  3. #defineLONG(x) ((x)/BITS_PER_LONG)                        //返回位x的索引  


以上的工作做完之后,即可注册为输入设备了,代码的35行。


[cpp] view plaincopy
  1. input_register_device(button_dev);  


这个函数把button_dev输入设备挂入输入设备链表中,并且通知事件处理层调用connect函数完成设备和事件处理的绑定,当用户打开设备时,便能够调用到相应的事件处理接口获得硬件上报的数据了。input_register_device()函数是会睡眠的函数,因此不能够在中断上下文和持有自旋锁的代码中调用。

当我们把上面的工作做完之后,设备驱动中唯一值得关注的就是button_interrupt()中断处理函数了。当按键动作发生,button_interrupt()函数被调用,完成事件的上报由其中的两条语句完成。


[cpp] view plaincopy
  1. input_report_key(button_dev, BTN_1, inb(BUTTON_PORT) & 1);  
  2. input_sync(button_dev);  


其中input_report_key上报了这是一个按键事件,且它的值为inb(BUTTON_PORT) & 1,由于案例代码只产生一个按键的值,因此input_sync()在这里不起关键作用。但如果是一个触摸屏,即有x坐标和y坐标,则需要通过input_sync()函数把x和y坐标完整地传递给输入子系统。

用于测试的应用层代码:
driver: Linux设备模型之input子系统详解test_key_write.rar
driver: Linux设备模型之input子系统详解test_key_read.rar


(编辑:温州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读