linux设备驱动(12)input子系统(一)基本知识
1.input子系统的介绍
Input驱动程序是linux输入设备的驱动程序,分成游戏杆(joystick)、鼠标(mouse和mice)、键盘(keyboard)、事件设备(event)。其中事件设备驱动程序是目前通用的驱动程序,可支持键盘、鼠标、触摸屏等多种输入设备。它们本身都是字符设备,不过内核为了能将这些设备的共性抽象出来,简化驱动的开发,建立了一个Input子系统。
Linux input 子系统将一个输入设备的输入过程分成了设备驱动(input device driver)和事件驱动(input event driver)两个层。前者负责从底层硬件采集数据;后者负责与用户程序接口,将采集到的数据分发给不同的用户接口。通过这样的设计,将千差万别的设备统一到了为数不多的几种驱动接口上。同一种事件驱动可以用来处理多个同类设备;同一个设备也可以和多种事件驱动相衔接。而事件驱动和设备驱动则由输入核心层进行连接,匹配。
Input子系统分为三层,从下至上分别是输入设备驱动层,输入核心层以及输入事件驱动层.
- 核心层: 高效,无bug,可重用,打包数据,面向应用层。
- 事件处理层: 负责与应用程序交互,向下提供注册接口,连接设备驱动层,向上给具体的hander发送数据。
- 设备驱动层: 负责与底层输入设备交互,面向硬件。
事件处理程序是标准的,对于所有的输入类是通用的,对于驱动工程师不是构建事件处理程序,因为内核已经为我们提供了常用的事件处理程序,基本上如果没有特需要求,我们没有必要去实现事件处理程序。设备驱动程序可以利用一个存在的,合适的事件处理程序通过核心曾和用户应用程序交互。
输入子系统三个层次框架如下:
2 input输入子系统的处理流程
以鼠标按下事件为例处理为例:
(1)设备驱动层:当我们按下鼠标左键的时候就会触发中断(中断是早就注册好的),就会去执行中断所绑定的处理函数,在函数中就会去读取硬件寄存器来判断按下的是哪个按键和状态 。
(2)将按键信息上报给input core层 ---> input core层处理好了之后就会上报给input event层,input event层会将我们的输入事件封装成一个input_event结构体放入一个缓冲区中。
(3)应用层read就会将缓冲区中的数据读取出去。
(4)此外,还存在一个返回路径 (return path)。返回路径允许给一个键盘设置 LED,给一个 force feedback joystick提供 motion commands。路径的两个方向(指从内核到用户的方向和从用户到内核的方向)使用相同的 event定义和不同的 type identifier。
3 主要数据结构
定义位于:include/linux/input.h
3.1 input_dev:
代表着具体的输入设备,它直接从硬件中读取数据,并以事件的形式转发。
1 struct input_dev { 2 const char *name;//设备名字 3 const char *phys;//设备在分层中的路径 4 const char *uniq; 5 struct input_id id;//设备信息 6 /* 可以上报的事件类型有哪些,用位图来表示 */ 7 unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; 8 9 unsigned long evbit[BITS_TO_LONGS(EV_CNT)];//支持所有的事件类型 10 unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];//按键事件类型 11 unsigned long relbit[BITS_TO_LONGS(REL_CNT)];//相对位移事件 12 unsigned long absbit[BITS_TO_LONGS(ABS_CNT)];//绝对位移事件 13 unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)];//其他事件 14 unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];//LED事件 15 unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];//音频事件 16 unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];//受力事件 17 unsigned long swbit[BITS_TO_LONGS(SW_CNT)];//开关机事件 18 19 unsigned int hint_events_per_packet; 20 21 unsigned int keycodemax; 22 unsigned int keycodesize; 23 void *keycode; 24 25 int (*setkeycode)(struct input_dev *dev, 26 const struct input_keymap_entry *ke, 27 unsigned int *old_keycode); 28 int (*getkeycode)(struct input_dev *dev, 29 struct input_keymap_entry *ke); 30 31 struct ff_device *ff; 32 33 unsigned int repeat_key;//重复上报键值,比如:一直按着某一按键不松 34 struct timer_list timer;//重复上报时间 35 36 int rep[REP_CNT]; 37 38 struct input_mt *mt; 39 40 struct input_absinfo *absinfo; 41 42 unsigned long key[BITS_TO_LONGS(KEY_CNT)];//反应设备当前按键状态 43 unsigned long led[BITS_TO_LONGS(LED_CNT)];//反应当前的LED状态 44 unsigned long snd[BITS_TO_LONGS(SND_CNT)];//反应当前的音频输入状态 45 unsigned long sw[BITS_TO_LONGS(SW_CNT)];//当前的开关机状态 46 47 int (*open)(struct input_dev *dev); 48 void (*close)(struct input_dev *dev); 49 int (*flush)(struct input_dev *dev, struct file *file); 50 int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);//上报事件 51 52 struct input_handle __rcu *grab; 53 54 spinlock_t event_lock; 55 struct mutex mutex; 56 57 unsigned int users;//打开设备的用户数量 58 bool going_away; 59 60 struct device dev; 61 62 struct list_head h_list;//用来挂接这个struct input_dev和所有struct handler的链表头 63 struct list_head node;//作为链表节点挂接到 input_dev_list 链表上 (input_dev_list链表是input核心层维护的一个用来挂接所有input设备的一个链表头) 64 65 unsigned int num_vals; 66 unsigned int max_vals; 67 struct input_value *vals; 68 69 bool devres_managed; 70 }
3.2 input_handle
用于将input_device 和 handler 连接起来,对应于某1个具体的设备文件。
1 struct input_handle { 2 3 void *private; 4 5 int open;/*用来做打开计数的*/ 6 const char *name; 7 8 struct input_dev *dev;/*用来指向该handle绑定的input_dev结构体*/ 9 struct input_handler *handler;/*用来指向该handle绑定的handler结构体*/ 10 11 struct list_head d_node;/*把它对应的dev里面的hlist与该handle绑定上*/ 12 struct list_head h_node;/*把它对应的handler里面的hlist与该handle绑定上*/ 13 };
3.3 input_handler
代表接收某一类事件的上层接口,对应于一类事件设备文件
1 struct input_handler { 2 3 void *private;//用户根据具体驱动存放的私有数据 4 5 void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);/*向上层报告事件*/ 6 void (*events)(struct input_handle *handle, 7 const struct input_value *vals, unsigned int count); 8 bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); 9 bool (*match)(struct input_handler *handler, struct input_dev *dev);/*函数用来匹配handler 与 input_dev设备*/ 10 int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);/*当handler 与 input_dev 匹配成功之后用来连接*/ 11 void (*disconnect)(struct input_handle *handle);/*断开handler 与 input_dev 之间的连接*/ 12 void (*start)(struct input_handle *handle); 13 14 bool legacy_minors; 15 int minor;/*该handler 的编号 (在input_table 数组中用来计算数组下标) input_table数组就是input子系统用来管理注册的handler的一个数据结构*/ 16 const char *name; 17 18 const struct input_device_id *id_table;/*里面放置着dev和本handler能匹配在一起的信息*/ 19 20 struct list_head h_list;/*用来挂接handler 上连接的所有handle 的一个链表头*/ 21 struct list_head node;/*作为一个链表节点挂接到 input_handler_list 链表上(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头)*/ 22 }
3.4Client
对应于用户程序对文件的访问接口,每open一次事件驱动,就创建一个client.
定义位于:drivers\input\evdev.c
1 struct evdev_client { 2 unsigned int head;/*为buffer做环形队列做标记*/ 3 unsigned int tail;/*当head和tail相等的时候,说明没有事件*/ 4 unsigned int packet_head; /* [future] position of the first element of next packet */ 5 spinlock_t buffer_lock; /* protects access to buffer, head and tail */ 6 struct fasync_struct *fasync;/*异步通知函数*/ 7 struct evdev *evdev;/*打开的那个evdev设备*/ 8 struct list_head node;/*evdev_client链表项*/ 9 int clkid; 10 unsigned int bufsize; 11 struct input_event buffer[];/*缓冲数据*/ 12 }
3.5 input子系统的核心层维护着两条中要的链表
static LIST_HEAD(input_dev_list);/*记录所有的输入设备*/ static LIST_HEAD(input_handler_list);/*记录所有的事件驱动*/
每当一个新的设备或者一个新的事件驱动被系统加载(调用input_register_device()或 input_register_driver()),都会扫描整个链表,并调用函数input_match_device() 尝试配对工作。Input_handler-->id_table 记录了需要匹配的特征。
3.5input_device_id
定义位于:include\linux\mod_devicetable.h
1 struct input_device_id { 2 3 kernel_ulong_t flags;/*表示我们的这个 input_device_id 是用来匹配下面的4个情况的哪一项.lag == 1表示匹配总线 2表示匹配供应商 4表示匹配产品 8表示匹配版本*/ 4 5 __u16 bustype; 6 __u16 vendor; 7 __u16 product; 8 __u16 version; 9 10 kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1]; 11 kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1]; 12 kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1]; 13 kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1]; 14 kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1]; 15 kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1]; 16 kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1]; 17 kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1]; 18 kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1]; 19 20 kernel_ulong_t driver_info; 21 }
4 总结
三者关系图:
(1)一个dev可以对应不只一个handler,比如一个mouse设备既可以匹配到mousedev里面的handler又可以匹配到evdev里面的handler,对应于上图的dev1,一个dev对应两个handler就必须有两个handle来连接它们。
(2)一个dev设备也可以只对应一个handler,比如我们自行定义的输入设备就只让匹配evdev里面的handler
input的核心层还一个8个数据的数组,里面放着已经注册的某一类的处理接口(如:mouse,event等)
static struct input_handler *input_table[8];
为什么是8呢?
因为目前常用的handler只有三种,evdev,mousedev,joydev,这么三个,而且evdev可以通用,所以定义8个肯定够用了。
定义了8个后,因为次设备号只有256个。所以平均每个设备类的次设备号最多就32个。
下面是系统目前为设备定义的次设备号信息。
#define JOYDEV_MINOR_BASE 0 /* 游戏手柄类次设备号开始位置 */ #define JOYDEV_MINORS 16 /* 游戏手柄类次设备号个数 */ #define MOUSEDEV_MINOR_BASE 32 /*鼠标类次设备号开始位置 */ #define MOUSEDEV_MINORS 32 /* 鼠标类次设备号个数 */ #define EVDEV_MINOR_BASE 64 /*通用事件类次设备号开始位置 */ #define EVDEV_MINORS 32 /*通用事件类次设备号个数 */
参考博文: