Input子系统是分层结构,由上图可以分为三层,核心层,硬件驱动层,事件处理层,各个层之间的通信的是通过事件,其传送方向为硬件驱动层-à子系统核心层à事件处理层-à用户空间,下面来分析下每层具体做的工作。
Input子系统结构与功能实现
(1)其中硬件驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,需要驱动程序的作者来编写。
(2)子系统核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
(3)事件处理层负责与用户程序打交道,将硬件驱动层传来的事件报告给用户程序。
1.核心层
输入子系统核心对应于/drivers/input/input.c文件,也是作为一个模块注册到内核的,先分析模块初始化函数。
- static int __init input_init(void)
- {
- int err;
- err = class_register(&input_class);//向内核注册一个类,用于linux设备模型,注册后会在/sys/class下面出现input目录。
- if (err) {
- printk(KERN_ERR "input: unable to register input_dev class\n");
- return err;
- }
- err = input_proc_init();//proc文件系统相关初始化
- if (err)
- goto fail1;
- err = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备
- if (err) {
- printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
- goto fail2;
- }
- return 0;
- fail2: input_proc_exit();
- fail1: class_unregister(&input_class);
- return err;
- }
从上面可以看出其实input设备也是一类字符设备,只不过操作方法交给了输入子系统。所有的主设备号为13的字符设备的操作最终都会转入到input_fops,Input_fops定义如下:
- static const struct file_operations input_fops = {
- .owner = THIS_MODULE,
- .open = input_open_file,
- };
仔细看看,这个和以前的字符设备不一样,该fops只有一个open函数,那么怎么样实现从输入设备获取到值呢?我们来看看input_open_file做了些什么。
- static int input_open_file(struct inode *inode, struct file *file)
- {
- struct input_handler *handler = input_table[iminor(inode) >> 5];
- //根据次设备号在input_table数组中查询相应的处理函数(Input_table[]在input_register_handler中初始化)
- const struct file_operations *old_fops, *new_fops = NULL;
- int err;
- /* No load-on-demand here? */
- if (!handler || !(new_fops = fops_get(handler->fops)))//获取handler结构体的fops
- return -ENODEV;
- /*
- * That's _really_ odd. Usually NULL ->open means "nothing special",
- * not "no device". Oh, well...
- */
- if (!new_fops->open) {
- fops_put(new_fops);
- return -ENODEV;
- }
- old_fops = file->f_op;
- file->f_op = new_fops;// 将handler中的fops换掉当前的fops
- err = new_fops->open(inode, file);//调用新的fops中的open函数
- if (err) {
- fops_put(file->f_op);
- file->f_op = fops_get(old_fops);
- }
- fops_put(old_fops);
- return err;
- }
通过input_table将handler的fops重新获取,那么来分析下input_table是怎么实现的,这个主要是通过iinput_register_handler来注册一个事件处理器。该函数主要是对于每个注册的handler进行保存 input_table[handler->minor >> 5] = handler;
- int input_register_handler(struct input_handler *handler)
- {
- struct input_dev *dev;
- INIT_LIST_HEAD(&handler->h_list);
- if (handler->fops != NULL) {
- if (input_table[handler->minor >> 5])
- return -EBUSY;
- input_table[handler->minor >> 5] = handler;//连接到input_handler_list链表
- //
- }
- list_add_tail(&handler->node, &input_handler_list);//连接到input_handler_list链表
- list_for_each_entry(dev, &input_dev_list, node)
- input_attach_handler(dev, handler);// 将handler与dev进行配对,不过这次主要是遍历input_dev
- input_wakeup_procfs_readers();
- return 0;
- }
其 其上主要是完成加handler加入到链表,同时完成handler与dev的匹配。下面看看input_attach_handler的处理过程。
- 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))
- return -ENODEV;
-
- id = input_match_device(handler->id_table, dev);//其主要是配对handler->id_table与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->cdev.kobj), error);
- return error;
- }
从 从上可以看出,其主要是配对handler->id_table与dev是否有mattch,如果配对成功,则调用handler的connect函数。
- 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;
- }
主 比较input_dev中的id和handler支持的id,这个存放在handler的id_table中,首先看id->driver_info是否有设置,如果设置了,表明他匹配所有的id,例如evdev。然后在根据id->flag来比较,如果成功进入MATCH_BIT,这个宏用来进行按位比较,比较所支持的事件类型。
- #define MATCH_BIT(bit, max) \
- for (i = 0; i < NBITS(max); i++) \
- if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \
- break; \
- if (i != NBITS(max)) \
- continue;
- 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;
- };
每 由此看出,在id->flag中第一了要匹配的项,定义INPUT_DEVICE_ID_MATCH_BUS,则要比较input_device和input_handler的总线类型。其他分别为设备厂商,设备号和设备版本。如果匹配成功,就会调用handler的connect函数。对于不同的connect函数处理的方式不同,下面以evdev为例
- static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
- const struct input_device_id *id)
- {
- struct evdev *evdev;
- struct class_device *cdev;
- dev_t devt;
- int minor;
- int error;
- //说明evdev这个handler可以同时有32个输入设备与他配对,evdev_table中以minor存放evdev结构体。
- for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
- if (minor == EVDEV_MINORS) {
- printk(KERN_ERR "evdev: no more free evdev devices\n");
- return -ENFILE;
- }
- //分配一个evdev结构体,这个结构体是evdev事件处理器特有
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
- if (!evdev)
- return -ENOMEM;
- INIT_LIST_HEAD(&evdev->client_list);
- init_waitqueue_head(&evdev->wait);
- //以上主要是注册一个evdev结构体的handle,然后初始化,连接到input_handler和input_dev
- evdev->exist = 1;
- evdev->minor = minor;
- evdev->handle.dev = dev;
- evdev->handle.name = evdev->name;
- evdev->handle.handler = handler;
- evdev->handle.private = evdev;
- sprintf(evdev->name, "event%d", minor);
- evdev_table[minor] = evdev;
- devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
- cdev = class_device_create(&input_class, &dev->cdev, devt,
- dev->cdev.dev, evdev->name); //注册handle结构体
- if (IS_ERR(cdev)) {
- error = PTR_ERR(cdev);
- goto err_free_evdev;
- }
- /* temporary symlink to keep userspace happy */
- error = sysfs_create_link(&input_class.subsys.kobj,
- &cdev->kobj, evdev->name);
- if (error)
- goto err_cdev_destroy;
- error = input_register_handle(&evdev->handle);
- if (error)
- goto err_remove_link;
- return 0;
- err_remove_link:
- sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
- err_cdev_destroy:
- class_device_destroy(&input_class, devt);
- err_free_evdev:
- kfree(evdev);
- evdev_table[minor] = NULL;
- return error;
- }
函数主要做的都是一些善后的工作,分配一个evdev结构体,并初始化相关成员,evdev结构体中也有input_handle结构,并初始化并注册。那么看看这个handle的实现。
- int input_register_handle(struct input_handle *handle)
- {
- struct input_handler *handler = handle->handler;
- list_add_tail(&handle->d_node, &handle->dev->h_list);
- list_add_tail(&handle->h_node, &handler->h_list);
- if (handler->start)
- handler->start(handle);
- return 0;
- }
这个函数只做了两个工作,把一个handle结构体通过d_node链表项,分别连接到input_dev的h_list,input_handler的h_list上,以后就可以通过h_list就可以遍历相关的input_handle了。input_hande 没有一个全局的链表,它注册的时候将自己分别挂在了input_dev 和input_handler 的h_list上了。通过input_dev 和input_handler就可以找到input_handle 在设备注册和事件处理器, 注册的时候都要进行配对工作,配对后就会实现链接。通过input_handle也可以找到input_dev和input_handler。那么input_dev怎么注册到链表中呢?
- 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); //EN_SYN 这个是设备都要支持的事件类型
- /*
- * 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;//如果没有定义有关重复按键的相关值,就用内核默认的
- }
- if (!dev->getkeycode)
- dev->getkeycode = input_default_getkeycode;
- if (!dev->setkeycode)
- dev->setkeycode = input_default_setkeycode;
- //以上设置的默认函数由input核心提供
- list_add_tail(&dev->node, &input_dev_list); // 将新分配的input设备连接到input_dev_list链表上
- snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
- "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
- if (!dev->cdev.dev)
- dev->cdev.dev = dev->dev.parent;
- error = class_device_add(&dev->cdev);//添加设备节点
- if (error)
- return error;
- 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链表,配对 input_dev 和 input_handler
- list_for_each_entry(handler, &input_handler_list, node)
- input_attach_handler(dev, handler);
- input_wakeup_procfs_readers();
- return 0;
- }
input_register_device完成的主要功能就是:初始化一些默认的值,将自己的device结构添加到linux设备模型当中,将input_dev添加到input_dev_list链表中,然后寻找合适的handler与input_handler配对,配对的核心函数是input_attach_handler,而该函数在input_register_handler中已经分析过了。
下面总结下上面的分析过程,如下图:
in input_dev 是硬件驱动层,代表一个input设备, 通过全局的input_dev_list链接在一起。设备注册的时候实现这个操作。input_handler 是事件处理层,代表一个事件处理器, nput_handler 通过全局的input_handler_list链接在一起。事件处理器注册的时候实现这个操作。input_handle 个人认为属于核心层,代表一个配对的input设备与input事件处理器
下面看看其中比较重要的数据结构input_handle
- struct input_handle {
- //每个配对的事件处理器都会分配一个对应的设备结构,如evdev事件处理器的evdev结构,注意这个结构与设备驱动层的input_dev不同,初始化handle时,保存到这里。
-
void *private;
- int open;
- const char *name;
- struct input_dev *dev;
- struct input_handler *handler;
- struct list_head d_node; //input_handle通过d_node连接到了input_dev上的h_list链表上
- struct list_head h_node; //input_handle通过h_node连接到了input_handler的h_list链表上
- };
那么写输入子系统的驱动程序方法:
1.分配一个input_dev结构体(input_allocate_device)
2.设置 包括设置是哪类事件等
3.注册 input_register_device
4.硬件相关的初始化
阅读(297) | 评论(0) | 转发(0) |