一,前言: 1, 近日要写一个很特殊的键盘驱动,故对linux的input子系统分析了一番,写下分析笔记,以防不日即忘。 2, 使用input子系统的一般流程为:input_allocate_device()申请一个input_dev设备——>初始化该input_dev——>input_register_device()向子系统注册该设备——>中断时input_event()向子系统报告事件。此流程一目了然,即使不看input子系统,我们按照它给出的一般流程也可以把自己写的IO驱动加入到input子系统里去,但input_event()提交完数据后,我们的数据去哪了呢,怎么处理呢,总觉得云里雾里的,总想一探究竟。 3,此分析基于linux内核:2.6.19.2 二:下面给出一个简单的驱动,用定时器来模拟中断来提交键盘的事件。 #include <linux/module.h> #include <linux/init.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/fs.h> #include <linux/timer.h> #include <linux/input.h> #include <linux/delay.h> /*! Input device structure. */ static struct input_dev *serkbd_dev = NULL; static struct timer_list report_timer; static unsigned short t_interval = 2*HZ; #define KEYCODES 8 #define press_left_code 30 #define press_right_code 29 #define press_up_code 28 #define press_down_code 27 #define rel_left_code 158 #define rel_right_code 157 #define rel_up_code 156 #define rel_down_code 155 static u16 serkpd_keycodes[KEYCODES] = { press_left_code, press_right_code, press_up_code, press_down_code, rel_left_code, rel_right_code, rel_up_code, rel_down_code }; static void ser_kpp_handle_timer(unsigned long data) { static int i=0;
input_event(serkbd_dev, EV_KEY, serkpd_keycodes [i/2], ((i%2)==0)?1:0); if(++i >=2*KEYCODES) i=0; report_timer.expires = jiffies + t_interval; add_timer(&report_timer); } static int ser_kpp_open(struct input_dev *dev) { return 0; } static void ser_kpp_close(struct input_dev *dev) { } static int init_ser_keyb() { int i, irq; int retval; unsigned int reg_val; serkbd_dev = input_allocate_device(); if (!serkbd_dev) { printk(KERN_ERR "serkbd_dev: not enough memory for input device/n"); return -ENOMEM; } serkbd_dev->keycode = &serkpd_keycodes; serkbd_dev->keycodesize = sizeof(unsigned char); serkbd_dev->keycodemax = KEYCODES; serkbd_dev->name = "serkpd"; serkbd_dev->id.bustype = BUS_HOST; serkbd_dev->open = ser_kpp_open; serkbd_dev->close = ser_kpp_close; __set_bit(EV_KEY, serkbd_dev->evbit); //设定其支持的事件码 for (i = 0; i < KEYCODES; i++) __set_bit(serkpd_keycodes[i], serkbd_dev->keybit); input_register_device(serkbd_dev);
/* Initialize the polling timer */ init_timer(&report_timer); report_timer.expires = jiffies + t_interval; report_timer.function = ser_kpp_handle_timer; add_timer(&report_timer); return 0; } static int __init ser_kpp_init(void) {
if(init_ser_keyb()){ printk(KERN_INFO "Cannot insmod ser_kpp/n"); return 1; }else{ printk(KERN_INFO "ser-keypad init/n"); return 0; } } static void __exit ser_kpp_cleanup(void) { del_timer(&report_timer); input_unregister_device(serkbd_dev); if (serkbd_dev) input_free_device(serkbd_dev); } module_init(ser_kpp_init); module_exit(ser_kpp_cleanup); MODULE_AUTHOR("sparkle-cliz@sohu.com"); MODULE_DESCRIPTION("input test Driver"); MODULE_LICENSE("GPL"); 现在就利用上面这个驱动来分析一下: 1, input_allocate_device()定义在/drivers/input/input.c下: struct input_dev *input_allocate_device(void) { struct input_dev *dev; dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); if (dev) { dev->cdev.class = &input_class; class_device_initialize(&dev->cdev); mutex_init(&dev->mutex); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); __module_get(THIS_MODULE); } return dev; } 这个函数很简单,主要是申请一个input_dev,再对其作些简单的初始化。 2,然后驱动再对返回的input_dev作进一步的初始化,并用__set_bit(EV_KEY, serkbd_dev->evbit);目的是设定其支持的事件。 3, input_register_device(serkbd_dev),把设备注册进input子系统,定义如下: int input_register_device(struct input_dev *dev) { static atomic_t input_no = ATOMIC_INIT(0); struct input_handle *handle; struct input_handler *handler; const struct input_device_id *id; const char *path; int error; //不管有没有设定过这个事件,都加上对其的支持 set_bit(EV_SYN, dev->evbit); /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */ init_timer(&dev->timer); if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; dev->rep[REP_DELAY] = 250; //加上默认值 dev->rep[REP_PERIOD] = 33; } //把这个新设备加入input_dev_list链表,以后有用(比如遍历这个链表) list_add_tail(&dev->node, &input_dev_list); snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id), "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); //往sys系统里添加相应的信息,在/sys/class/input/里添加一个目录项。 error = class_device_add(&dev->cdev); if (error) return error; error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group); if (error) goto fail1; error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group); if (error) goto fail2; error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group); if (error) goto fail3; path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s/n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); //加入一个新设备的时候会遍历一个input_handler_list链表,以找到目前有哪些handler可以处理这个设备,input_match_device(handler->id_table, dev)是一个匹配函数,下面会作分析。如果匹配成功就调用handler->connect。 list_for_each_entry(handler, &input_handler_list, node) if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) if ((id = input_match_device(handler->id_table, dev))) if ((handle = handler->connect(handler, dev, id))) { input_link_handle(handle); if (handler->start) handler->start(handle); } input_wakeup_procfs_readers(); return 0; fail3: sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group); fail2: sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group); fail1: class_device_del(&dev->cdev); return error; } 3.1,input_match_device定下如下: static const struct input_device_id *input_match_device(const struct input_device_id *id, struct input_dev *dev) { int i; for (; id->flags || id->driver_info; id++) { if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) if (id->bustype != dev->id.bustype) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) if (id->vendor != dev->id.vendor) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) if (id->product != dev->id.product) continue;
if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) if (id->version != dev->id.version) continue; MATCH_BIT(evbit, EV_MAX); MATCH_BIT(keybit, KEY_MAX); MATCH_BIT(relbit, REL_MAX); MATCH_BIT(absbit, ABS_MAX); MATCH_BIT(mscbit, MSC_MAX); MATCH_BIT(ledbit, LED_MAX); MATCH_BIT(sndbit, SND_MAX); MATCH_BIT(ffbit, FF_MAX); MATCH_BIT(swbit, SW_MAX); return id; } return NULL; } Id->flags是包括要匹配的内容,然后是MATCH_BIT匹配每一位。 #define MATCH_BIT(bit, max) / for (i = 0; i < NBITS(max); i++) / //这里必须注意一下,bit不是id或dev的成员,而是一个宏替换。 if ((id->bit[i] & dev->bit[i]) != id->bit[i]) / break; / if (i != NBITS(max)) / continue; 匹配过程中只要有任何不一样的,就直接进入下一个id的匹配。 3.2,这里留心一下input_link_handle(handle) 。 static void input_link_handle(struct input_handle *handle) { list_add_tail(&handle->d_node, &handle->dev->h_list); list_add_tail(&handle->h_node, &handle->handler->h_list); } 可以看到它把&handle->d_node加到了dev->h_list尾部,这个链表就包含了所有能处理这个设备的handler。可以说input_dev和input_handler就是通过input_handle关联起来的。 4, 定时器中断调用input_event()报告事件。该函数定义如下(片断): void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handle *handle; if (type > EV_MAX || !test_bit(type, dev->evbit)) return; add_input_randomness(type, code, value); switch (type) { … … case EV_KEY: if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) return; if (value == 2) break; change_bit(code, dev->key); if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) { dev->repeat_key = code; mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); } break; … … } if (type != EV_SYN) dev->sync = 0; if (dev->grab) dev->grab->handler->event(dev->grab, type, code, value); else list_for_each_entry(handle, &dev->h_list, d_node) if (handle->open) handle->handler->event(handle, type, code, value); } 这个函数先对type判断一下,看是否是支持的事件。然后在switch里面根据不同的type分别作处理。 看到dev->h_list我们应该感到有眼熟了,对了,我们就遍历这个链表,找到所有能处理这个input设备的handler,如果它是打开的就调用它本身的event()。这就是我们开篇时想知道的它的去向了。由此我们就知道处理一个设备的事件可能有不止一个的handler,也就是数据可能流向很多个地方。 三,下面以系统中最简单的一个handler来分析一下它的注册及上面提到的event处理过程。 该文件在drivers/input/evbug.c 1,首先看其入口: static struct input_handler evbug_handler = { .event = evbug_event, .connect = evbug_connect, .disconnect = evbug_disconnect, .name = "evbug", .id_table = evbug_ids, }; static int __init evbug_init(void) { return input_register_handler(&evbug_handler); } 很简单干脆,直接向input子系统注册一个静态的input_handler结构体。 2, input_register_handler函数如下: int input_register_handler(struct input_handler *handler) { struct input_dev *dev; struct input_handle *handle; const struct input_device_id *id; INIT_LIST_HEAD(&handler->h_list); //在此例中,fops==NULL,所以不占用input_table。 if (handler->fops != NULL) { if (input_table[handler->minor >> 5]) return -EBUSY; //把handler保存在input_table里,由minor >> 5可知,高三位相同,低五位不同的从设备号都由同一个handler处理。所以input_table数组成员只有8个。 input_table[handler->minor >> 5] = handler; } list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) if ((id = input_match_device(handler->id_table, dev))) if ((handle = handler->connect(handler, dev, id))) { input_link_handle(handle); if (handler->start) handler->start(handle); } input_wakeup_procfs_readers(); return 0; } 流程跟input_register_device差不多,把handler加入链表input_handler_list——>遍历input_dev_list——> input_match_device()找到匹配设备——>connect()——> input_link_handle()。 3,在此例中,要匹配的id定义为: static const struct input_device_id evbug_ids[] = { { .driver_info = 1 }, /* Matches all devices */ { }, /* Terminating zero entry */ }; 如英文注解,可以匹配所有的input设备。故每匹配完一个设备就调用一次它本身的connect(): static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct input_handle *handle; if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; handle->dev = dev; handle->handler = handler; handle->name = evbug_name input_open_device(handle); printk(KERN_DEBUG "evbug.c: Connected device: /"%s/", %s/n", dev->name, dev->phys); return handle; } 这个函数很简单,它先申请一个input_handle结构体,然后把dev和handler赋给它,可以想像一下,handle有两只手,一只手牵着dev,另一只手牵着handler,这样无论是dev还是handler都可以通过handle找到对方。 4,input_open_device()函数定义如下: int input_open_device(struct input_handle *handle) { struct input_dev *dev = handle->dev; int err; err = mutex_lock_interruptible(&dev->mutex); if (err) return err; handle->open++; if (!dev->users++ && dev->open) err = dev->open(dev); if (err) handle->open--; mutex_unlock(&dev->mutex); return err; } 函数很简单,增加open和users计数,如果第一次打开,就调用dev->open,记得我们的测试驱动中的open函数下定义是这样的: static int ser_kpp_open(struct input_dev *dev) { return 0; } 什么都没做,直接返回0. 5, 最后我们看一下evbug.c里面的event函数。 static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d/n", handle->dev->phys, type, code, value); } 就是input_event()报告事件的时候最终把数据传到这里来。这里只是简单地把这些信息输出来。 四,小结:input_dev在注册的时候会挂在input_dev_list链表上,然后通过遍历input_handler_list尝试匹配已有的每个handler,并把成功匹配的handle挂在dev->h_list链表上,当驱动input_event()报告事件时,就通过dev->h_list链表找到每一个已匹配的handler,并把事件传给它们处理。 而与此对应handler注册时则会把自己挂在input_handler_list上,然后通过遍历input_dev_list链表,看看有没有匹配的input_dev, 有则把自己挂在匹配的dev->h_list上。当有事件到来时,input_handler结构体里面的event函数就可以对事件作处理了。 抓住了这样一个主线,整个input子系统的大概来龙去脉就基本清楚了。 下面顺便贴一下测试结果: root@361-21com:/mnt/sd# insmod ser_keyb.ko
input: serkpd as /class/input/input0 ser-keypad init
root@361-21com:/mnt/sd# evbug.c: Event. Dev: <NULL>, Type: 1, Code: 30, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 30, Value: 0 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 29, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 29, Value: 0 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 28, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 28, Value: 0 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 27, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 27, Value: 0 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 158, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 158, Value: 0 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 157, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 157, Value: 0 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 156, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 156, Value: 0 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 155, Value: 1 evbug.c: Event. Dev: <NULL>, Type: 1, Code: 155, Value: 0
文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/6_system/linux/Linuxjs/20091217/184960_2.html
文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/6_system/linux/Linuxjs/20091217/184960.html