【输入子系统03】输入子系统核心
发布日期:2021-06-29 14:51:13 浏览次数:3 分类:技术文章

本文共 9894 字,大约阅读时间需要 32 分钟。

【输入子系统03】输入子系统核心

input子系统,作为管理输入设备与系统进行交互的中枢,任何的输入设备驱动都要通过input向内核注册其设备,

常用的输入设备也就是鼠标,键盘,触摸屏。

稍微细分一点整个输入体系,就是 硬件驱动层,input核心中转层,事件处理层.层次之间传递都以event事件的形式,这其中input连接上下层,分别提供接口.

之前有分析usbtouchscreen的驱动,也就是硬件驱动部分,这里简单记录一下input核心中转处理 input.c .

一、input_init

源码位于/kernel/drivers/input/input.c ,模块初始调用口subsys_initcall(input_init)

由kernel启动的时候由kernel_init——>do_basic_setup();——>do_initcalls调用到
这里首先调用到初始函数:

static int __init input_init(void){
err = class_register(&input_class); //注册input class,可在/sys/class下看到对应节点文件 err = input_proc_init(); //proc fs的下的一些初始操作,函数原型在input.c,可查看/proc/bus/input err = register_chrdev(INPUT_MAJOR, "input", &input_fops); // 注册input字符设备,主节点为INPUT_MAJOR==13,可以去input_fops里看注册函数,注册到/dev/input return 0;}

这就是最开始的初始化过程了.

可以看下注册方法函数:

static const struct file_operations input_fops = {
.owner = THIS_MODULE, .open = input_open_file, .llseek = noop_llseek,};

input.c中还有很多其它的接口以及全局数据,后面陆续联通,先从设备驱动最先调用到的注册 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. */ int input_register_device(struct input_dev *dev){
static atomic_t input_no = ATOMIC_INIT(0); //这个原子变量,代表总共注册的input设备,每注册一个加1,因为是静态变量,所以每次调用都不会清零的 struct input_handler *handler; const char *path; __set_bit(EV_SYN, dev->evbit); //EN_SYN 这个是设备都要支持的事件类型,所以要设置 /* 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. */ // 这个内核定时器是为了重复按键而设置的 init_timer(&dev->timer); if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; dev->rep[REP_DELAY] = 250; dev->rep[REP_PERIOD] = 33; //如果没有定义有关重复按键的相关值,就用内核默认的 } if (!dev->getkeycode) dev->getkeycode = input_default_getkeycode; if (!dev->setkeycode) dev->setkeycode = input_default_setkeycode; //以上设置的默认函数由input核心提供 dev_set_name(&dev->dev, "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); //设置input_dev中device的名字,这个名字会在/class/input中出现 error = device_add(&dev->dev); //将device加入到linux设备模型中去 path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); printk(KERN_INFO "input: %s as %s\n", dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); kfree(path); //这个得到路径名称,并打印出来 error = mutex_lock_interruptible(&input_mutex); list_add_tail(&dev->node, &input_dev_list); // 将新分配的input设备连接到input_dev_list链表上 list_for_each_entry(handler, &input_handler_list, node) input_attach_handler(dev, handler); //遍历input_handler_list链表,配对 input_dev 和 input_handler //input_attach_handler 这个函数是配对的关键,下面将详细分析 input_wakeup_procfs_readers(); // 和proc文件系统有关,暂时不考虑 mutex_unlock(&input_mutex); return 0;}

可以看到前面都是一些初始设置,加入到input.c 的全局input_dev 链表里面,同时下面就行匹配对应handler的时候需要遍历 handler 链表:

static LIST_HEAD(input_dev_list);static LIST_HEAD(input_handler_list);

可以看到用到了一个list_for_each_entry, 刚开始看到还没看懂,这是一个宏定义,原型是在/kernel/include/linux/list.h:

/** * list_for_each_entry    -    iterate over list of given type * @pos:    the type * to use as a loop cursor. * @head:    the head for your list. * @member:    the name of the list_struct within the struct. */#define list_for_each_entry(pos, head, member)                \    for (pos = list_entry((head)->next, typeof(*pos), member);    \         &pos->member != (head);     \    //就是个for循环,跳出条件遍历了一遍,又回到链表头         pos = list_entry(pos->member.next, typeof(*pos), member))

input_attach_handler(dev, handler) 则是匹配这个要注册 dev 的 handler:

static int input_attach_handler(struct input_dev *dev, struct input_handler *handler){
id = input_match_device(handler, dev); //返回匹配的id,类型是struct input_device_id error = handler->connect(handler, dev, id); //配对成功调用handler的connect函数,这个函数在事件处理器中定义,主要生成一个input_handle结构,并初始化,还生成一个事件处理器相关的设备结构}

可以看下匹配 id 的结构:

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 driver_info;};

有两个函数input_match_device 以及 下面的 connect需要了解:

三、input_match_device:

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++) {
if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) //匹配总线id if (id->bustype != dev->id.bustype) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) //匹配生产商id if (id->vendor != dev->id.vendor) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) //匹配产品id if (id->product != dev->id.product) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) //匹配版本 if (id->version != dev->id.version) continue; MATCH_BIT(evbit, EV_MAX); //匹配id的evbit和input_dev中evbit的各个位,如果不匹配则continue,数组中下一个设备 MATCH_BIT(keybit, KEY_MAX); MATCH_BIT(relbit, REL_MAX); MATCH_BIT(absbit, ABS_MAX); MATCH_BIT(mscbit, MSC_MAX); MATCH_BIT(ledbit, LED_MAX); MATCH_BIT(sndbit, SND_MAX); MATCH_BIT(ffbit, FF_MAX); MATCH_BIT(swbit, SW_MAX); if (!handler->match || handler->match(handler, dev)) return id; } return NULL;}

MATCH_bit 原型:

#define MATCH_BIT(bit, max) \		for (i = 0; i < BITS_TO_LONGS(max); i++) \			if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \				break; \		if (i != BITS_TO_LONGS(max)) \			continue;

可以看到这么多步的目的除了初始以及添加input_dev到链表,就是为了去匹配 input_handler_list 中对应的handler ,

匹配的最终是需要比对handler以及input_dev中的 id,其中input_dev 中的id类型为 input_id :

struct input_id {
__u16 bustype; __u16 vendor; __u16 product; __u16 version;};

这跟上面 input_handler 结构里面的 input_device_id 匹配id 变量,来确认 handler!

在最开始的时候就有提到,整个input输入体系,分三个层次,现在的input核心层做的事就是:

在硬件驱动层调用 input_register_device时 ,往内核注册驱动的同时,根据硬件的相关id去匹配 适用的事件处理层(input_handler)!

这里匹配上之后就会调用对应 input_handler 的connect 函数。

四、input_handler

input_handler 变量代表的是事件处理器

同样在input.h 中定义:

/** * 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 * @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 * @fops: file operations this driver implements * @minor: beginning of range of 32 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); 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); const struct file_operations *fops; int minor; const char *name; const struct input_device_id *id_table; //这个就是上面说到的 会跟input_dev中的input_id 比对 id项的 struct list_head h_list; struct list_head node;};

这个结构详细的含义,注释有。

这个结构里面暂时只需要理解的:

注册input_dev ,在事件处理数据链表里面匹配上 input_handler ,就会调用其 *connect 函数指针 进行连接,

将input_dev 跟 input_handler 进行绑定, 后续的运作事件的handler处理将会走这个input_handler的 *event !

在上篇input_event 传递中最后调用到event阶段.

这里简单记录到这里,下篇介绍input_handler 的处理机制~

————————————————

原文链接:https://blog.csdn.net/jscese/article/details/42123673

转载地址:https://ciellee.blog.csdn.net/article/details/104917231 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:【输入子系统04】input_handler之evdev
下一篇:【输入子系统02】输入子系统Input_event 传递

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2024年05月02日 02时27分49秒