1 Linux输入子系统

前面的章节,对字符设备驱动的框架进行了学习,包括中断、poll机制、异步信号通知、原子操作、阻塞、定时器按键消抖等机制。其驱动框架结构如下:

  1. 编写file_operation结构体的成员函数:openreadwrite等。
  2. 在驱动入口函数xx_init()中,调用register_chrdev()注册驱动,生成主设备号major,写入file_operation结构体。
  3. 出口函数中调用unregister_chrdev()卸载驱动。

    这种方式编写的驱动,必须指定具体的设备文件"/dev/buttons"才能打开,而一般的应用程序不会直接去打开"/dev/buttons"设备。这些设备文件只有编写驱动的人员比较清楚,并不具有通用性。因此,为了提高驱动的通用性,以便应用程序可以无缝使用,就需要借助内核现成的驱动框架,将驱动添加到内核的驱动框架中。输入子系统-现成的驱动。

    1 Linux输入子系统的简介

    输入子系统的诞生主要是为了统一分散的、不同类别的输入设备的驱动。具有如下优点:

  4. 统一了物理形态各异的相似的输入设备的处理功能。例如:各种类型的鼠标(PS/2USB、蓝牙)做相同的处理。
  5. 提供用于分发输入报告给用户应用程的简单的事件(event)接口。不在使用创建、管理/dev节点的访问方式,而采用调用输入API发送事件到应用层的形式。Windows这样的应用程序能够无缝地运行于输入子系统提供的事件接口之上。
  6. 抽取出了输入程序的通用部分,简化了驱动程序,并引入了一致性。例如:输入子系统提供可一个底层驱动程序(serio)的集合,支持对串行端口和键盘控制器等硬件输入设备的访问。

    Linux输入子系统(linux input subsystem)由上到下分为三层:输入子系统事件处理层(EventHandler)、输入子系统核心层(InputCore)和输入子系统设备驱动层(Input driver)。

    Input driver:实现对硬件设备的读写访问,中断设置,并将硬件产生的事件转换为InputCore层定义的规范提交给EventHandler

    InputCore:承上启下。为Input driver层提供设备注册和操作的接口。通知EventHandler对事件进行处理。

    EventHandler:用户编程的接口。对驱动层提交的数据进行处理。

    其结构如图所示。

    2输入子系统代码分析

    上面从功能方面对输入子系统的框架进行了介绍,接下来对代码进行分析。

    分析驱动首先从入口函数开始,找到drivers/input/input.c

    1. subsys_initcall(input_init);
    2. module_exit(input_exit);

    入口函数为input_init

    1. static int __init input_init(void)
    2. {
    3. int err;
    4.  
    5. err = class_register(&input_class);///sys/class下创建逻辑类input_class
    6. if (err) {
    7. printk(KERN_ERR "input: unable to register input_dev class ");
    8. return err;
    9. }
    10.  
    11. err = input_proc_init();///proc下面创建
    12. if (err)
    13. goto fail1;
    14.  
    15. //申请一个字符设备,主设备号13
    16. err = register_chrdev(INPUT_MAJOR, "input", &input_fops);
    17. if (err) {
    18. printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
    19. goto fail2;
    20. }
    21.  
    22. return 0;
    23.  
    24. fail2: input_proc_exit();
    25. fail1: class_unregister(&input_class);
    26. return err;
    27. }

    在入口函数中"err = class_register(&input_class);"创建一个input_class类,即在/sys/class下创建目录input

    启动系统后,可以查看到"input"类:

    这里为什么只创建了类,没有使用class_device_create()函数在类下面创建驱动设备?后面在分析。。。

    通过register_chrdev()函数创建驱动设备,其中INPUT_MAJOR宏为13,即创建一个主设备号为13的"input"设备。传入的input_fops结构体定义如下:

    该结构体中只有open成员函数,在挂载该驱动后,就会调用open函数,下面就分析.open函数。

    进入input_open_file函数

    1. static int input_open_file(struct inode *inode, struct file *file)
    2. {
    3. struct input_handler *handler = input_table[iminor(inode) >> 5];//(1)
    4. const struct file_operations *old_fops, *new_fops = NULL;
    5. int err;
    6.  
    7. /* No load-on-demand here? */
    8. if (!handler || !(new_fops = fops_get(handler->fops)))//(2)
    9. return -ENODEV;
    10.  
    11. /*
    12. * That's _really_ odd. Usually NULL ->open means "nothing special",
    13. * not "no device". Oh, well...
    14. */
    15. if (!new_fops->open) {
    16. fops_put(new_fops);
    17. return -ENODEV;
    18. }
    19. old_fops = file->f_op;
    20. file->f_op = new_fops;//(3)
    21.  
    22. err = new_fops->open(inode, file);//(4)
    23.  
    24. if (err) {
    25. fops_put(file->f_op);
    26. file->f_op = fops_get(old_fops);
    27. }
    28. fops_put(old_fops);
    29. return err;
    30. }
  7. 第三行中,iminor(inode)取出设备的次设备号,再除以32,找到input_table数组中选项的索引,并将输入处理函数指针handler指向该选项。
  8. handler 有值,说明已经挂载了该驱动,将handler结构体中的file_operations *fops成员赋值给新的file_operations *new_fops
  9. 在将新的file_operations *new_fops赋值给file->f_op ,此时输入子系统的file_operations就是新挂接的input驱动的file_operations结构体了。
  10. 调用新挂接的input驱动的new_fops->open函数。

    上面这段代码中input_table数组在初始化时并未赋值,这里在drivers/input/input.c文件中查看一下该数组在哪里赋值。

    找到在input_register_handler函数中对数组进行赋值,代码如下:

    1. int input_register_handler(struct input_handler *handler)
    2. {
    3. struct input_dev *dev;
    4.  
    5. INIT_LIST_HEAD(&handler->h_list);
    6.  
    7. if (handler->fops != NULL) {
    8. if (input_table[handler->minor >> 5])
    9. return -EBUSY;
    10.  
    11. input_table[handler->minor >> 5] = handler;
    12. }
    13.  
    14. list_add_tail(&handler->node, &input_handler_list);
    15.  
    16. list_for_each_entry(dev, &input_dev_list, node)
    17. input_attach_handler(dev, handler);
    18.  
    19. input_wakeup_procfs_readers();
    20. return 0;
    21. }

    代码中将handler写入到数组input_table,并将其加入到input_handler_list链表中。

    继续搜索函数input_register_handler被谁调用?

    发现很多文件中都调用了该函数

    这里以evdev.c为例,在evdev_init()函数中调用了input_register_handler()函数。

    1. static int __init evdev_init(void)
    2. {
    3. return input_register_handler(&evdev_handler);
    4. }

    这里evdev_handler结构体的定义如下:

    1. static struct input_handler evdev_handler = {
    2. .event = evdev_event,
    3. .connect = evdev_connect,
    4. .disconnect = evdev_disconnect,
    5. .fops = &evdev_fops,
    6. .minor = EVDEV_MINOR_BASE,
    7. .name = "evdev",
    8. .id_table = evdev_ids,
    9. };

    这是一个input_handler类型的结构体,

    1)成员evdev_fops就是我们需要自己实现的驱动的操作函数。

    2)次设备号EVDEV_MINOR_BASE64,调用input_register_handler函数,64/32=2,即将evdev_handler结构体存放到input_table[2]中。在打开这个evdev input设备时,调用.open实际上就是evdev_handler->evdev_fops.evdev_open函数。

    3)其中第8行的.id_table表示支持哪些输入设备。当驱动设备的id input_dev->idinput_handlerid_table相匹配时,就会调用.connnect连接函数。

    4)第3行的.connect函数是将设备input_devinput_handler建立如下图的关联

    接下来分析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.  
    8. set_bit(EV_SYN, dev->evbit);
    9.  
    10. /*
    11. * If delay and period are pre-set by the driver, then autorepeating
    12. * is handled by the driver itself and we don't do it in input.c.
    13. */
    14.  
    15. init_timer(&dev->timer);
    16. if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
    17. dev->timer.data = (long) dev;
    18. dev->timer.function = input_repeat_key;
    19. dev->rep[REP_DELAY] = 250;
    20. dev->rep[REP_PERIOD] = 33;
    21. }
    22.  
    23. if (!dev->getkeycode)
    24. dev->getkeycode = input_default_getkeycode;
    25.  
    26. if (!dev->setkeycode)
    27. dev->setkeycode = input_default_setkeycode;
    28.  
    29. list_add_tail(&dev->node, &input_dev_list);//(1)
    30.  
    31. snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
    32. "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
    33.  
    34. if (!dev->cdev.dev)
    35. dev->cdev.dev = dev->dev.parent;
    36.  
    37. error = class_device_add(&dev->cdev);
    38. if (error)
    39. return error;
    40.  
    41. path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
    42. printk(KERN_INFO "input: %s as %s ",
    43. dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
    44. kfree(path);
    45.  
    46. list_for_each_entry(handler, &input_handler_list, node)//(2)
    47. input_attach_handler(dev, handler);
    48.  
    49. input_wakeup_procfs_readers();
    50.  
    51. return 0;
    52. }
  11. 4行中,将要注册的input_dev驱动设备添加到链表input_dev_list中;
  12. 46行中,input_handler_list是驱动处理函数结构体链表,list_for_each_entry是将input_handler_list链表中对应的节点取出来,存放到handler中。
  13. 最后调用input_attach_handler,将dev->id依次与handler-id_table进行判断,如果相同就进行连接。

    接着看input_handler的注册函数input_register_handler()

    1. int input_register_handler(struct input_handler *handler)
    2. {
    3. struct input_dev *dev;
    4.  
    5. INIT_LIST_HEAD(&handler->h_list);
    6.  
    7. if (handler->fops != NULL) {
    8. if (input_table[handler->minor >> 5])
    9. return -EBUSY;
    10.  
    11. input_table[handler->minor >> 5] = handler; //(1)
    12. }
    13.  
    14. list_add_tail(&handler->node, &input_handler_list); //(2)
    15.  
    16. list_for_each_entry(dev, &input_dev_list, node) //(3)
    17. input_attach_handler(dev, handler); //(4)
    18.  
    19. input_wakeup_procfs_readers();
    20. return 0;
    21. }
  14. 根据次设备号/32,定位到input_table数组中,将handler放到数组对应的选项中。
  15. 14行,将handler存放到input_handler_list中。
  16. input_dev_list中的对应设备节点取出来存到input_dev中。
  17. 最后调用input_attach_handler,将input_dev->id依次与handler->id_table进行判断,如果相同就进行连接。

    下面来看看input_attach_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.  
    6. if (handler->blacklist && input_match_device(handler->blacklist, dev))
    7. return -ENODEV;
    8.  
    9. id = input_match_device(handler->id_table, dev); //匹配
    10. if (!id)                                         //不相同,退出
    11. return -ENODEV;
    12.  
    13. error = handler->connect(handler, dev, id);         //相同,连接
    14. if (error && error != -ENODEV)
    15. printk(KERN_ERR
    16. "input: failed to attach handler %s to device %s, "
    17. "error: %d ",
    18. handler->name, kobject_name(&dev->cdev.kobj), error);
    19.  
    20. return error;
    21. }

    evdev_connect为例。

    1. static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)
    2. {
    3. struct evdev *evdev;
    4. struct class_device *cdev;
    5. dev_t devt;
    6. int minor;
    7. int error;
    8.  
    9. for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
    10. if (minor == EVDEV_MINORS) {
    11. printk(KERN_ERR "evdev: no more free evdev devices ");
    12. return -ENFILE;
    13. }
    14.  
    15. evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); //分配一个evdev结构体大小的空间
    16. if (!evdev)
    17. return -ENOMEM;
    18.  
    19. INIT_LIST_HEAD(&evdev->client_list);
    20. init_waitqueue_head(&evdev->wait);
    21. //配置
    22. evdev->exist = 1;
    23. evdev->minor = minor;
    24. evdev->handle.dev = dev;
    25. evdev->handle.name = evdev->name;
    26. evdev->handle.handler = handler;
    27. evdev->handle.private = evdev;
    28. sprintf(evdev->name, "event%d", minor);
    29.  
    30. evdev_table[minor] = evdev;
    31.  
    32. devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor), //将主设备号和次设备号转换为dev_t类型
    33.  
    34. cdev = class_device_create(&input_class, &dev->cdev, devt,
    35. dev->cdev.dev, evdev->name); //input_class类下面创建设备dev
    36. if (IS_ERR(cdev)) {
    37. error = PTR_ERR(cdev);
    38. goto err_free_evdev;
    39. }
    40.  
    41. /* temporary symlink to keep userspace happy */
    42. error = sysfs_create_link(&input_class.subsys.kobj,
    43. &cdev->kobj, evdev->name);
    44. if (error)
    45. goto err_cdev_destroy;
    46.  
    47. error = input_register_handle(&evdev->handle); //注册input_handle结构体
    48. if (error)
    49. goto err_remove_link;
    50.  
    51. return 0;
    52.  
    53. err_remove_link:
    54. sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
    55. err_cdev_destroy:
    56. class_device_destroy(&input_class, devt);
    57. err_free_evdev:
    58. kfree(evdev);
    59. evdev_table[minor] = NULL;
    60. return error;
    61. }

    47行调用input_register_handle注册input_handle

    下面来看看input_register_handle函数。

    1. int input_register_handle(struct input_handle *handle)
    2. {
    3. struct input_handler *handler = handle->handler;
    4.  
    5. list_add_tail(&handle->d_node, &handle->dev->h_list); //(1)
    6. list_add_tail(&handle->h_node, &handler->h_list);      //(2)
    7.  
    8. if (handler->start)
    9. handler->start(handle);
    10.  
    11. return 0;
    12. }
  18. 5行中,handle->dev指向的是input_dev,将handle->d_node存放到input_devh_list链表中,即input_devh_list链表指向handle->d_node
  19. 6行中,将handle->h_node存放到input_handler结构体的h_list中,即input_handler结构体的h_list链表指向handle->h_node

    其关系如图所示。

    input_devinput_handler.h_list都指向了handle结构,通过handle的成员.dev.handler就可以分别找到对应的input_devinput_handler,即建立了驱动层和handler层之间的联系。

    回到最开始的问题,应用层如何读取底层驱动的事件?

    来看看底层驱动的.read函数,继续以evdev.c为例,代码如下:

    1. static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
    2. {
    3. struct evdev_client *client = file->private_data;
    4. struct evdev *evdev = client->evdev;
    5. int retval;
    6.  
    7. if (count < evdev_event_size()) //判断应用层传进来的参数是否正确
    8. return -EINVAL;
    9.  
    10. if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK)) //在非阻塞的情况下,若 client->head == client->tail || evdev->exist 没有数据,则返回
    11. return -EAGAIN;
    12.  
    13. retval = wait_event_interruptible(evdev->wait,
    14. client->head != client->tail || !evdev->exist); //等待数据,进入休眠
    15. if (retval)
    16. return retval;
    17.  
    18. if (!evdev->exist)
    19. return -ENODEV;
    20.  
    21. while (client->head != client->tail && retval + evdev_event_size() <= count) {
    22.  
    23. struct input_event *event = (struct input_event *) client->buffer + client->tail;
    24.  
    25. if (evdev_event_to_user(buffer + retval, event)) //向应用层发送事件
    26. return -EFAULT;
    27.  
    28. client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);
    29. retval += evdev_event_size();
    30. }
    31.  
    32. return retval;
    33. }

    上述代码中,第13行进入休眠后,在哪里唤醒呢?

    evdev_event函数中,会调用wake_up_interruptible唤醒函数。

    1. static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
    2. {
    3. struct evdev *evdev = handle->private;
    4. struct evdev_client *client;
    5.  
    6. if (evdev->grab) {
    7. client = evdev->grab;
    8.  
    9. do_gettimeofday(&client->buffer[client->head].time);
    10. client->buffer[client->head].type = type;
    11. client->buffer[client->head].code = code;
    12. client->buffer[client->head].value = value;
    13. client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
    14.  
    15. kill_fasync(&client->fasync, SIGIO, POLL_IN);
    16. } else
    17. list_for_each_entry(client, &evdev->client_list, node) {
    18.  
    19. do_gettimeofday(&client->buffer[client->head].time);
    20. client->buffer[client->head].type = type;
    21. client->buffer[client->head].code = code;
    22. client->buffer[client->head].value = value;
    23. client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
    24.  
    25. kill_fasync(&client->fasync, SIGIO, POLL_IN);
    26. }
    27.  
    28. wake_up_interruptible(&evdev->wait);
    29. }

    evdev_event函数是evdev_handler结构体的成员。

    那么谁会调用evdev_handler.event函数?

    这里input_event()函数会调用.event函数,代码如下

    1. void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
    2. {
    3. struct input_handle *handle;
    4. ......
    5.  
    6. if (dev->grab)
    7. dev->grab->handler->event(dev->grab, type, code, value);
    8. else
    9. list_for_each_entry(handle, &dev->h_list, d_node) //通过dev->h_list结构找到handle结构
    10. if (handle->open)
    11. handle->handler->event(handle, type, code, value); //在通过handle结构结构调用handlerevent函数
    12. }

     标注:

  20. 文章中大量参考博文:https://www.cnblogs.com/lifexy/p/7542989.html ,非常感谢前辈总结的经验,给了我很多帮助和鼓励!
原文地址:https://www.cnblogs.com/beijiqie1104/p/11418082.html