Chinaunix首页 | 论坛 | 博客
  • 博客访问: 328961
  • 博文数量: 67
  • 博客积分: 668
  • 博客等级: 上士
  • 技术积分: 1591
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-16 22:08
文章分类

全部博文(67)

文章存档

2015年(1)

2014年(13)

2013年(28)

2012年(23)

2011年(2)

分类: LINUX

2012-04-24 10:53:07

input子系统学习进行简单的总结。先看driver/input/input.c
入口函数input_init

点击(此处)折叠或打开

  1. static int __init input_init(void)
  2. {    //此函数主要代码
  3.     input_init_abs_bypass();
  4.     err = class_register(&input_class);
  5.     err = input_proc_init();
  6.     err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
  7. }

register_chrdev注册了一个字符设备,此设备号INPUT_MAJOR被定义成13。

点击(此处)折叠或打开

  1. static const struct file_operations input_fops = {
  2.     .owner = THIS_MODULE,
  3.     .open = input_open_file,
  4. };

注册的此设备的file_operations中只有open函数。这样当有程序调用此驱动的open函数时,input_open_file函数将执行。

点击(此处)折叠或打开

  1. static int input_open_file(struct inode *inode, struct file *file)
  2. {
  3.     struct input_handler *handler;
  4.     const struct file_operations *old_fops, *new_fops = NULL;

  5.     lock_kernel();
  6.     handler = input_table[iminor(inode) >> 5];        //(1)
  7.     if (!handler || !(new_fops = fops_get(handler->fops))) {
  8.         err = -ENODEV;
  9.         goto out;
  10.     }
  11.     if (!new_fops->open) {
  12.         fops_put(new_fops);
  13.         err = -ENODEV;
  14.         goto out;
  15.     }
  16.     old_fops = file->f_op;    
  17.     file->f_op = new_fops;         //(2)

  18.     err = new_fops->open(inode, file);    //(3)

  19.     fops_put(old_fops);    //减少旧file计数引用
  20. out:
  21.     unlock_kernel();
  22.     return err;
  23. }
(1)input_table是一个全局的input_handler结构体指针数组,定义如static struct input_handler *input_table[8],iminor(inode) >> 5 将inode设备文件节点的次设备号除以32。取出input_table中相应handler。
input_handler结构体如下,

点击(此处)折叠或打开

  1. /**
  2.  * struct input_handler - implements one of interfaces for input devices
  3.  * @private: driver-specific data
  4.  * @event: event handler. This method is being called by input core with
  5.  *    interrupts disabled and dev->event_lock spinlock held and so
  6.  *    it may not sleep
  7.  * @connect: called when attaching a handler to an input device
  8.  * @disconnect: disconnects a handler from input device
  9.  * @start: starts handler for given handle. This function is called by
  10.  *    input core right after connect() method and also when a process
  11.  *    that "grabbed" a device releases it
  12.  * @fops: file operations this driver implements
  13.  * @minor: beginning of range of 32 minors for devices this driver
  14.  *    can provide
  15.  * @name: name of the handler, to be shown in /proc/bus/input/handlers
  16.  * @id_table: pointer to a table of input_device_ids this driver can
  17.  *    handle
  18.  * @blacklist: pointer to a table of input_device_ids this driver should
  19.  *    ignore even if they match @id_table
  20.  * @h_list: list of input handles associated with the handler
  21.  * @node: for placing the driver onto input_handler_list
  22.  *
  23.  * Input handlers attach to input devices and create input handles. There
  24.  * are likely several handlers attached to any given input device at the
  25.  * same time. All of them will get their copy of input event generated by
  26.  * the device.
  27.  *
  28.  * Note that input core serializes calls to connect() and disconnect()
  29.  * methods.
  30.  */
  31. struct input_handler {

  32.     void *private;

  33.     void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
  34.     int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
  35.     void (*disconnect)(struct input_handle *handle);
  36.     void (*start)(struct input_handle *handle);

  37.     const struct file_operations *fops;
  38.     int minor;
  39.     const char *name;

  40.     const struct input_device_id *id_table;
  41.     const struct input_device_id *blacklist;

  42.     struct list_head    h_list;
  43.     struct list_head    node;
  44. };

(2)file是打开的相关设备文件的一个表示,它由内核在open时创建并会传递在该文件上操作的所有函数,直到close一个文件。在这里是用从input_table[]中新取出的new_fops代替file文件中原file_operations.

(3)使用new_fops中的open函数重新打开此设备文件.

