input 就是输入的意思,因此 input 子系统就是管理输入的子系统,和 pinctrl、 gpio 子系统一样,都是 Linux 内核针对某一类设备而创建的框架。比如按键输入、键盘、鼠标、触摸屏等等这些都属于输入设备,为此 input 子系统分为 input 驱动层、 input 核心层、 input 事件处理层,最终给用户空间提供可访问的设备节点。
驱动部分包含三个部分:驱动层,核心层,事件层 驱动层:输入设备的具体驱动程序,比如按键驱动程序,向内核层报告输入内容。 核心层:承上启下,为驱动层提供输入设备注册和操作接口。通知事件层对输入事件进行处理。 事件层:主要和用户空间进行交互。
在使用 input 子系统的时候我们只需要注册一个 input 设备即可, input_dev 结构体表示 input设备
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 121 struct input_dev {122 const char *name;123 const char *phys;124 const char *uniq;125 struct input_id id ;126 127 unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];128 129 unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; 130 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; 131 unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; 132 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; 133 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; 134 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; 135 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];136 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; 137 unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; ...... 189 bool devres_managed;190 };
evbit 表示输入事件类型,可选的事件类型定义在 include/uapi/linux/input.h 文件中,事件类型如下:
1 2 3 4 5 6 7 8 9 10 11 12 #define EV_SYN 0x00 #define EV_KEY 0x01 #define EV_REL 0x02 #define EV_ABS 0x03 #define EV_MSC 0x04 #define EV_SW 0x05 #define EV_LED 0x11 #define EV_SND 0x12 #define EV_REP 0x14 #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17
Linux 内核定义了很多按键值,这些按键值定义在 include/uapi/linux/input.h 文件中,按键值如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 215 #define KEY_RESERVED 0 216 #define KEY_ESC 1 217 #define KEY_1 2 218 #define KEY_2 3 219 #define KEY_3 4 220 #define KEY_4 5 221 #define KEY_5 6 222 #define KEY_6 7 223 #define KEY_7 8 224 #define KEY_8 9 225 #define KEY_9 10 226 #define KEY_0 11 ...... 794 #define BTN_TRIGGER_HAPPY39 0x2e6 795 #define BTN_TRIGGER_HAPPY40 0x2e7
在编写 input 设备驱动的时候我们需要先申请一个 input_dev 结构体变量,使用input_allocate_device 函数来申请一个 input_dev,
1 struct input_dev *input_allocate_device (void )
如果要注销的 input 设备的话需要使用 input_free_device 函数来释放掉前面申请到的input_dev, i
1 void input_free_device (struct input_dev *dev)
申请好一个 input_dev 以后就需要初始化这个 input_dev,需要初始化的内容主要为事件类型(evbit)和事件值(keybit)这两种。 input_dev 初始化完成以后就需要向 Linux 内核注册 input_dev了,需要用到 input_register_device 函数
1 int input_register_device (struct input_dev *dev)
①、使用 input_allocate_device 函数申请一个 input_dev。 ②、初始化 input_dev 的事件类型以及事件值。 ③、使用 input_register_device 函数向 Linux 系统注册前面初始化好的 input_dev。 ④、卸载input驱动的时候需要先使用input_unregister_device 函数注销掉注册的input_dev,然后使用 input_free_device 函数释放掉前面申请的 input_dev。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 struct input_dev *inputdev ;static int __init xxx_init (void ) { ... inputdev = input_allocate_device(); inputdev -> name = "test_inputdev" ; __set_bit(EV_KEY, inputdev->evbit); __set_bit(EV_REP, inputdev->evbit); __set_bit(KEY_0, inputdev->keyit); keyinputdev.inputdev->evbit[0 ] = BIT_MASK(EV_KEY)|BIT_MASK(EV_REP); keyinputdev.inputdev->keyit[BIT_WORD(KEY_0)] |= BIT_MASK(KEY_0); keyinputdev.inputdev->evbit[0 ] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); input_set_capability(keyinputdev.inputdev, EV_KEY, KEY_0); input_register_device(inputdev); ... return 0 ; } static void __exit xxx_exit (void ) { input_unregister_device(inputdev); input_free_device(inputdev); }
上报输入事件 Linux 内核注册好 input_dev 不能顺利的使用input设备, input 设备都是具有输入功能的,但是具体是什么样的输入值 Linux 内核是不知道的,需要获取到具体的输入值,或者说是输入事件,然后将输入事件上报给 Linux 内核。比如按键,我们需要在按键中断处理函数,或者消抖定时器中断函数中将按键值上报给 Linux 内核,这样 Linux 内核才能获取到正确的输入值。不同的事件,其上报事件的 API 函数不同。
1 2 3 4 5 void input_event (struct input_dev *dev, unsigned int type, unsigned int code, int value)
nput_event 函数可以上报所有的事件类型和事件值, Linux 内核也提供了其他的针对具体事件的上报函数,这些函数其实都用到了 input_event 函数。
1 2 3 4 5 static inline void input_report_key (struct input_dev *dev, unsigned int code, int value){ input_event(dev, EV_KEY, code, !!value); }
同样的还有一些其他的事件上报函数,这些函数如下所示:
1 2 3 4 5 void input_report_rel (struct input_dev *dev, unsigned int code, int value) void input_report_abs (struct input_dev *dev, unsigned int code, int value) void input_report_ff_status (struct input_dev *dev, unsigned int code, int value) void input_report_switch (struct input_dev *dev, unsigned int code, int value) void input_mt_sync (struct input_dev *dev)
上报事件以后还需要使用 input_sync 函数来告诉 Linux 内核 input 子系统上报结束,input_sync 函数本质是上报一个同步事件,
1 void input_sync (struct input_dev *dev)
eg:按键的上报事件代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void timer_function (unsigned long arg) { unsigned char value; value = gpio_get_value(keydesc->gpio); if (value == 0 ){ input_report_key(inputdev, KEY_0, 1 ); input_sync(inputdev); } else { input_report_key(inputdev, KEY_0, 0 ); input_sync(inputdev); } }
Linux 内核使用 input_event 这个结构体来表示所有的输入事件, input_envent 结构体定义在include/uapi/linux/input.h 文件中,结构体内容如下:
1 2 3 4 5 6 struct input_event { struct timeval time ; __u16 type; __u16 code; __s32 value; };
关于时间timeval的结构体
1 2 3 4 5 6 7 typedef long __kernel_long_t ;typedef __kernel_long_t __kernel_time_t ;typedef __kernel_long_t __kernel_suseconds_t ;struct timeval { __kernel_time_t tv_sec; __kernel_suseconds_t tv_usec; };
Linux 自带按键驱动程序的使用 编译内核make menuconfig
使 用 Linux 内 核 自 带 的 按 键 驱 动 程 序 添加设备节点。
①、节点名字为“gpio-keys”。 ②、 gpio-keys 节点的 compatible 属性值一定要设置为“gpio-keys”。 ③、所有的 KEY 都是 gpio-keys 的子节点,每个子节点可以用如下属性描述自己: gpios: KEY 所连接的 GPIO 信息。 interrupts: KEY 所使用 GPIO 中断信息,不是必须的,可以不写。 label: KEY 名字 linux,code: KEY 要模拟的按键,也就是示例代码 58.1.2.4 中的这些按键。 ④、如果按键要支持连按的话要加入 autorepeat。 gpio-keys 节点内容
1 2 3 4 5 6 7 8 9 10 11 gpio-keys { compatible = "gpio-keys" ; #address-cells = <1> ; #size-cells = <0> ; autorepeat; key0 { label = "GPIO Key Enter" ; linux,code = <KEY_ENTER>; gpios = <&gpio1 18 GPIO_ACTIVE_LOW>; }; };