下面是按键驱动的简单例子,这个输入设备只有一个按键,按键被连接到一条中断线上,当按键被按下时,将产生一个中断,内核将检测到这个中断,并对其进行处理。代码含注释如下:
C++代码
- #include <asm/irq.h>
- #include <asm/io.h>
- static struct input_dev *button_dev;
- static irqreturn_t button_interrupt(int,irq,void *dummy)
- {
- input_report_key(button_dev, BTN_0, inb(BUTTON_PORT) & 1);
-
- input_sync(button_dev);
-
- return IRQ_HANDLED;
- }
- static int __init button_init(void)
- {
- int error;
- if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL))
-
- {
-
- printk(KERN_ERR "button.c: Can't allocate irq %d\n", button_irq);
- return -EBUSY;
- }
- button_dev = input_allocate_device();
- if (!button_dev)
- {
- printk(KERN_ERR "button.c: Not enough memory\n");
- error = -ENOMEM;
- goto err_free_irq;
- }
- button_dev->evbit[0] = BIT_MASK(EV_KEY);
- button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
- error = input_register_device(button_dev);
- if (error)
- {
- printk(KERN_ERR "button.c: Failed to register device\n");
- goto err_free_dev;
- }
- return 0;
- err_free_dev:
- input_free_device(button_dev);
- err_free_irq:
- free_irq(BUTTON_IRQ, button_interrupt);
- return error;
- }
- static void __exit button_exit(void)
-
- {
- input_unregister_device(button_dev);
-
- free_irq(BUTTON_IRQ, button_interrupt);
- }
- module_init(button_init);
- module_exit(button_exit);
重要函数分析
这个实例程序代码比较简单,在初始化函数 button_init()中注册了一个中断处理函数,然后并调用 input_register_device()调用 input_allocate_device()函数分配了一个 input_dev 结构体,函数对其进行了注册。在中断处理函数 button_interrupt()中,实例将接收到的按键信息上报给 input 子系统。从而通过 input 子系统,向用户态程序提供按键输入信息。本实例采用了中断方式,除了中断相关的代码外,实例中包含了一些 input 子系统提供的函数,现对其中一些重要的函数进行分析。
input_allocate_device()
C++代码
- struct input_dev *input_allocate_device(void)
- {
- struct input_dev *dev;
- dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
-
- if (dev) {
- dev->dev.type = &input_dev_type;
- dev->dev.class = &input_class;
- device_initialize(&dev->dev);
- mutex_init(&dev->mutex);
- spin_lock_init(&dev->event_lock);
- INIT_LIST_HEAD(&dev->h_list);
- INIT_LIST_HEAD(&dev->node);
- __module_get(THIS_MODULE);
- }
- return dev;
- }
该函数返回一个指向 input_dev 类型的指针,该结构体是一个输入设备结构体,包含了输入设备的一些相关信息,如设备支持的按键码、设备的名称、设备支持的事件等。
注册函数 input_register_device()
input_register_device()函数是输入子系统核心(input core)提供的函数。该函数将 input_dev结构体注册到输入子系统核心中,input_dev 结构体必须由前面讲的 input_allocate_device()函数来分配。input_register_device()函数如果注册失败,必须调用 input_free_device()函数释放分配的空间。如果该函数注册成功,在卸载函数中应该调用 input_unregister_device()函数来注销输入设备结构体。简而言之,注册 input device 的过程就是为 input device 设置默认值,并将其挂以 input_dev_list。与挂载在 input_handler_list 中的 handler 相匹配。如果匹配成功,就会调用 handler 的 connect函数。代码如下:
C++代码
- int input_register_device(struct input_dev *dev)
- {
- static atomic_t input_no = ATOMIC_INIT(0);
- struct input_handler *handler;
- const char *path;
- int error;
-
-
- __set_bit(EV_SYN, dev->evbit);
-
-
- __clear_bit(KEY_RESERVED, dev->keybit);
-
-
- input_cleanse_bitmasks(dev);
-
-
-
-
-
- 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;
- }
-
-
- if (!dev->getkeycode)
- dev->getkeycode = input_default_getkeycode;
-
- if (!dev->setkeycode)
- dev->setkeycode = input_default_setkeycode;
-
- dev_set_name(&dev->dev, "input%ld",
- (unsigned long) atomic_inc_return(&input_no) - 1);
-
- error = device_add(&dev->dev);
- if (error)
- return error;
-
- path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
- printk(KERN_INFO "input: %s as %s\n",
- dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
- kfree(path);
-
- error = mutex_lock_interruptible(&input_mutex);
- if (error) {
- device_del(&dev->dev);
- return error;
- }
-
- list_add_tail(&dev->node, &input_dev_list);
-
- list_for_each_entry(handler, &input_handler_list, node)
- input_attach_handler(dev,handler);
-
- input_wakeup_procfs_readers();
-
- mutex_unlock(&input_mutex);
-
- return 0;
- }
input_attach_handler()
内核中代码如下:
XML/HTML代码
- static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
- {
- const struct input_device_id *id;/*输入设备的指针,该结构体表示设备的标识,标识中存储了设备的信息*/
-
- int error;
- if (handler->blacklist && input_match_device(handler->blacklist,
- dev))/*首先判断 handle 的 blacklist 是否被赋值,如果被赋值,则匹配 blacklist 中的数据跟 dev->id 的数据是否匹配。blacklist 是一个 input_device_id*的类型,其指向 input_device_id的一个表,这个表中存放了驱动程序应该忽略的设备。即使在 id_table 中找到支持的项,也应该忽略这种设备。*/
- return -ENODEV;
-
- id = input_match_device(handler, dev);
- if (!id)
- return -ENODEV;
-
- error = handler->connect(handler, dev, id);/*连接设备和处理函数*/
-
- if (error && error != -ENODEV)
- printk(KERN_ERR
- "input: failed to attach handler %s to device %s, "
- "error: %d\n",
- handler->name, kobject_name(&dev->dev.kobj), error);
-
- return error;
- }
input_device_id
C++代码
- struct input_device_id {
-
- kernel_ulong_t flags;
- __u16 bustype;
- __u16 vendor;
- __u16 product;
- __u16 version;
-
- kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG +
- 1];
- kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1];
- kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1];
-
- kernel_ulong_t driver_info;
-
- };
input_match_device ()
input_match_device()函数匹配 handle->>id_table 和 dev->id 中的数据。
如果不成功则返回。handle->id_table 也是一个 input_device_id 类型的指针,其表示驱动支持的设备列表。input_match_device ()函数用来与 input_dev 和 handler 进行匹配。handler 的 id_table 表中定义了其支持的 input_dev 设备。该函数的代码如下:
Java代码
- static const struct input_device_id *input_match_device(struct input_handler *handler,struct input_dev *dev)
- {
- const struct input_device_id *id;
- int i;
-
-
- for (id = handler->id_table; 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);
-
- if (!handler->match || handler->match(handler, dev))
- return id;
- }
-
- return NULL;
- }
从 MATCH_BIT 宏的定义可以看出。只有当 iput device 和 input handler 的 ID 成员在 evbit、keybit、... swbit 项相同才会匹配成功。而且匹配的顺序是从 evbit、keybit 到 swbit。只要有一项不同,就会循环到 ID 中的下一项进行比较。