linux驱动移植-输入子系统
一、基本概念
在之前的章节,我们已经介绍了简单驱动的编写,并通过对应查询方式的按键驱动程序的不断改进,逐渐在程序中引入中断、poll、异步通知、互斥机制来完善驱动程序。
虽然我们编写的驱动也实现了读、写等功能,但是确很难被别人的应用程序无缝的链接,纵然别人的应用程序打开了/dev/button设备,并进行了读取,但是确很难明白读取到的数据的意义。
我们在使用linux操作系统的时候,我们可以使用鼠标进行点击,使用键盘、触摸屏等设备进行输入,那么linux系统支持的输入设备如此繁多,linux系统是如何管理如此之多的不同类型、不同原理、不同的输入信息的输入设备的呢?
其实就是通过input输入子系统这套软件体系来完成的,input的引入其目的是将设备输入后对的信息按照一个事先规定好的方式对信息进行一个特殊的处理并放在统一的位置,使得处理过之后的信息可以直接被各应用程序访问获取使用,避免了不同输入类设备之间的差异对系统造成影响,同时提高了效率。
1.1 linux内核架构
linux内核作为操作系统内核,向下承接最底层的硬件驱动,向上提供应用层的接口实现,适用于各类软硬件结合系统。
其拥有五大核心部分:进程管理、内存管理、文件系统、设备驱动与网络模块。
下图是从网上找来的linux内核的架构图,下图反映了应用程序、linux内核、驱动程序、硬件的关系,当然这图中忽略了文件系统、内存管理、进程管理等模块:
1.2 输入子系统框图
从整体上来说,input子系统分为三层:
- 设备驱动层(Drivers): 下图图中的Drivers对应的就是下层设备驱动层,对应各种各样不同的输入设备,设备驱动层可以将硬件输入转换为统一事件形式,向输入核心层汇报;
- 输入核心层(Input Core):输入核心层为设备驱动层提供输入设备注册与操作接口,如input_register_device;为事件驱动层提供handler/handle注册接口,通知事件驱动层对事件进行处理;
- 事件驱动层(Handlers):Handlers对应的就是上层事件驱动层,最右边的代表的是用户空间;事件驱动层和用户空间交互,linux在用户空间将所有设备当做文件来处理,在一般的驱动程序中都提供fops接口,以及在/dev下生成相应的设备文件nod,而在输入子系统中,这些工作都是由事件驱动层完成的;
如下图所示:
从上图可以看出:
- 系统中可以注册多个输入设备,每个输入设备的可以是不同的,例如一台电脑上可以带有鼠标,键盘....;
- 上层中的各个handler(Keyboard/Mouse/Joystick/Event)是属于平行关系,他们都是属于上层。不同的handler下对应的输入设备在应用层中的接口命名方式不一样,例如:
- Mouse下的输入设备在应用层的接口是 /dev/input/mouseX (X代表0、1、2...);
- Joystick下的输入设备在应用层的接口是 /dev/input/jsX(X代表0、1、2...);
- Event下的输入设备在应用层的接口是 /dev/input/eventX(X代表0、1、2...);
- 输入核心层其实是负责协调上层和下层,使得上层和下层之间能够完成数据传递。当下层发生输入事件的时候,整个系统就被激活了,事件就会通过核心层传递到上层对应的一个/多个handler中,最终会传递到用户空间;
1.3 输入子系统的意义
在GUI界面中,用户的自由度太大了,可以做的事情太多了,可以响应不同的输入类设备,而且还能够对不同的输入类设备的输入做出不同的动作。
例如window中的一个软件既可以响应鼠标输入事件,也可以响应键盘输入事件,而且这些事件都是预先不知道的。
input子系统解决了不同的输入类设备的输入事件与应用层之间的数据传输,使得应用层能够获取到各种不同的输入设备的输入事件,input输入子系统能够囊括所有的不同种类的输入设备,在应用层都能够感知到所有发生的输入事件。
以一次鼠标按下事件为例子来说明我们的input输入子系统的工作过程:
- 当我们按下鼠标左键的时候就会触发中断(中断是早就注册好的),就会去执行中断所绑定的处理函数;
- 在函数中就会去读取硬件寄存器来判断按下的是哪个按键和状态 ;
- 将按键信息上报给输入核心层;
- 输入核心层处理好了之后就会上报给事件驱动层;
- 在这里会将我们的输入事件封装成一个input_event结构体放入一个缓冲区中;应用层read就会将缓冲区中的数据读取出去;
1.4 输入子系统的实现要求
输入子系统的实现需要满足以下需求:
- 输入子系统要为每个输入设备在/dev/input目录下生成一个设备文件,以方便应用程序读取指定输入设备产生的事件;
- 对于每一个输入设备,在输入子系统只需要实现其事件获取即可,至于事件如何处理、如何到达设备文件则不需要考虑;
- linux输入设备可以分为事件类(如USB鼠标、USB键盘、触摸屏等)、MOUSE类(特指PS/2接口的输入设备)、游戏杆类3种,为这些输入设备实现的设备文件的接口必须有所差别。因此,输入子系统需要为不同类型的输入设备实现正确的设备文件接口;
二、输入子系统相关数据结构
2.1 input_dev
在linux内核中,input设备用input_dev结构体描述,input_dev在include/linux/input.h文件中定义:
/** * struct input_dev - represents an input device * @name: name of the device * @phys: physical path to the device in the system hierarchy * @uniq: unique identification code for the device (if device has it) * @id: id of the device (struct input_id) * @propbit: bitmap of device properties and quirks * @evbit: bitmap of types of events supported by the device (EV_KEY, * EV_REL, etc.) * @keybit: bitmap of keys/buttons this device has * @relbit: bitmap of relative axes for the device * @absbit: bitmap of absolute axes for the device * @mscbit: bitmap of miscellaneous events supported by the device * @ledbit: bitmap of leds present on the device * @sndbit: bitmap of sound effects supported by the device * @ffbit: bitmap of force feedback effects supported by the device * @swbit: bitmap of switches present on the device * @hint_events_per_packet: average number of events generated by the * device in a packet (between EV_SYN/SYN_REPORT events). Used by * event handlers to estimate size of the buffer needed to hold * events. * @keycodemax: size of keycode table * @keycodesize: size of elements in keycode table * @keycode: map of scancodes to keycodes for this device * @getkeycode: optional legacy method to retrieve current keymap. * @setkeycode: optional method to alter current keymap, used to implement * sparse keymaps. If not supplied default mechanism will be used. * The method is being called while holding event_lock and thus must * not sleep * @ff: force feedback structure associated with the device if device * supports force feedback effects * @repeat_key: stores key code of the last key pressed; used to implement * software autorepeat * @timer: timer for software autorepeat * @rep: current values for autorepeat parameters (delay, rate) * @mt: pointer to multitouch state * @absinfo: array of &struct input_absinfo elements holding information * about absolute axes (current value, min, max, flat, fuzz, * resolution) * @key: reflects current state of device's keys/buttons * @led: reflects current state of device's LEDs * @snd: reflects current state of sound effects * @sw: reflects current state of device's switches * @open: this method is called when the very first user calls * input_open_device(). The driver must prepare the device * to start generating events (start polling thread, * request an IRQ, submit URB, etc.) * @close: this method is called when the very last user calls * input_close_device(). * @flush: purges the device. Most commonly used to get rid of force * feedback effects loaded into the device when disconnecting * from it * @event: event handler for events sent _to_ the device, like EV_LED * or EV_SND. The device is expected to carry out the requested * action (turn on a LED, play sound, etc.) The call is protected * by @event_lock and must not sleep * @grab: input handle that currently has the device grabbed (via * EVIOCGRAB ioctl). When a handle grabs a device it becomes sole * recipient for all input events coming from the device * @event_lock: this spinlock is taken when input core receives * and processes a new event for the device (in input_event()). * Code that accesses and/or modifies parameters of a device * (such as keymap or absmin, absmax, absfuzz, etc.) after device * has been registered with input core must take this lock. * @mutex: serializes calls to open(), close() and flush() methods * @users: stores number of users (input handlers) that opened this * device. It is used by input_open_device() and input_close_device() * to make sure that dev->open() is only called when the first * user opens device and dev->close() is called when the very * last user closes the device * @going_away: marks devices that are in a middle of unregistering and * causes input_open_device*() fail with -ENODEV. * @dev: driver model's view of this device * @h_list: list of input handles associated with the device. When * accessing the list dev->mutex must be held * @node: used to place the device onto input_dev_list * @num_vals: number of values queued in the current frame * @max_vals: maximum number of values queued in a frame * @vals: array of values queued in the current frame * @devres_managed: indicates that devices is managed with devres framework * and needs not be explicitly unregistered or freed. */ struct input_dev { const char *name; const char *phys; const char *uniq; struct input_id id; unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]; unsigned long mscbit[BITS_TO_LONGS(MSC_CNT)]; unsigned long ledbit[BITS_TO_LONGS(LED_CNT)]; unsigned long sndbit[BITS_TO_LONGS(SND_CNT)]; unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; unsigned int hint_events_per_packet; unsigned int keycodemax; unsigned int keycodesize; void *keycode; int (*setkeycode)(struct input_dev *dev, const struct input_keymap_entry *ke, unsigned int *old_keycode); int (*getkeycode)(struct input_dev *dev, struct input_keymap_entry *ke); struct ff_device *ff; unsigned int repeat_key; struct timer_list timer; int rep[REP_CNT]; struct input_mt *mt; struct input_absinfo *absinfo; unsigned long key[BITS_TO_LONGS(KEY_CNT)]; unsigned long led[BITS_TO_LONGS(LED_CNT)]; unsigned long snd[BITS_TO_LONGS(SND_CNT)]; unsigned long sw[BITS_TO_LONGS(SW_CNT)]; int (*open)(struct input_dev *dev); void (*close)(struct input_dev *dev); int (*flush)(struct input_dev *dev, struct file *file); int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); struct input_handle __rcu *grab; spinlock_t event_lock; struct mutex mutex; unsigned int users; bool going_away; struct device dev; struct list_head h_list; struct list_head node; unsigned int num_vals; unsigned int max_vals; struct input_value *vals; bool devres_managed; };
其中部分字段介绍如下:
- name:设备名称;
- phys:系统层次结构中设备物理路径;
- uniq:设备的唯一标识码(如果设备有的话);
- id:设备id;
- evbit、keybit、relbit、absbit、mscbit、ledbit、sndbit、ffbit、swbit:这些是用来表示该input设备能够上报的事件类型有哪些,是用位的方式来表示的;
- open:设备的open函数,开始调用input_open_device时被调用
- close:设备的close函数,最后调用input_close_device时被调用;
- event:事件处理程序,例如EV_LED、EV_SND;
- dev:内置的device结构体变量;
- h_list:用来挂接input_dev设备连接的所有handle的一个链表头;
- node: 用来挂接input_dev 设备连接的所有handle 的一个链表头;
BITS_TO_LONGS宏被定义为:
#define BITS_TO_LONGS(nr) ((nr) + (32 -1) / (32))
用来判断nr个数需要使用几个long类型表示:
- 1~32:1;
- 32~64:2;
- 65~96:3
- ...
结合定义unsigned long evbit[BITS_TO_LONGS(EV_CNT)];就是根据EV_CNT的个数定义一个数组,如果EV_CNT小于32,就定义成evbit[1],如果大于32就装不下了就要使用evbit[2]这么大的数组了。
比如如果我们想让input设备支持按键事件,那么只需要设置dev->evbit数组的EV_KEY,即第1位位1就可以了:
set_bit(EV_KEY, input_dev->evbit)
2.2 input_handler
/** * struct input_handler - implements one of interfaces for input devices * @private: driver-specific data * @event: event handler. This method is being called by input core with * interrupts disabled and dev->event_lock spinlock held and so * it may not sleep * @events: event sequence handler. This method is being called by * input core with interrupts disabled and dev->event_lock * spinlock held and so it may not sleep * @filter: similar to @event; separates normal event handlers from * "filters". * @match: called after comparing device's id with handler's id_table * to perform fine-grained matching between device and handler * @connect: called when attaching a handler to an input device * @disconnect: disconnects a handler from input device * @start: starts handler for given handle. This function is called by * input core right after connect() method and also when a process * that "grabbed" a device releases it * @legacy_minors: set to %true by drivers using legacy minor ranges * @minor: beginning of range of 32 legacy minors for devices this driver * can provide * @name: name of the handler, to be shown in /proc/bus/input/handlers * @id_table: pointer to a table of input_device_ids this driver can * handle * @h_list: list of input handles associated with the handler * @node: for placing the driver onto input_handler_list * * Input handlers attach to input devices and create input handles. There * are likely several handlers attached to any given input device at the * same time. All of them will get their copy of input event generated by * the device. * * The very same structure is used to implement input filters. Input core * allows filters to run first and will not pass event to regular handlers * if any of the filters indicate that the event should be filtered (by * returning %true from their filter() method). * * Note that input core serializes calls to connect() and disconnect() * methods. */ struct input_handler { void *private; void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); void (*events)(struct input_handle *handle, const struct input_value *vals, unsigned int count); bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value); bool (*match)(struct input_handler *handler, struct input_dev *dev); int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id); void (*disconnect)(struct input_handle *handle); void (*start)(struct input_handle *handle); bool legacy_minors; int minor; const char *name; const struct input_device_id *id_table; struct list_head h_list; struct list_head node; };
其中部分字段介绍如下:
- private:私有字段;
- event:handler用于向上层上报输入事件的函数;
- match:mactch函数用来匹配handler与input_dev设备;
- connect:当handler与input_dev匹配成功之后用来连接;
- disconnect:断开handler与input_dev之间的连接;
- fops:一个file_operation指针;
- minor:该handler的编号;
- name:handler的名字;
- id_table:指向一个 input_device_id 类型的数组,表示能支持哪些输入设备,用来进行与input设备匹配时用到的信息;
- h_list:用来挂接handler 上连接的所有handle 的一个链表头;
- node:作为一个链表节点挂接到 input_handler_list 链表上(input_handler_list 链表是一个由上层handler参维护的一个用来挂接所有注册的handler的链表头);
input_handle结构如下:
/** * struct input_handle - links input device with an input handler * @private: handler-specific data * @open: counter showing whether the handle is 'open', i.e. should deliver * events from its device * @name: name given to the handle by handler that created it * @dev: input device the handle is attached to * @handler: handler that works with the device through this handle * @d_node: used to put the handle on device's list of attached handles * @h_node: used to put the handle on handler's list of handles from which * it gets events */ struct input_handle { void *private; int open; const char *name; struct input_dev *dev; struct input_handler *handler; struct list_head d_node; struct list_head h_node; };
其中部分字段介绍如下:
- private:handle的私有数据;
- open:用来做open计数的;
- name:handle名字;
- dev:用来指向该handle绑定的input_dev结构体;
- hanlder:用来指向该handle 绑定的 handler 结构体;
- d_node:作为一个链表节点挂接到与它绑定的input_dev ->hlist 链表上;
- h_node:作为一个链表节点挂接到与它绑定的handler->hlist 链表上;
2.3 input_device_id
input_device_id定义在include/linux/mod_devicetable.h:
/* Input */ #define INPUT_DEVICE_ID_EV_MAX 0x1f #define INPUT_DEVICE_ID_KEY_MIN_INTERESTING 0x71 #define INPUT_DEVICE_ID_KEY_MAX 0x2ff #define INPUT_DEVICE_ID_REL_MAX 0x0f #define INPUT_DEVICE_ID_ABS_MAX 0x3f #define INPUT_DEVICE_ID_MSC_MAX 0x07 #define INPUT_DEVICE_ID_LED_MAX 0x0f #define INPUT_DEVICE_ID_SND_MAX 0x07 #define INPUT_DEVICE_ID_FF_MAX 0x7f #define INPUT_DEVICE_ID_SW_MAX 0x0f #define INPUT_DEVICE_ID_PROP_MAX 0x1f #define INPUT_DEVICE_ID_MATCH_BUS 1 #define INPUT_DEVICE_ID_MATCH_VENDOR 2 #define INPUT_DEVICE_ID_MATCH_PRODUCT 4 #define INPUT_DEVICE_ID_MATCH_VERSION 8 #define INPUT_DEVICE_ID_MATCH_EVBIT 0x0010 #define INPUT_DEVICE_ID_MATCH_KEYBIT 0x0020 #define INPUT_DEVICE_ID_MATCH_RELBIT 0x0040 #define INPUT_DEVICE_ID_MATCH_ABSBIT 0x0080 #define INPUT_DEVICE_ID_MATCH_MSCIT 0x0100 #define INPUT_DEVICE_ID_MATCH_LEDBIT 0x0200 #define INPUT_DEVICE_ID_MATCH_SNDBIT 0x0400 #define INPUT_DEVICE_ID_MATCH_FFBIT 0x0800 #define INPUT_DEVICE_ID_MATCH_SWBIT 0x1000 #define INPUT_DEVICE_ID_MATCH_PROPBIT 0x2000 struct input_device_id { kernel_ulong_t flags; __u16 bustype; __u16 vendor; __u16 product; __u16 version; kernel_ulong_t evbit[INPUT_DEVICE_ID_EV_MAX / BITS_PER_LONG + 1]; kernel_ulong_t keybit[INPUT_DEVICE_ID_KEY_MAX / BITS_PER_LONG + 1]; kernel_ulong_t relbit[INPUT_DEVICE_ID_REL_MAX / BITS_PER_LONG + 1]; kernel_ulong_t absbit[INPUT_DEVICE_ID_ABS_MAX / BITS_PER_LONG + 1]; kernel_ulong_t mscbit[INPUT_DEVICE_ID_MSC_MAX / BITS_PER_LONG + 1]; kernel_ulong_t ledbit[INPUT_DEVICE_ID_LED_MAX / BITS_PER_LONG + 1]; kernel_ulong_t sndbit[INPUT_DEVICE_ID_SND_MAX / BITS_PER_LONG + 1]; kernel_ulong_t ffbit[INPUT_DEVICE_ID_FF_MAX / BITS_PER_LONG + 1]; kernel_ulong_t swbit[INPUT_DEVICE_ID_SW_MAX / BITS_PER_LONG + 1]; kernel_ulong_t propbit[INPUT_DEVICE_ID_PROP_MAX / BITS_PER_LONG + 1]; kernel_ulong_t driver_info; };
其中flags参数:表示我们的这个 input_device_id 是用来匹配下面的4个情况的哪一项:
- 1:表示匹配总线;
- 2:表示匹配供应商;
- 4:表示匹配产品;
- 8:表示匹配版本;
三、输入子系统入口
input子系统的所有源码都放在drivers/input这个目录。
- input.c就是输入核心层(input core)的源码;
- 此外在该路径下,还可以看到gameport、joystick、keyboard、misc、mouse等,这些文件夹里面存放的就是属于这类的input子系统的设备驱动源码,可以理解为input子系统的驱动层;
- input目录下的evdev.c、joydev.c、mousedev.c等分别对应事件驱动层的各个不同的handler的源代码。
在linux内核中,input子系统也是以驱动模块形式存在于内核中,因此可以在内核配置进行动态加载和卸载,这样做的原由是,存在有些系统中不需要任何的输入类设备,这样就可以将input输入子系统这个模块去掉,使得内核尽量变得更小。
在编写驱动程序时有驱动入口和出口函数,同理input子系统的入口和出口函数,如下:
- module_init(input_init);
- module_exit(input_exit);
3.1 模块注册函数input_init
input_init函数用于input子系统的初始化,input_init函数实现在drivers/input/input.c:
static int __init input_init(void) { int err; err = class_register(&input_class); if (err) { pr_err("unable to register input_dev class\n"); return err; } err = input_proc_init(); if (err) goto fail1; err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), INPUT_MAX_CHAR_DEVICES, "input"); if (err) { pr_err("unable to register char major %d", INPUT_MAJOR); goto fail2; } return 0; fail2: input_proc_exit(); fail1: class_unregister(&input_class); return err; }
(1) 使用class_register注册类,class_register和我们之前使用的class_create类似,class_register(&input_class)是在/sys/class下注册一个名称为"input"的类:
struct class input_class = { .name = "input", .devnode = input_devnode, };
注册完成后可以在/sys/class/input可看到注册信息;
root@zhengyang:~# ls /sys/class/input/ event0 event1 event2 event3 event4 input0 input1 input3 input4 input5 mice mouse0 mouse1 mouse2
注意:这里我们仅仅是/sys/class/input类,并没有调用device_create/device_register在/syc/device/input下创建设备文件,只有注册了input子系统的设备驱动后,才会有驱动设备,此时我们并没有注册设备驱动的;
devnode用于返回device设备节点的相对路径,input_devnode函数定义如下:
static char *input_devnode(struct device *dev, umode_t *mode) { return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); }
(2) input_proc_init主要是创建了devices和handlers,创建完成后可在/proc/bus/input下查看已经注册devices和handlers;
使用register_chrdev_region来静态注册一组字符设备编号,MKDEV(INPUT_MAJOR, 0)为要分配的设备编号范围的初始值,主设备号为INPUT_MAJOR(13),次设备号为0,并且需要连续注册的次设备编号个数设置为INPUT_MAX_CHAR_DEVICES,当返回值小于0,表示注册失败;
3.2 init_proc_init
static int __init input_proc_init(void) { struct proc_dir_entry *entry; proc_bus_input_dir = proc_mkdir("bus/input", NULL); if (!proc_bus_input_dir) return -ENOMEM; entry = proc_create("devices", 0, proc_bus_input_dir, &input_devices_fileops); if (!entry) goto fail1; entry = proc_create("handlers", 0, proc_bus_input_dir, &input_handlers_fileops); if (!entry) goto fail2; return 0; fail2: remove_proc_entry("devices", proc_bus_input_dir); fail1: remove_proc_entry("bus/input", NULL); return -ENOMEM; }
当我们启动系统之后进入到proc文件系统中,可以看到在/proc/bus/input/目录下有两个文件devices和handlers,这两个文件就是在这里被创建的:
root@zhengyang:/work/sambashare/linux-5.2.8# ls /proc/bus/input/ devices handlers
我们cat devices和cat handlers时对应的操作方法就被封装在input_devices_fileops和input_handlers_fileops结构体中。
3.2.1 input_handlers_fileops变量
static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) { union input_seq_state *state = (union input_seq_state *)&seq->private; int error; /* We need to fit into seq->private pointer */ BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); error = mutex_lock_interruptible(&input_mutex); if (error) { state->mutex_acquired = false; return ERR_PTR(error); } state->mutex_acquired = true; state->pos = *pos; return seq_list_start(&input_handler_list, *pos); } static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) { union input_seq_state *state = (union input_seq_state *)&seq->private; state->pos = *pos + 1; return seq_list_next(v, &input_handler_list, pos); } static int input_handlers_seq_show(struct seq_file *seq, void *v) { struct input_handler *handler = container_of(v, struct input_handler, node); union input_seq_state *state = (union input_seq_state *)&seq->private; seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name); if (handler->filter) seq_puts(seq, " (filter)"); if (handler->legacy_minors) seq_printf(seq, " Minor=%d", handler->minor); seq_putc(seq, '\n'); return 0; } static const struct seq_operations input_handlers_seq_ops = { .start = input_handlers_seq_start, .next = input_handlers_seq_next, .stop = input_seq_stop, .show = input_handlers_seq_show, }; static int input_proc_handlers_open(struct inode *inode, struct file *file) { return seq_open(file, &input_handlers_seq_ops); }
当执行cat /proc/bus/input/handlers,将会执行input_handlers_fileops.open方法,也就是input_proc_handlers_open,最终调用input_handlers_seq_ops.show方法:
root@zhengyang:/work/sambashare/linux-5.2.8# cat /proc/bus/input/handlers N: Number=0 Name=rfkill N: Number=1 Name=kbd N: Number=2 Name=sysrq (filter) N: Number=3 Name=mousedev Minor=32 N: Number=4 Name=evdev Minor=64 N: Number=5 Name=leds N: Number=6 Name=joydev Minor=0
3.2.2 input_devices_fileops变量
static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) { union input_seq_state *state = (union input_seq_state *)&seq->private; int error; /* We need to fit into seq->private pointer */ BUILD_BUG_ON(sizeof(union input_seq_state) != sizeof(seq->private)); error = mutex_lock_interruptible(&input_mutex); if (error) { state->mutex_acquired = false; return ERR_PTR(error); } state->mutex_acquired = true; return seq_list_start(&input_dev_list, *pos); } static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) { return seq_list_next(v, &input_dev_list, pos); } static void input_seq_stop(struct seq_file *seq, void *v) { union input_seq_state *state = (union input_seq_state *)&seq->private; if (state->mutex_acquired) mutex_unlock(&input_mutex); } static int input_devices_seq_show(struct seq_file *seq, void *v) { struct input_dev *dev = container_of(v, struct input_dev, node); const char *path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); struct input_handle *handle; seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version); seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : ""); seq_printf(seq, "S: Sysfs=%s\n", path ? path : ""); seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : ""); seq_puts(seq, "H: Handlers="); list_for_each_entry(handle, &dev->h_list, d_node) seq_printf(seq, "%s ", handle->name); seq_putc(seq, '\n'); input_seq_print_bitmap(seq, "PROP", dev->propbit, INPUT_PROP_MAX); input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX); if (test_bit(EV_KEY, dev->evbit)) input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX); if (test_bit(EV_REL, dev->evbit)) input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX); if (test_bit(EV_ABS, dev->evbit)) input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX); if (test_bit(EV_MSC, dev->evbit)) input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX); if (test_bit(EV_LED, dev->evbit)) input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX); if (test_bit(EV_SND, dev->evbit)) input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX); if (test_bit(EV_FF, dev->evbit)) input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX); if (test_bit(EV_SW, dev->evbit)) input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX); seq_putc(seq, '\n'); kfree(path); return 0; } static const struct seq_operations input_devices_seq_ops = { .start = input_devices_seq_start, .next = input_devices_seq_next, .stop = input_seq_stop, .show = input_devices_seq_show, }; static int input_proc_devices_open(struct inode *inode, struct file *file) { return seq_open(file, &input_devices_seq_ops); } static const struct file_operations input_devices_fileops = { .owner = THIS_MODULE, .open = input_proc_devices_open, .poll = input_proc_devices_poll, .read = seq_read, .llseek = seq_lseek, .release = seq_release, };
当执行cat /proc/bus/input/devices,将会执行input_devices_fileops.open方法,也就是input_proc_devices_open,最终调用input_devices_seq_ops.show方法:
root@zhengyang:/work/sambashare/linux-5.2.8# cat /proc/bus/input/devices I: Bus=0019 Vendor=0000 Product=0001 Version=0000 N: Name="Power Button" P: Phys=LNXPWRBN/button/input0 S: Sysfs=/devices/LNXSYSTM:00/LNXPWRBN:00/input/input0 U: Uniq= H: Handlers=kbd event0 B: PROP=0 B: EV=3 B: KEY=10000000000000 0 I: Bus=0011 Vendor=0001 Product=0001 Version=ab41 N: Name="AT Translated Set 2 keyboard" P: Phys=isa0060/serio0/input0 S: Sysfs=/devices/platform/i8042/serio0/input/input1 U: Uniq= H: Handlers=sysrq kbd event1 leds B: PROP=0 B: EV=120013 B: KEY=402000000 3803078f800d001 feffffdfffefffff fffffffffffffffe B: MSC=10 B: LED=7 I: Bus=0003 Vendor=0e0f Product=0003 Version=0110 N: Name="VMware VMware Virtual USB Mouse" P: Phys=usb-0000:02:00.0-1/input0 S: Sysfs=/devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0/0003:0E0F:0003.0001/input/input3 U: Uniq= H: Handlers=mouse0 event2 B: PROP=0 B: EV=17 B: KEY=ff0000 0 0 0 0 B: REL=103 B: MSC=10 I: Bus=0011 Vendor=0002 Product=0013 Version=0006 N: Name="VirtualPS/2 VMware VMMouse" P: Phys=isa0060/serio1/input1 S: Sysfs=/devices/platform/i8042/serio1/input/input5 U: Uniq= H: Handlers=mouse1 event3 B: PROP=0 B: EV=b B: KEY=70000 0 0 0 0 B: ABS=3 I: Bus=0011 Vendor=0002 Product=0013 Version=0006 N: Name="VirtualPS/2 VMware VMMouse" P: Phys=isa0060/serio1/input0 S: Sysfs=/devices/platform/i8042/serio1/input/input4 U: Uniq= H: Handlers=mouse2 event4 B: PROP=1 B: EV=7 B: KEY=30000 0 0 0 0 B: REL=103
四、输入核心层提供给设备驱动层的接口函数
input子系统输入核心层为设备驱动层提供了一些重要的接口函数:
- input_allocate_device/input_free_device:分配/释放input_dev结构体类型大小的内存;
- input_set_capability:设置输入设备可以上报哪些输入事件;
- input_register_device:向输入核心层注册输入设备;
4.1 分配/释放输入设备
4.1.1 input_allocate_device
input_allocate_device用于为输入设备分配内存,函数返回一个struct input_dev实例;
/** * input_allocate_device - allocate memory for new input device * * Returns prepared struct input_dev or %NULL. * * NOTE: Use input_free_device() to free devices that have not been * registered; input_unregister_device() should be used for already * registered devices. */ struct input_dev *input_allocate_device(void) { static atomic_t input_no = ATOMIC_INIT(-1); struct input_dev *dev; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev) { dev->dev.type = &input_dev_type; // 设备类型 dev->dev.class = &input_class; // 保存设备class device_initialize(&dev->dev); // 初始化设备 mutex_init(&dev->mutex); spin_lock_init(&dev->event_lock); timer_setup(&dev->timer, NULL, 0); INIT_LIST_HEAD(&dev->h_list); INIT_LIST_HEAD(&dev->node); dev_set_name(&dev->dev, "input%lu", (unsigned long)atomic_inc_return(&input_no)); // 设置输入设备名称 __module_get(THIS_MODULE); } return dev; }
函数执行流程如下:
- 定义一个input_dev指针,并动态申请内存;
- 确定input设备的设备类型;
- 确定input设备所属的设备类;
- 调用device_initialize初始化设备,这个函数是device_register函数的前半部分的实现,主要用于设备的初始化;而device_add是在input_register_device函数中调用,该函数执行完会在/sys/class/input创建input%lu文件,同时创建设备节点/dev/input/input%lu(前提是设置了设备号devt,否则不会创建设备节点);
- 初始化互斥锁、自旋锁;
- 初始化链表h_list、node;
这里我们简单简单介绍一下device_create、device_register、device_add的区别;device_register函数调用了device_add,如下所示:
int device_register(struct device *dev) { device_initialize(dev);//初始化dev return device_add(dev); //添加dev }
device_create函数又调用了device_resiger,只不过device_create在调用device_register之前构造了一个device变量dev,并将该dev的devt,class,parent,device_create_release等变量赋值了。
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
4.1.2 input_free_device
input_free_device用于释放动态分配的struct input_dev实例;
/** * input_free_device - free memory occupied by input_dev structure * @dev: input device to free * * This function should only be used if input_register_device() * was not called yet or if it failed. Once device was registered * use input_unregister_device() and memory will be freed once last * reference to the device is dropped. * * Device should be allocated by input_allocate_device(). * * NOTE: If there are references to the input device then memory * will not be freed until last reference is dropped. */ void input_free_device(struct input_dev *dev) { if (dev) { if (dev->devres_managed) WARN_ON(devres_destroy(dev->dev.parent, devm_input_device_release, devm_input_device_match, dev)); input_put_device(dev); } }
4.2 input_set_capability
input_set_capability用于设置输入设备可以上报哪些输入事件;
/** * input_set_capability - mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event * @type: type of the event (EV_KEY, EV_REL, etc...) * @code: event code * * In addition to setting up corresponding bit in appropriate capability * bitmap the function also adjusts dev->evbit. */ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code) { switch (type) { case EV_KEY: __set_bit(code, dev->keybit); break; case EV_REL: __set_bit(code, dev->relbit); break; case EV_ABS: input_alloc_absinfo(dev); if (!dev->absinfo) return; __set_bit(code, dev->absbit); break; case EV_MSC: __set_bit(code, dev->mscbit); break; case EV_SW: __set_bit(code, dev->swbit); break; case EV_LED: __set_bit(code, dev->ledbit); break; case EV_SND: __set_bit(code, dev->sndbit); break; case EV_FF: __set_bit(code, dev->ffbit); break; case EV_PWR: /* do nothing */ break; default: pr_err("%s: unknown type %u (code %u)\n", __func__, type, code); dump_stack(); return; } __set_bit(type, dev->evbit); }
参数:
- dev:设备的input_dev结构体变量;
- type:表示设备可以上报的事件类型;
- code:事件编码,一个类型的事件下面根据code又进行了更加具体的划分,表示上报该类型哪个事件;
注意:input_set_capability函数一次只能设置一个具体事件,如果设备可以上报多个事件,则需要重复调用这个函数来进行设置。
具体事件可以在include/uapi/linux/input-event-codes.h中定义:
/* * Event types */ #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 // 力反馈状态 #define EV_MAX 0x1f // 事件类型最大个数和提供位掩码支持 #define EV_CNT (EV_MAX+1)
4.3 input_register_device
input_register_device用于向输入核心层注册输入设备;
/** * input_register_device - register device with input core * @dev: device to be registered * * This function registers device with input core. The device must be * allocated with input_allocate_device() and all it's capabilities * set up before registering. * If function fails the device must be freed with input_free_device(). * Once device has been successfully registered it can be unregistered * with input_unregister_device(); input_free_device() should not be * called in this case. * * Note that this function is also used to register managed input devices * (ones allocated with devm_input_allocate_device()). Such managed input * devices need not be explicitly unregistered or freed, their tear down * is controlled by the devres infrastructure. It is also worth noting * that tear down of managed input devices is internally a 2-step process: * registered managed input device is first unregistered, but stays in * memory and can still handle input_event() calls (although events will * not be delivered anywhere). The freeing of managed input device will * happen later, when devres stack is unwound to the point where device * allocation was made. */ int input_register_device(struct input_dev *dev) { struct input_devres *devres = NULL; struct input_handler *handler; // 定义结构体指针 unsigned int packet_size; const char *path; int error; if (test_bit(EV_ABS, dev->evbit) && !dev->absinfo) { dev_err(&dev->dev, "Absolute device without dev->absinfo, refusing to register\n"); return -EINVAL; } if (dev->devres_managed) { devres = devres_alloc(devm_input_device_unregister, sizeof(*devres), GFP_KERNEL); if (!devres) return -ENOMEM; devres->input = dev; } /* Every input device generates EV_SYN/SYN_REPORT events. */ __set_bit(EV_SYN, dev->evbit); // 每一个输入设备都会发生这个事件 /* KEY_RESERVED is not supposed to be transmitted to userspace. */ __clear_bit(KEY_RESERVED, dev->keybit); // 清除EV_KEY事件类型下KEY_RESERVED事件码对应的bit位,也就是不传输这种事件 /* Make sure that bitmasks not mentioned in dev->evbit are clean. */ input_cleanse_bitmasks(dev); // 确保input_dev中的用来记录事件的变量中没有提到的位掩码是干净的 packet_size = input_estimate_events_per_packet(dev); if (dev->hint_events_per_packet < packet_size) dev->hint_events_per_packet = packet_size; dev->max_vals = dev->hint_events_per_packet + 2; dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL); if (!dev->vals) { error = -ENOMEM; goto err_devres_free; } /* * If delay and period are pre-set by the driver, then autorepeating * is handled by the driver itself and we don't do it in input.c. */ if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) input_enable_softrepeat(dev, 250, 33); if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; error = device_add(&dev->dev); // 对设备对象部分字段初始化,然后将设备加到linux内核系统的设备驱动模型中,会在/sys/class/input类文件下创建链接文件input%d
// 但是由于未设置设备号devt(默认就是0),不会在文件系统创建设备节点/dev/input/input%ld。 if (error) goto err_free_vals; path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); // 获取input设备对象所在的路径 /sys/devices/virtual/input/input%d pr_info("%s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); error = mutex_lock_interruptible(&input_mutex); if (error) goto err_device_del; list_add_tail(&dev->node, &input_dev_list); // 将dev_node作为节点链接到input_dev_list list_for_each_entry(handler, &input_handler_list, node) // 遍历input_handler_list链表上的所有handler input_attach_handler(dev, handler); // 将handler于input设备进行匹配 input_wakeup_procfs_readers(); // 更新proc文件系统 mutex_unlock(&input_mutex); if (dev->devres_managed) { dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n", __func__, dev_name(&dev->dev)); devres_add(dev->dev.parent, devres); } return 0; err_device_del: device_del(&dev->dev); err_free_vals: kfree(dev->vals); dev->vals = NULL; err_devres_free: devres_free(devres); return error; }
其中input_dev_list定义如下:
static LIST_HEAD(input_dev_list);
一个input_dev可以与多个handler匹配成功,匹配成功就会在连接过程中创建设备文件,而不同的handler创建的设备文件的次设备号是不一样的,所以就是导致一个设备对应多个次设备号。
4.3.1 input_attach_handler
input_attach_handler就是input_register_device函数中用来对下层的设备驱动和上层的handler进行匹配的一个函数,不管注册input_dev,还是注册input_handler,都会进入input_attach_handler判断两者id是否匹配。
只有匹配成功之后就会调用上层handler中的connect函数进行连接绑定。
static int input_attach_handler(struct input_dev *dev, struct input_handler *handler) { const struct input_device_id *id; int error; id = input_match_device(handler, dev); // 进行handler与input设备匹配 if (!id) return -ENODEV; error = handler->connect(handler, dev, id); // 匹配成功调用handler中的connect函数进行连接 if (error && error != -ENODEV) pr_err("failed to attach handler %s to device %s, error: %d\n", handler->name, kobject_name(&dev->dev.kobj), error); return error; }
4.3.2 input_match_device
input_match_device用于handler与input设备匹配工作,这里匹配是通过遍历handler->id_table数组,判断是否和dev->id匹配实现的。
static const struct input_device_id *input_match_device(struct input_handler *handler, struct input_dev *dev) { const struct input_device_id *id; for (id = handler->id_table; id->flags || id->driver_info; id++) { // 依次遍历handler->id_table所指向的input_device_id数组中的各个元素 if (input_match_device_id(dev, id) && (!handler->match || handler->match(handler, dev))) { // 如果数组中某个元素匹配成功了就返回它的地址 return id; } } return NULL; }
函数内部又调用了input_match_device_id:
bool input_match_device_id(const struct input_dev *dev, const struct input_device_id *id) { if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) // 匹配总线 if (id->bustype != dev->id.bustype) return false; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) // 匹配供应商 if (id->vendor != dev->id.vendor) return false; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) // 匹配产品 if (id->product != dev->id.product) return false; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) // 匹配版本 if (id->version != dev->id.version) return false; if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX) || !bitmap_subset(id->keybit, dev->keybit, KEY_MAX) || !bitmap_subset(id->relbit, dev->relbit, REL_MAX) || !bitmap_subset(id->absbit, dev->absbit, ABS_MAX) || !bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX) || !bitmap_subset(id->ledbit, dev->ledbit, LED_MAX) || !bitmap_subset(id->sndbit, dev->sndbit, SND_MAX) || !bitmap_subset(id->ffbit, dev->ffbit, FF_MAX) || !bitmap_subset(id->swbit, dev->swbit, SW_MAX) || !bitmap_subset(id->propbit, dev->propbit, INPUT_PROP_MAX)) { return false; } return true; }
五、输入核心层提供给事件驱动层的接口函数
input子系统输入核心层为事件驱动层提供了一些重要的接口函数:
- input_register_handler/input_unregister_handler:事件驱动层向输入核心层注册/卸载handler;
- input_register_handle/input_unregister_handle:事件驱动层向输入核心层注册/卸载handle;
5.1 注册/卸载handler
5.1.1 input_register_handler
input_register_handler用于向输入核心层注册input handler;
/** * input_register_handler - register a new input handler * @handler: handler to be registered * * This function registers a new input handler (interface) for input * devices in the system and attaches it to all input devices that * are compatible with the handler. */ int input_register_handler(struct input_handler *handler) { struct input_dev *dev; int error; error = mutex_lock_interruptible(&input_mutex); if (error) return error; INIT_LIST_HEAD(&handler->h_list); // 初始化链表 list_add_tail(&handler->node, &input_handler_list); // 将handler通过handler->node节点连接到input_handler_list链表上 list_for_each_entry(dev, &input_dev_list, node) // 遍历input_dev_list链表下挂接的所有input_dev设备 input_attach_handler(dev, handler); // 进行匹配 input_wakeup_procfs_readers(); // 更新proc文件系统 mutex_unlock(&input_mutex); return 0; }
通过分析了上面的input_register_device和这里的input_register_handler函数可以知道:
- 注册设备的时候,不一定是先注册了handler才能够注册设备。当注册设备时,会先将设备挂接到设备管理链表(input_dev_list)上,然后再去遍历input_handler_list链表匹配handler;
- 同样对于handler注册的时候,也会先将handler挂接到handler管理链表(input_handler_list)上,然后再去遍历input_dev_list链表匹配设备;
5.1.2 input_unregister_handler
input_unbregister_handler用于卸载input handler;
/** * input_unregister_handler - unregisters an input handler * @handler: handler to be unregistered * * This function disconnects a handler from its input devices and * removes it from lists of known handlers. */ void input_unregister_handler(struct input_handler *handler) { struct input_handle *handle, *next; mutex_lock(&input_mutex); list_for_each_entry_safe(handle, next, &handler->h_list, h_node) handler->disconnect(handle); WARN_ON(!list_empty(&handler->h_list)); list_del_init(&handler->node); input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); }
5.2 注册/卸载handle
5.2.1 input_register_handle
input_register_handle用于向输入核心层注册input handle;
/** * input_register_handle - register a new input handle * @handle: handle to register * * This function puts a new input handle onto device's * and handler's lists so that events can flow through * it once it is opened using input_open_device(). * * This function is supposed to be called from handler's * connect() method. */ int input_register_handle(struct input_handle *handle) { struct input_handler *handler = handle->handler; struct input_dev *dev = handle->dev; int error; /* * We take dev->mutex here to prevent race with * input_release_device(). */ error = mutex_lock_interruptible(&dev->mutex); if (error) return error; /* * Filters go to the head of the list, normal handlers * to the tail. */ if (handler->filter) list_add_rcu(&handle->d_node, &dev->h_list); else list_add_tail_rcu(&handle->d_node, &dev->h_list); mutex_unlock(&dev->mutex); /* * Since we are supposed to be called from ->connect() * which is mutually exclusive with ->disconnect() * we can't be racing with input_unregister_handle() * and so separate lock is not needed here. */ list_add_tail_rcu(&handle->h_node, &handler->h_list); if (handler->start) handler->start(handle); return 0; }
这个函数的作用就是注册一个handle,也就是将各个handle的d_node和h_node连接起来构成一个环形的结构,在调用这个函数之前已经将handle中的dev和handler已经是填充好了的。
5.2.2 input_unregister_handle
input_unbregister_handle用于卸载input handle;
/** * input_unregister_handle - unregister an input handle * @handle: handle to unregister * * This function removes input handle from device's * and handler's lists. * * This function is supposed to be called from handler's * disconnect() method. */ void input_unregister_handle(struct input_handle *handle) { struct input_dev *dev = handle->dev; list_del_rcu(&handle->h_node); /* * Take dev->mutex to prevent race with input_release_device(). */ mutex_lock(&dev->mutex); list_del_rcu(&handle->d_node); mutex_unlock(&dev->mutex); synchronize_rcu(); }
5.3 handler、input_dev、handle关系结构图
下面用一副关系图来描述handler、input_dev、handle之间的一个关系(注意:为了减少连线,在绘制双向链表的时候,这里A->B实际表示的是A->next=B,A<-B实际表示的是B->prev=A):
从本质上讲,input_dev与handler是多对多的关系,从上图可以看出来:
- 一个input_dev可以对应多个handler;
- 一个handler也可以对应多个input_dev。
在通过input_match_device进行handler与inputs设备匹配的时候:一个input_dev会与所有的handler都进行匹配的。
从图中可以看出来,一个handle就是用来记录系统中一对匹配成功的handler和device,我们可以从这个handle出发得到handler的信息,还可以得到device的信息。所以正因为有这样的功能,所以可以由handler经过handle最终获取到device的信息,同理也可以从device从发经过handle最终获取到handler的信息。
六、事件驱动层源码分析
input子系统的的事件驱动层由各个handler组成,各个handler之间是平行关系,他们都是属于上层。
在drivers/input目录下的evdev.c、joydev.c、mousedev.c等分别对应事件驱动层的各个不同的handler的源代码:
root@zhengyang:/work/sambashare/linux-5.2.8# ls drivers/input/ apm-power.c ff-core.c input.c input-leds.o joydev.c matrix-keymap.c serio built-in.a ff-core.o input-compat.c input-mt.c joystick misc sparse-keymap.c evbug.c ff-memless.c input-compat.h input-mt.o Kconfig mouse tablet evdev.c ff-memless.o input-compat.o input.o keyboard mousedev.c touchscreen evdev.o gameport input-leds.c input-polldev.c Makefile rmi4
它们同input子系统一样,也是以驱动模块的形式存在于内核中。目前用的到最多的是event,这里就以这个handler为例进行源码分析。
event相关的源码位于drivers/input/evdev.c文件中。
6.1 模块注册函数
从evdev.c文件的最后可以看到module_init、module_exit这些宏,这说明各个事件驱动层也是以驱动模块形式存在于内核中。
module_init(evdev_init);
module_exit(evdev_exit);
6.1.1 模块注册函数evdev_init
static int __init evdev_init(void) { return input_register_handler(&evdev_handler); }
event_init函数直接调用input_register_handler来注册evdev_handler这个handler,evdev_handler这个struct handler类型的变量就是对这个handler的一个描述。
6.1.2 模块卸载函数evdev_exit
static void __exit evdev_exit(void) { input_unregister_handler(&evdev_handler); }
6.2 evdev_handler
evdev_handler定义如下:
static struct input_handler evdev_handler = { .event = evdev_event, .events = evdev_events, .connect = evdev_connect, .disconnect = evdev_disconnect, .legacy_minors = true, .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, };
这里设置次设备版本号从EVDEV_MINOR_BASE开始,EVDEV_MINOR_BASE默认值为64。
6.2.1 evdev_ids
id_table被设置为evdev_ids:
static const struct input_device_id evdev_ids[] = { { .driver_info = 1 }, /* Matches all devices */ { }, /* Terminating zero entry */ };
这里我们可以看到evdev_handler和所有input设备都匹配,所以通过input_register_device注册input设备后,均会通过evdev_connect建立input设备和evdev_handler之间的关系。
6.2.2 evdev_connect
evdev_connect被用来连接evdev_hanlder和input设备(这些设备都是事件类设备),在input_attach_handler中会被调用,而input_register_handler会调用input_attach_handler。
/* * Create new evdev device. Note that input core serializes calls * to connect and disconnect. */ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; // 定义evdev指针 int minor; int dev_no; int error; minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); // 获取次设备号 由于EVDEV_MINOR_BASE=64,而EVDEV设备最多32个,所以次设备号[64,96) if (minor < 0) { error = minor; pr_err("failed to reserve new minor: %d\n", error); return error; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); // 动态位evdev分配内存 if (!evdev) { error = -ENOMEM; goto err_free_minor; } INIT_LIST_HEAD(&evdev->client_list); // 初始化列表 spin_lock_init(&evdev->client_lock); // 初始化自旋锁 mutex_init(&evdev->mutex); // 初始化互斥锁 init_waitqueue_head(&evdev->wait); evdev->exist = true; dev_no = minor; /* Normalize device number if it falls into legacy range */ if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS) // 64 + 32 dev_no -= EVDEV_MINOR_BASE; dev_set_name(&evdev->dev, "event%d", dev_no); // 设置设备名称 evdev->handle.dev = input_get_device(dev); // 将dev指针赋值给evdev->handle.dev evdev->handle.name = dev_name(&evdev->dev); // 设置evdev->dev对象的名字,并把名字赋值给evdev->handle.name evdev->handle.handler = handler; evdev->handle.private = evdev; // 将evdev作为handle的私有数据 evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); // 初始化设备编号 主设备号为13 evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; // 设置父设备 evdev->dev.release = evdev_free; device_initialize(&evdev->dev); // 初始化设备 error = input_register_handle(&evdev->handle); // 注册handle if (error) goto err_free_evdev; cdev_init(&evdev->cdev, &evdev_fops); // 初始化evdev->cdev 字符设备 error = cdev_device_add(&evdev->cdev, &evdev->dev); // 向系统注册注册字符设备evdev->cdev,并将evdev->dev设备添加到linux内核系统的设备驱动模型 相当于执行了cdev_add(&evdev->cdev,evdev->dev->devt,1)、device_add(&evdev->dev) 该函数位于fs/char_dev.c if (error) goto err_cleanup_evdev; return 0; err_cleanup_evdev: evdev_cleanup(evdev); input_unregister_handle(&evdev->handle); err_free_evdev: put_device(&evdev->dev); err_free_minor: input_free_minor(minor); return error; }
该连接函数主要做了以下工作:
- 动态分配分配evdev结构体,这个结构表示事件类的输入设备,具体设备信息保存在cdev和dev成员中;
- 动态分配input_handle(即evdev->hanlde),并初始化该结构体成员,最后调用input_register_handle注册该handle;
- 初始化evdev->dev结构体成员;
- 通过cdev_init初始化字符设备evdev->cdev,并设置文件操作类对象evdev_fops;
- 在input_init中已经通过register_chrdev_region静态注册了一组字符设备编号,所以这里直接通过cdev_device_add函数向系统注册注册字符设备evdev->cdev,并将evdev->dev设备添加到linux内核系统的设备驱动模型。字符设备主设备编号为INPUT_MAJOR(13)、次设备编号为minor的值;
执行完会为handler和dev建立如下关系(其中下图的handle、evdev、dev、cdev都是在该函数中生成的):
我们可以运行如下命令查看input类下的文件:
root@zhengyang:/work/sambashare/linux-5.2.8# ll /sys/class/input/event* lrwxrwxrwx 1 root root 0 4月 9 13:09 /sys/class/input/event0 -> ../../devices/LNXSYSTM:00/LNXPWRBN:00/input/input0/event0/ lrwxrwxrwx 1 root root 0 4月 9 13:09 /sys/class/input/event1 -> ../../devices/platform/i8042/serio0/input/input1/event1/ lrwxrwxrwx 1 root root 0 4月 9 13:09 /sys/class/input/event2 -> ../../devices/pci0000:00/0000:00:11.0/0000:02:00.0/usb2/2-1/2-1:1.0/0003:0E0F:0003.0001/input/input3/event2/ lrwxrwxrwx 1 root root 0 4月 9 13:09 /sys/class/input/event3 -> ../../devices/platform/i8042/serio1/input/input5/event3/ lrwxrwxrwx 1 root root 0 4月 9 13:09 /sys/class/input/event4 -> ../../devices/platform/i8042/serio1/input/input4/event4/
同时可以运行如下命令查看事件类输入设备节点:
root@zhengyang:/work/sambashare/linux-5.2.8# ll /dev/input/event* crw-rw---- 1 root input 13, 64 4月 6 21:11 /dev/input/event0 crw-rw---- 1 root input 13, 65 4月 6 21:11 /dev/input/event1 crw-rw---- 1 root input 13, 66 4月 6 21:11 /dev/input/event2 crw-rw---- 1 root input 13, 67 4月 6 21:11 /dev/input/event3 crw-rw---- 1 root input 13, 68 4月 6 21:11 /dev/input/event4
可以看到这些事件类输入设备都是字符设备,并且设备的名字为event%d,并且主设备号为13,次设备号从64开始,到68,和我们之前分析的一致。
6.2.3 evdev_fops
在evdev_connect函数中调用cdev_init(&evdev->cdev, &evdev_fops)初始化事件类字符设备,并将字符设备的ops初始化为evdev_fops:
static const struct file_operations evdev_fops = { .owner = THIS_MODULE, .read = evdev_read, .write = evdev_write, .poll = evdev_poll, .open = evdev_open, .release = evdev_release, .unlocked_ioctl = evdev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = evdev_ioctl_compat, #endif .fasync = evdev_fasync, .flush = evdev_flush, .llseek = no_llseek, };
6.2.4 evdev_open
当我们打开我们注册的这个事件类输入设备,比如/dev/input/event1,就会执行设备操作evdev_fops中的open函数:
static int evdev_open(struct inode *inode, struct file *file) { struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); // 获取到事件类设备对象 unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); struct evdev_client *client; // 定义client结构体指针 int error; client = kzalloc(struct_size(client, buffer, bufsize), // 动态申请内存 GFP_KERNEL | __GFP_NOWARN); if (!client) client = vzalloc(struct_size(client, buffer, bufsize)); if (!client) return -ENOMEM; client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); client->evdev = evdev; evdev_attach_client(evdev, client); // 其实这个函数就是做了一个链表挂接:client->node挂接到evdev->client_list error = evdev_open_device(evdev); // 打开evdev设备 最终就会打开input_dev->open函数 if (error) goto err_free_client; file->private_data = client; // 将evdev_client作为file的私有数据存在 stream_open(inode, file); return 0; err_free_client: evdev_detach_client(evdev, client); kvfree(client); return error; }
我们这里重点看一下evdev_open_device函数是如何调用input_dev->open函数的:
static int evdev_open_device(struct evdev *evdev) { int retval; retval = mutex_lock_interruptible(&evdev->mutex); // 获取互斥锁 if (retval) return retval; if (!evdev->exist) retval = -ENODEV; else if (!evdev->open++) { retval = input_open_device(&evdev->handle); // 这里是通过evdev->handle获取到input_dev对象,然后执行input_dev对象的open函数 if (retval) evdev->open--; } mutex_unlock(&evdev->mutex); // 释放互斥锁 return retval; }
6.2.5 evdev_read
当我们读取这个事件类输入设备,比如/dev/input/event1,就会执行设备操作evdev_fops中的read函数:
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; struct input_event event; // 输入事件对象 size_t read = 0; int error; if (count != 0 && count < input_event_size()) // 判断应用程序要读取的数据是否正确 return -EINVAL; for (;;) { if (!evdev->exist || client->revoked) return -ENODEV; if (client->packet_head == client->tail && // 在非堵塞模式下。如果没有数据,则返回-EAGAIN (file->f_flags & O_NONBLOCK)) return -EAGAIN; /* * count == 0 is special - no IO is done but we check * for error conditions (see above). */ if (count == 0) break; while (read + input_event_size() <= count && // 已读取长度 + 剩下数据长度 <= 要读取的,继续读取 evdev_fetch_next_event(client, &event)) { if (input_event_to_user(buffer + read, &event)) // 将读取数据写回用户空间 return -EFAULT; read += input_event_size(); // 已读取长度 } if (read) break; if (!(file->f_flags & O_NONBLOCK)) { // 堵塞模式 error = wait_event_interruptible(evdev->wait, // 没有数据进入睡眠状态,可以通过evdev_event唤醒 client->packet_head != client->tail || !evdev->exist || client->revoked); if (error) return error; } } return read; // 返回读取长度 }
6.2.6 evdev_event
如果调用read函数,当前进程/线程进入睡眠状态,可以通过evdev_event唤醒,其中evdev_event是evdev.c(事件驱动) 的evdev_handler.event成员
static struct input_handler evdev_handler = { .event = evdev_event, .events = evdev_events, .connect = evdev_connect, .disconnect = evdev_disconnect, .legacy_minors = true, .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, };
当有事件发生了,比如对于按键驱动,当有按键按下时,就会执行evdev_event函数,就会进入input_dev对象的event函数中处理事件。
七、总结
7.1 回顾驱动程序开发步骤
这里我们回归一下我们在linux驱动基础概念以及驱动程序框架搭建介绍驱动程序的编写流程:以字符设备为例:
-
注册一组字符设备编号,实现函数register_chrdev_region/alloc_chrdev_region;
-
初始化字符设备,填充file_operations,实现函数cdev_init;
-
向内核注册字符设备,实现函数cdev_add;
- 设计所要实现的操作,比如 open、close、read、write 等函数;
7.2 回顾输入子系统
通过input子系统模块初始化函数input_init:在/sys/class下注册input类,并通过register_chrdev_region注册一组字符设备编号,主设备编号为13;
通过input_register_device注册input设备,函数参数需要一个struct input_dev对象;
通过input_register_handler注册handler,这一步已经在内核的evdev.c、keyboard.c等模块中实现了;
- 以事件类设备为例,通过evdev模块初始化函数evdev_init中调用input_register_handler(&evdev_handler)注册evdev_handler;
- 由于内核中实现了实现了若干个handler,因此需要通过input_match_device获取与事件类设备匹配的evdev_handler,当然这个可能是1对多的关系;
- 匹配成功后通过evdev_handler.connect建立事件类设备和evdev_handler之间的关系,同时完成了事件类字符设备的初始化,以及向内核注册的过程,执行完后会在/sys/class/input下生成文件event%d,并在/dev/input下生成事件类设备节点event%d;
7.3 注册input子系统设备驱动
到此为止,input输入子系统中我们已经介绍了输入核心层以及事件驱动层。
还有设备驱动层没有说到,如果我们想要向input子系统注册设备驱动,那我们应该怎么做,以键盘设备为例:
- 我们首先通过input_allocate_device创建struct input_dev结构对象dev;
- 通过input_set_capability设置input设备可以上报哪些输入事件;
- 初始化dev成员,设计所要实现的操作,比如 open、close、event、flush函数,需要注意的是这里没有read、write函数,而是event函数,这是因为在input子系统中,设备驱动层是通过将输入转换为转换为统一事件形式,向输入核心层汇报;
- 然后调用input_register_device注册这个设备,并且会在/sys/class/input下生成文件input%d;
- 在input_register_device函数最后会遍历input_handler_list链表上的所有handler,通过input_match_device获取与设备匹配的handler,当然这个可能是1对多的关系;
- 匹配成功后通过handler.connect建立事件类设备和handler之间的关系,同时完成了事件类字符设备的初始化,以及向内核注册的过程,执行完后会在/sys/class/input下生成文件event%d或mouse%d、js%,并在/dev/input下生成设备节点event%d或mouse%d、js%;
至于其他的工作,input子系统已经帮我们做了,因此我们就不用了去做了。
当我们去读写设备节点时,比如/dev/input/event0,实际上最终就会执行到我们注册的设备dev中实现的操作函数。
参考文章
[4] INPUT输入子系统