当前位置:   article > 正文

【Linux驱动开发】024 INPUT子系统_linux input 子系统驱动开发

linux input 子系统驱动开发

一、前言

按键、鼠标、键盘、触摸屏等都属于输入(input)设备,Linux 内核为此专门做了一个叫做 input子系统的框架来处理输入事件。输入设备本质上还是字符设备,只是在此基础上套上了 input 框架,用户只需要负责上报输入事件,比如按键值、坐标等信息,input 核心层负责处理这些事件。

按键输入、键盘、鼠标、触摸屏等都属于输入设备,不同的输入设备所代表的含义不同,按键和键盘就是代表按键信息,鼠标和触摸屏代表坐标信息,因此在应用层的处理就不同,对于驱动编写者而言不需要去关心应用层的事情,我们只需要按照要求上报这些输入事件即可。为此 input 子系统分为 input 驱动层、input 核心层、input 事件处理层,最终给用户空间提供可访问的设备节点。

目的:简化驱动开发人员操作,驱动开发者只需上报事件,至于事件如何处理并提供给应用层使用则无需关心。


二、input 子系统简介

左边就是最底层的具体设备,比如按键、USB 键盘/鼠标等,中间部分属于Linux 内核空间,分为驱动层、核心层和事件层,最右边的就是用户空间,所有的输入设备以文件的形式供用户应用程序使用,可以看出 input 子系统用到了我们前面讲解的驱动分层模型。

我们编写驱动程序的时候只需要关注中间的驱动层、核心层和事件层,这三个层的分工如下: 

  • 驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。 
  • 核心层:承上启下,为驱动层提供输入设备注册和操作接口。负责将输入事件从驱动层传递到事件层,并记录输入设备的属性和类型等信息。
  • 事件层:主要和用户空间进行交互。 负责将输入事件传递给用户空间的应用程序,并提供标准的系统调用来读取和处理输入事件。

核心层和事件层之间通过一些接口和数据结构进行交互,以实现输入事件的传递和处理。

也就是说input子系统的核心层和事件层已经写好,驱动开发者只需要按照要求上报事件给核心层即可,无需关心事件的处理和传递,这也体现出驱动分层的思想。


三、input 驱动编写流程 

input 核心层会向 Linux 内核注册一个字符设备,大家找到 drivers/input/input.c 这个文件,input.c 就是 input 输入子系统的核心层。

  1. struct class input_class = {
  2. name = "input",
  3. devnode = input_devnode,
  4. };
  5. ......
  6. static int __init input_init(void)
  7. {
  8. int err;
  9. err = class_register(&input_class);// 向内核注册一个类
  10. if (err) {
  11. pr_err("unable to register input_dev class\n");
  12. return err;
  13. }
  14. err = input_proc_init();
  15. if (err)
  16. goto fail1;
  17. err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0),// 注册字符设备
  18. INPUT_MAX_CHAR_DEVICES, "input");
  19. if (err) {
  20. pr_err("unable to register char major %d", INPUT_MAJOR);
  21. goto fail2;
  22. }
  23. return 0;
  24. fail2: input_proc_exit();
  25. fail1: class_unregister(&input_class);
  26. return err;
  27. }

注释1:注册一个 input 类,这样系统启动以后就会在 /sys/class 目录下有一个 input 子目录。

注释2:注册一个字符设备,主设备号为 INPUT_MAJOR,INPUT_MAJOR 定义在 include/uapi/linux/major.h 文件中:

#define INPUT_MAJOR    13 

因此,input 子系统的所有设备主设备号都为 13,我们在使用 input 子系统处理输入设备的时候就不需要去注册字符设备了,我们只需要向系统注册一个 input_device 即可。(不需要自行注册字符设备,但需要提供一个input_device) 

1、注册 input_dev (该结构体表示input设备)

使用 input 子系统的时候我们只需要注册一个 input 设备即可,input_dev 结构体表示 input 设备:

  1. struct input_dev {
  2. const char *name;
  3. const char *phys;
  4. const char *uniq;
  5. struct input_id id;
  6. unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
  7. unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; /* 事件类型的位图 */
  8. unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; /* 按键值的位图 */
  9. unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; /* 相对坐标的位图 */
  10. unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; /* 绝对坐标的位图 */
  11. unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; /* 杂项事件的位图 */
  12. unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; /*LED 相关的位图 */
  13. unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];/* sound 有关的位图 */
  14. unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; /* 压力反馈的位图 */
  15. unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; /*开关状态的位图 */
  16. bool devres_managed;
  17. };

evbit 表示输入事件类型,另外几个位图是该事件的对应值,比如 keybit 就是按键事件使用的
位图,可选事件类型定义在 include/uapi/linux/input.h 文件中: 

  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 /* sound(声音) */
  9. #define EV_REP 0x14 /* 重复事件 */
  10. #define EV_FF 0x15 /* 压力事件 */
  11. #define EV_PWR 0x16 /* 电源事件 */
  12. #define EV_FF_STATUS 0x17 /* 压力状态事件 */

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/866942

推荐阅读
相关标签