input_table[]数组是由什么来初始化的?input_register_handler函数将注册一个新的input handler。函数如下

点击(此处)折叠或打开

  1. int input_register_handler(struct input_handler *handler)
  2. {    //主要代码
  3.     struct input_dev *dev;
  4.     int retval;

  5.     retval = mutex_lock_interruptible(&input_mutex);
  6.     INIT_LIST_HEAD(&handler->h_list);    //(1)

  7.     if (handler->fops != NULL) {        //(2)
  8.         if (input_table[handler->minor >> 5]) {
  9.             retval = -EBUSY;
  10.             goto out;
  11.         }
  12.         input_table[handler->minor >> 5] = handler;
  13.     }
  14.     list_add_tail(&handler->node, &input_handler_list); //(3)
  15.     list_for_each_entry(dev, &input_dev_list, node)        //(4)
  16.         input_attach_handler(dev, handler);
  17.     input_wakeup_procfs_readers();

  18. }

(1)初始化新的handler的h_list成员,也就是list->next = list;list->prev = list; h_list是保存了handle的链表,它是设备和handler联系的结构,后面会分析到。

(2)如果新hanlder的file_operations不为空,则判断这个次设备号除32对于的input_table是否已经被占用,如果是则返回以占用(-EBUSY),否则添加到input_table中。也说明input子系统类型中最多有8中不同的设备hanlder。

(3)将新hanlder的node节点添加到全局的input_handler_list链表中。

(4)list_for_each_entry是定义的一个宏,

点击(此处)折叠或打开

  1. #define list_for_each_entry(pos, head, member)                \
  2.     for (pos = list_entry((head)->next, typeof(*pos), member);    \    
  3.      prefetch(pos->member.next), &pos->member != (head);     \
  4.      pos = list_entry(pos->member.next, typeof(*pos), member))
功能是遍历head中的member成员。pos是容器作用的指针。在这里既是遍历input_dev_list链表中的所有node节点,这个node节点是一个list_head类型,而pos是input_dev类型,list_entry这个宏展开为container_of,它会求得包含了这个node的结构变量。

input_attach_handler(dev, handler)则是匹配这个dev和要注册的这个handler。

点击(此处)折叠或打开

  1. static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
  2. {
  3.     const struct input_device_id *id;
  4.     int error;

  5.     if (handler->blacklist && input_match_device(handler->blacklist, dev))    //(1)
  6.         return -ENODEV;

  7.     id = input_match_device(handler->id_table, dev);        //(2)
  8.     if (!id)
  9.         return -ENODEV;

  10.     error = handler->connect(handler, dev, id);        //(3)
  11.     if (error && error != -ENODEV)
  12.         printk(KERN_ERR
  13.             "input: failed to attach handler %s to device %s, "
  14.             "error: %d\n",
  15.             handler->name, kobject_name(&dev->dev.kobj), error);

  16.     return error;
  17. }
(1)handler中blacklist变量表示此handler应该忽略的设备,所以如果应该忽略的设备是参数传来的设备则返回-ENODEV错误。
(2)返回input_match_device(handler->id_table, dev)匹配的id,类型是struct input_device_id。
(3)调用handler的connect函数链接设备和处理函数。

点击(此处)折叠或打开

  1. 看一下input_device_id 结构体
  2. struct input_device_id {

  3.     kernel_ulong_t flags;    //标志信息

  4.     __u16 bustype;        //总线类型
  5.     __u16 vendor;        //制造商
  6.     __u16 product;        //产品ID
  7.     __u16 version;        //版本

  8.     kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
  9.     kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
  10.     kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
  11.     kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
  12.     kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
  13.     kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
  14.     kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1];
  15.     kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
  16.     kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];

  17.     kernel_ulong_t driver_info;
  18. };

分析input_match_device函数是如何匹配input_device_id类型和input_dev类型设备的

