zoukankan      html  css  js  c++  java
  • Linux输入子系统:输入设备编程指南 -- input-programming.txt


    输入设备编程指南(Programming input drivers)

    ~~~~~~~~~~~~~~~~~~~~~~~~~

    1. 新建一个输入设备驱动程序

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    1.0 一个最简单的例子

    ~~~~~~~~~~~~~~~~~~~~~~~~

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    本文由DroidPhone 翻译:http://blog.csdn.net/droidphone
    Kernel版本:V3.4.10
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    以下是一个非常简单的输入设备驱动程序。该设备只有一个按键,它通过BUTTON_PORT这一i/o端口访问,当按下或释放该按键,会发生BUTTON_IRQ中断,驱动程序看起来就像这样:

    1. #include <linux/input.h>  
    2. #include <linux/module.h>  
    3. #include <linux/init.h>  
    4. #include <asm/irq.h>  
    5. #include <asm/io.h>  
    6.   
    7. static struct input_dev *button_dev;  
    8.   
    9. static irqreturn_t button_interrupt(int irq, void *dummy)  
    10. {  
    11.     input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);  
    12.     input_sync(button_dev);  
    13.     return IRQ_HANDLED;  
    14. }  
    15.   
    16. static int __init button_init(void)  
    17. {  
    18.     int error;  
    19.     if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {  
    20.         printk(KERN_ERR "button.c: Can't allocate irq %d ", button_irq);  
    21.         return -EBUSY;  
    22.     }  
    23.     button_dev = input_allocate_device();  
    24.     if (!button_dev) {  
    25.         printk(KERN_ERR "button.c: Not enough memory ");  
    26.         error = -ENOMEM;  
    27.         goto err_free_irq;  
    28.     }  
    29.     button_dev->evbit[0] = BIT_MASK(EV_KEY);  
    30.     button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);  
    31.     error = input_register_device(button_dev);  
    32.     if (error) {  
    33.         printk(KERN_ERR "button.c: Failed to register device ");  
    34.         goto err_free_dev;  
    35.     }  
    36.     return 0;  
    37. err_free_dev:  
    38.     input_free_device(button_dev);  
    39. err_free_irq:  
    40.     free_irq(BUTTON_IRQ, button_interrupt);  
    41.     return error;  
    42. }  
    43.   
    44. static void __exit button_exit(void)  
    45. {  
    46.     input_unregister_device(button_dev);  
    47.     free_irq(BUTTON_IRQ, button_interrupt);  
    48. }  
    49.   
    50. module_init(button_init);  
    51. module_exit(button_exit);  

    1.1 例子驱动的工作过程

    ~~~~~~~~~~~~~~~~~~~~~~~~~

    首先它必须包含头文件<linux/input.h>,它是input子系统的接口,它提供了所有必要的定义信息。

    _init初始化函数,可以通过模块进行加载,也可以编译进内核中,它收集设备需要的资源(也会检查设备是否存在)。

    接着,它用input_allocate_device()分配了一个input device结构,设置它的bitfields,设备驱动程序通过这一方式把自身的信息通知input子系统的其他部分:它能产生或者接受什么事件。我们的例子设备只能产生EV_KEY类型的事件,而且只能发出BTN_0事件code。这样我们只需要设置这两个bits,我们也可以用

    1. set_bit(EV_KEY, button_dev.evbit);  
    2. set_bit(BTN_0, button_dev.keybit);  

    进行设置,但是上面例子代码中的方法可以一次设置更多的位。

    然后,例子驱动用下面的函数注册input device结构:

    1. input_register_device(&button_dev);  

    这会把button_dev结构添加到input driver的全局链表中,调用device handler模块中的_connect函数来通知他一个新的设备出现了。input_register_device()可能会休眠,所以他不能在中断或者持有一个spinlock的情况下被使用。

    功能上,该驱动只使用了函数:

    1. button_interrupt()  

    在每次中断中通过检查按键的状态,并通过以下函数上报:

    1. input_report_key()  

    该函数会进入input子系统。中断服务程序不必检查它是否会给input子系统报告value重复的事件(例如:按下,按下)。因为input_report_*函数自己会对此进行检查。

    然后就是调用:

    1. input_sync()  

    该函数告诉事件的接收者,我们已经发送了一次完整的报告信息。这对于我们这个只有一个按键的设备好像不太重要,但有些情况下它是非常重要的,例如当鼠标移动后,你不希望X和Y值被分开解释,因为那样会被解释为两次移动。

    1.2 dev->open() and dev->close()

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    当驱动因为设备没有提供中断能力时,它需要不停地查询设备的状态,但是如果一直进行这个查询显得有点浪费。有时设备需要使用一些有价值的资源(例如中断)。这时,我们可以使用open和close回调函数来实现动态地停止查询和释放中断和决定何时再次恢复查询和获取中断。要实现这一功能,我们的例子驱动需要添加以下代码:

    1. static int button_open(struct input_dev *dev)  
    2. {  
    3.     if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL)) {  
    4.         printk(KERN_ERR "button.c: Can't allocate irq %d ", button_irq);  
    5.         return -EBUSY;  
    6.     }  
    7.     return 0;  
    8. }  
    9.   
    10. static void button_close(struct input_dev *dev)  
    11. {  
    12.     free_irq(IRQ_AMIGA_VERTB, button_interrupt);  
    13. }  
    14.   
    15. static int __init button_init(void)  
    16. {  
    17. ...  
    18.     button_dev->open = button_open;  
    19.     button_dev->close = button_close;  
    20.     ...  
    21. }  

    需要注意的是,input核心会保持设备的使用计数来保证dev->open()只有当第一个用户连接该设备时才被调用,而dev->close()只有当最后一个用户断开和设备的连接时才被调用。对两个回调的调用都是串行化的。

    当调用成功时,open()回调返回0,返回非0则表示发生了错误。返回类型是void的close()回调必须一直成功。

    1.3 基本事件类型(types)

    ~~~~~~~~~~~~~~~~~~~~~

    最简单的事件类型是EV_KEY,它用于键盘和按钮,它通过以下函数上报给input子系统:

    1. input_report_key(struct input_dev *dev, int code, int value)  
    linux/input.h定义了该类型可用的values和code (从0 到 KEY_MAX)。Value被解释为真假值,也就是任何非0值意味着键被按下,0则意味着键被松开。input子系统的代码只有当value的值和之前的值不同时才会生成一次事件。

    除了EV_KEY外,还有另外两个基本的事件类型:EV_REL和EV_ABS 。它们用来提供设备的相对和绝对值。鼠标移动是一种相对值,鼠标报告相对于上一个位置的相对差值,因为它工作在没有任何绝对坐标系系统中。绝对值事件对游戏操纵杆和数字化仪有用,这些设备工作在一个绝对坐标系统中。

    设备上报EV_REL就像EV_KEY一样简单,只要设置相应的位然后调用以下函数即可:

    1. input_report_rel(struct input_dev *dev, int code, int value)  

    只有当非0的value时,事件才会被产生。

    不过EV_ABS需要一点点特别的处理。在调用input_register_device之前,你需要在input_dev结构中为你的设备所支持的轴填充的额外字段。假如我们的按钮设备有ABS_X轴:

    1. button_dev.absmin[ABS_X] = 0;  
    2. button_dev.absmax[ABS_X] = 255;  
    3. button_dev.absfuzz[ABS_X] = 4;  
    4. button_dev.absflat[ABS_X] = 8;  

    或者,你只需要这样:

    1. input_set_abs_params(button_dev, ABS_X, 0, 255, 4, 8);  

    上述设置适合于一个游系操纵杆设备,它有一个X轴,最小值是0,最大值是255(这表明它必须能提供的范围,偶尔上报超出该范围也不会有问题,但它必须要能达到最大和最小值)

    ,它的噪声范围是+-4,并且有一个大小是8的中心点。

    如果你不需要absfuzz和absflat,你可以把它们设置为0,这意味着它是绝对准确的并总是返回正中心位置(如果他有的话)。

    1.4 BITS_TO_LONGS(), BIT_WORD(), BIT_MASK()

    ~~~~~~~~~~~~~~~~~~~~~~~~~~

    这3个宏来自bitops.h,有助于进行位域计算:

    • BITS_TO_LONGS(x) - 返回x位的位域数组需要多少个long类型来组成。
    • BIT_WORD(x) - 返回位域数组中第x位所对应的按long为单位的索引。
    • BIT_MASK(x) - 返回位x对应的long型的mask值。

    1.5 The id* and name fields

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    dev->name字段应该要在驱动程序注册输入设备之前设置好。它是一个像'Generic button device'之类的对用户友好的设备名字符串

    id*字段包含了总线的ID(PCI,USB...),厂商ID和设备ID。总线IDs在input.h中定义。厂商和设备IDs在pci_ids.h,usb_ids.h和类似的头文件中定义。这些字段也应该在注册设备之前被设置好。

    idtype字段可以被用作输入设备的专有信息。

    这些id和name字段可以通过evdev接口传递到用户空间中。

    1.6 keycode, keycodemax, keycodesize 字段

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    这3个字段用于需要键值映射的输入设备,keycode是一个用于把扫描码转换为输入系统键值的数组,keycodemax是这个数组的大小,keycodesize则是该数组每个元素的大小(以字节为单位)

    用户空间可以通过相应的evdev接口,使用EVIOCGKEYCODE和EVIOCSKEYCODE ioctls来查询和修改当前的扫描码到键值的映射表。当设备填充了3个上述的字段,驱动程序可以根据kernel的默认实现来设置和查询键值映射表。

    1.7 dev->getkeycode() and dev->setkeycode()

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    getkeycode()和setkeycode()回调允许驱动程序覆写由input核心代码提供的对keycode/keycodemax/keycodesize的默认映射机制,从而可以实现稀疏的映射方式。

    1.8 按键的autorepeat

    ~~~~~~~~~~~~~~~~~~

    ... 很简单,它由input.c模块处理。硬件autorepeat没有被使用,因为很多设备不存在该功能,而且就算存在该功能,有时候也不正常(例如,Toshiba笔记本中的键盘)。要使能你的设备的autorepeat功能,只要设置dev->evbit中的EV_REP位即可,其它的事情都有输入子系统处理。

    1.9 其它的事件types, 输出事件处理

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    现在为止,其它的事件types有:

    • EV_LED - 用作键盘的LEDs灯。
    • EV_SND - 用于键盘的蜂鸣器。

    它们和键盘事件很相似,但是它们按另一个方向走动 - 从系统到输入设备驱动程序。如果你的驱动程序可以处理这些事件,必须设置evbit中相应的位,而且要实现一个回调函数:

    1. button_dev->event = button_event;  
    2.   
    3. int button_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);  
    4. {  
    5.     if (type == EV_SND && code == SND_BELL) {  
    6.         outb(value, BUTTON_BELL);  
    7.         return 0;  
    8.     }  
    9.     return -1;  
    10. }  

    这个回调可以在中断上下文或者BH上下文中被调用,所以它不能睡眠,不要处理太长的时间。

    =============================


    input_dev 结构体成员注释


    1. Name
    2. struct input_dev — represents an input device

    3. Synopsis
    4. struct input_dev {
    5.   const char * name; //name of the device
    6.   const char * phys; //physical path to the device in the system hierarchy
    7.   const char * uniq; //unique identification code for the device (if device has it)
    8.   struct input_id id; //id of the device (struct input_id)
    9.   unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; //bitmap of types of events supported by the device (EV_KEY, EV_REL, etc.)
    10.   unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; //bitmap of keys/buttons this device has
    11.   unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; //bitmap of relative axes for the device
    12.   unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; //bitmap of absolute axes for the device
    13.   unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; //bitmap of miscellaneous events supported by the device
    14.   unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; //bitmap of leds present on the device
    15.   unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; //bitmap of sound effects supported by the device
    16.   unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; //bitmap of force feedback effects supported by the device
    17.   unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; //bitmap of switches present on the device
    18.   unsigned int keycodemax; //size of keycode table
    19.   unsigned int keycodesize; //size of elements in keycode table
    20.   void * keycode; //map of scancodes to keycodes for this device
    21.   /*optional method to alter current keymap, used to implement sparse keymaps.
    22.     If not supplied default mechanism will be used*/
    23.   int (* setkeycode) (struct input_dev *dev, int scancode, int keycode);
    24.  /*optional method to retrieve current keymap. If not supplied default mechanism will be used*/
    25.   int (* getkeycode) (struct input_dev *dev, int scancode, int *keycode);
    26.   struct ff_device * ff; //force feedback structure associated with the device if device supports force feedback effects
    27.   unsigned int repeat_key; //stores key code of the last key pressed; used to implement software autorepeat
    28.   struct timer_list timer; //timer for software autorepeat
    29.   int sync; //set to 1 when there were no new events since last EV_SYNC
    30.   int abs[ABS_MAX + 1]; //current values for reports from absolute axes
    31.   int rep[REP_MAX + 1]; //current values for autorepeat parameters (delay, rate)
    32.   unsigned long key[BITS_TO_LONGS(KEY_CNT)]; //reflects current state of device's keys/buttons
    33.   unsigned long led[BITS_TO_LONGS(LED_CNT)]; //reflects current state of device's LEDs
    34.   unsigned long snd[BITS_TO_LONGS(SND_CNT)]; //reflects current state of sound effects
    35.   unsigned long sw[BITS_TO_LONGS(SW_CNT)]; //reflects current state of device's switches
    36.   int absmax[ABS_MAX + 1]; //maximum values for events coming from absolute axes
    37.   int absmin[ABS_MAX + 1]; //minimum values for events coming from absolute axes
    38.   int absfuzz[ABS_MAX + 1]; //describes noisiness for axes
    39.   int absflat[ABS_MAX + 1]; //size of the center flat position (used by joydev)
    40. /*this method is called when the very first user calls input_open_device.
    41.  The driver must prepare the device to start generating events (start polling
    42.  thread, request an IRQ, submit URB, etc.) */
    43.   int (* open) (struct input_dev *dev);
    44.   void (* close) (struct input_dev *dev); //this method is called when the very last user calls input_close_device.
    45. /*purges the device. Most commonly used to get rid of force feedback effects
    46.   loaded into the device when disconnecting from it */
    47.   int (* flush) (struct input_dev *dev, struct file *file);
    48. /*event handler for events sent _to_ the device, like EV_LED or EV_SND.
    49.   The device is expected to carry out the requested action (turn on a LED, play sound, etc.)
    50.   The call is protected by event_lock and must not sleep */
    51.   int (* event) (struct input_dev *dev, unsigned int type, unsigned int code, int value);
    52. /*input handle that currently has the device grabbed (via EVIOCGRAB ioctl).
    53.   When a handle grabs a device it becomes sole recipient for all input events coming from the device */
    54.   struct input_handle * grab;
    55. /*this spinlock is is taken when input core receives and processes a new event for the device (in input_event). Code that accesses and/or modifies parameters of a device (such as keymap or absmin, absmax, absfuzz, etc.) after device has been registered with input core must take this lock. */
    56.   spinlock_t event_lock;
    57.   struct mutex mutex; //serializes calls to open, close and flush methods
    58. /*stores number of users (input handlers) that opened this device. It is used by input_open_device and input_close_device to make sure that dev->open is only called when the first user opens device and dev->close is called when the very last user closes the device */
    59.   unsigned int users;
    60.   int going_away; //marks devices that are in a middle of unregistering and causes input_open_device*() fail with -ENODEV.
    61.   struct device dev; //driver model's view of this device

    62.   struct list_head h_list; //list of input handles associated with the device. When accessing the list dev->mutex must be held
    63.   struct list_head node; //used to place the device onto input_dev_list
    64. };



  • 相关阅读:
    构造代码块重要理解
    Java中静态代码块、构造代码块、构造函数、普通代码块
    MySQL-分组查询(GROUP BY)及二次筛选(HAVING)
    mysql select将多个字段横向合拼到一个字段
    java语言支持的变量类型
    static修饰属性,方法,类
    恶意代码分析----网络环境配置
    Windows反调试技术(下)
    Windows反调试技术(上)
    脱壳入门----常见的寻找OEP的方法
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744624.html
Copyright © 2011-2022 走看看