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       /*通用事件类次设备号个数 */ 

 

参考博文:

https://www.cnblogs.com/jason-lu/p/3155105.html

https://blog.csdn.net/qq_16777851/article/details/81211774

posted @ 2020-05-20 22:24  Action_er  阅读(920)  评论(0编辑  收藏  举报