点击(此处)折叠或打开

  1. static const struct input_device_id *input_match_device(const struct input_device_id *id,
  2.                             struct input_dev *dev)
  3. {
  4.     int i;

  5.     for (; id->flags || id->driver_info; id++) {

  6.         if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)    //(1)
  7.             if (id->bustype != dev->id.bustype)    //(2)
  8.                 continue;

  9.         if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)    //匹配生产商
  10.             if (id->vendor != dev->id.vendor)
  11.                 continue;

  12.         if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)    //匹配产品ID
  13.             if (id->product != dev->id.product)
  14.                 continue;

  15.         if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)     //匹配版本
  16.             if (id->version != dev->id.version)
  17.                 continue;

  18.         MATCH_BIT(evbit, EV_MAX);        //(3)
  19.         MATCH_BIT(keybit, KEY_MAX);
  20.         MATCH_BIT(relbit, REL_MAX);
  21.         MATCH_BIT(absbit, ABS_MAX);
  22.         MATCH_BIT(mscbit, MSC_MAX);
  23.         MATCH_BIT(ledbit, LED_MAX);
  24.         MATCH_BIT(sndbit, SND_MAX);
  25.         MATCH_BIT(ffbit, FF_MAX);
  26.         MATCH_BIT(swbit, SW_MAX);

  27.         return id;
  28.     }

  29.     return NULL;
  30. }
(1)设置了总线匹配标志,则进入匹配判断
(2)判断总线是否匹配,如果不匹配则continue 匹配id数组中下一个设备,如果匹配则接着执行下面的代码继续匹配其他项目。
(3)匹配id的evbit和input_dev中evbit的各个位,如果不匹配则continue,数组中下一个设备。MATCH_BIT宏定义如下

点击(此处)折叠或打开

  1. #define MATCH_BIT(bit, max) \
  2.         for (i = 0; i < BITS_TO_LONGS(max); i++) \        //(1)
  3.             if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \    //(2)
  4.                 break; \
  5.         if (i != BITS_TO_LONGS(max)) \  //(3)
  6.             continue;
(1)循环所有位
(2)id的bit[i]中置1则dev的bit[i]也应置1 否则break
(3)如果不是max,即是break出来的而不是循环完的,则continue


阶段小结:
(1)首先在input_init入口函数中调用register_chrdev,注册了一个主设备号13的input字符设备
(2)此驱动的只有open函数,所以在调用open时input_open_file函数会在input_table[]全局数组中找到对应的handler,然后替换

处理函数结构,再用新open打开设备文件。以后所有的操作就会使用input_table中新的操作函数。
(3)input_table[]中结构可以通过input_register_handler函数来注册,且将node添加到input_handler_list中。
(4)在input_register_handler函数注册中会去匹配所有和这个handler相关的在input_dev_list中的设备。
(5)在匹配成功后会调用handler中的connect函数,做链接设备和handler的操作。

以上分析都是分析input核心和handler的注册部分。handler是函数处理部分,那下面则要分析设备部分。分析完设备部分,

最后在看它们之间的联系,之后则就可大致了解input子系统。
-------------------------------------------------------------------------------
上面我们已经知道input类型的设备是用input_dev结构体表示的。

点击(此处)折叠或打开

  1. struct input_dev {
  2.     const char *name;
  3.     const char *phys;
  4.     const char *uniq;
  5.     struct input_id id;

  6.     unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
  7.     unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
  8.     unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
  9.     unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];
  10.     unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];
  11.     unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
  12.     unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
  13.     unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
  14.     unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

  15.     unsigned int keycodemax;
  16.     unsigned int keycodesize;
  17.     void *keycode;
  18.     int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
  19.     int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);

  20.     struct ff_device *ff;

  21.     unsigned int repeat_key;
  22.     struct timer_list timer;

  23.     int sync;

  24.     int abs[ABS_MAX + 1];
  25.     int rep[REP_MAX + 1];

  26.     unsigned long key[BITS_TO_LONGS(KEY_CNT)];
  27.     unsigned long led[BITS_TO_LONGS(LED_CNT)];
  28.     unsigned long snd[BITS_TO_LONGS(SND_CNT)];
  29.     unsigned long sw[BITS_TO_LONGS(SW_CNT)];

  30.     int absmax[ABS_MAX + 1];
  31.     int absmin[ABS_MAX + 1];
  32.     int absfuzz[ABS_MAX + 1];
  33.     int absflat[ABS_MAX + 1];
  34.     int absres[ABS_MAX + 1];

  35.     int (*open)(struct input_dev *dev);
  36.     void (*close)(struct input_dev *dev);
  37.     int (*flush)(struct input_dev *dev, struct file *file);
  38.     int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

  39.     struct input_handle *grab;

  40.     spinlock_t event_lock;
  41.     struct mutex mutex;

  42.     unsigned int users;
  43.     bool going_away;

  44.     struct device dev;

  45.     struct list_head    h_list;
  46.     struct list_head    node;
  47. };

分配一个input_dev结构体需要调用input_allocate_device函数

点击(此处)折叠或打开

  1. struct input_dev *input_allocate_device(void)
  2. {
  3.     struct input_dev *dev;

  4.     dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);        //申请内存
  5.     if (dev) {
  6.         dev->dev.type = &input_dev_type;            //device_type类型
  7.         dev->dev.class = &input_class;                //struct class类型
  8.         device_initialize(&dev->dev);                //初始化input_dev中的device类型
  9.         mutex_init(&dev->mutex);                //初始化互斥量
  10.         spin_lock_init(&dev->event_lock);
  11.         INIT_LIST_HEAD(&dev->h_list);                //list_head类型,初始化h_list
  12.         INIT_LIST_HEAD(&dev->node);                //list_head类型初始化node

  13.         __module_get(THIS_MODULE);
  14.     }

  15.     return dev;
  16. }

注册一个input_dev类型的设备需要调用input_register_device函数,如下

点击(此处)折叠或打开

  1. int input_register_device(struct input_dev *dev)
  2. {
  3.     static atomic_t input_no = ATOMIC_INIT(0);
  4.     struct input_handler *handler;
  5.     const char *path;
  6.     int error;

  7.     __set_bit(EV_SYN, dev->evbit);            //(1)

  8.     /*
  9.      * If delay and period are pre-set by the driver, then autorepeating
  10.      * is handled by the driver itself and we don't do it in input.c.
  11.      */

  12.     init_timer(&dev->timer);    //(2)
  13.     if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
  14.         dev->timer.data = (long) dev;
  15.         dev->timer.function = input_repeat_key;
  16.         dev->rep[REP_DELAY] = 250;
  17.         dev->rep[REP_PERIOD] = 33;
  18.     }

  19.     if (!dev->getkeycode)        //(3)
  20.         dev->getkeycode = input_default_getkeycode;

  21.     if (!dev->setkeycode)        //(4)
  22.         dev->setkeycode = input_default_setkeycode;

  23.     dev_set_name(&dev->dev, "input%ld",        //(5)
  24.          (unsigned long) atomic_inc_return(&input_no) - 1);

  25.     error = device_add(&dev->dev);        //(6)
  26.     if (error)
  27.         return error;

  28.     path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);    //(7)
  29.     printk(KERN_INFO "input: %s as %s\n",
  30.         dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
  31.     kfree(path);

  32.     error = mutex_lock_interruptible(&input_mutex);
  33.     if (error) {
  34.         device_del(&dev->dev);
  35.         return error;
  36.     }

  37.     list_add_tail(&dev->node, &input_dev_list);        //(8)

  38.     list_for_each_entry(handler, &input_handler_list, node)        //(9)
  39.         input_attach_handler(dev, handler);

  40.     input_wakeup_procfs_readers();        //(10)

  41.     mutex_unlock(&input_mutex);

  42.     return 0;
  43. }

(1)设置evbit成员,EN_SYN位,evbit代表设备可以支持的事件类型。?
(2)初始化dev->timer定时器,此定时器用于重复按键,下面6行也是设置重复按键相关
(3)检查是否有getkeycode函数,没有则使用默认函数input_default_getkeycode。此函数功能为获取按键的code值。(?)
(4)逻辑同上,setkeycode,设置按键code值
(5)设置input_dev中的device的名字,形如input1,input2,...这些名字可在内核导出的sysfs文件系统中看到
(6)将input_dev中的device添加到内核设备模型中,可在sysfs中体现。
(7)获得设备路径,在后面要kfree掉返回的字符串,它是kobject_get_path中kzalloc出的空间。 (?)
(8)将此input_dev添加到input_dev_list链表中,此表保存了所有的input设备。
(9)匹配相应的handler,这和之前分析的input_register_handler函数,是一样的。
(10)proc目录信息相关。

顺便看一下evbit可以设置的设备事件类型,

点击(此处)折叠或打开

  1. #define EV_SYN            0x00     //
  2. #define EV_KEY            0x01    //键盘或者按键*
  3. #define EV_REL            0x02    //鼠标设备,相对光标位置
  4. #define EV_ABS            0x03    //触摸屏等,绝对位置
  5. #define EV_MSC            0x04    //
  6. #define EV_SW            0x05    //
  7. #define EV_LED            0x11    //LED
  8. #define EV_SND            0x12    //蜂鸣器
  9. #define EV_REP            0x14    //重复按键类型
  10. #define EV_FF            0x15
  11. #define EV_PWR            0x16    //电源管理
  12. #define EV_FF_STATUS        0x17

阅读(3247) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~