厦门seo建站,wordpress后台修改文件,为什么做的网站打开自动缩放,公司开发设计推荐Linux input 更多内容可以查看我的github Linux输入子系统框架
Linux输入子系统由驱动层、核心层、事件处理层三部分组成。
驱动层#xff1a;输入设备的具体驱动程序#xff0c;负责与具体的硬件设备进行交互#xff0c;并将底层的硬件输入转化为统一的事件形式#xff…Linux input 更多内容可以查看我的github Linux输入子系统框架
Linux输入子系统由驱动层、核心层、事件处理层三部分组成。
驱动层输入设备的具体驱动程序负责与具体的硬件设备进行交互并将底层的硬件输入转化为统一的事件形式向核心层发送核心层连接驱动层和事件处理层负责对下层提供驱动层借口向上层提供事件处理接口事件处理层负责对输入事件进行处理并将处理结果传递给应用程序层的设备抽象出对应的接口提供给应用层。将底层设备的触发的事件通过这个接口传达给应用层。
核心层的代码在 linux/drivers/input/input.c 中实现
重要结构体
input_dev
这个结构体属于驱动层描述了一个具体的input设备记录相关的硬件信息事件位图等
struct input_dev {const char *name; // 设备名称struct input_id id; // 设备id存储输入设备的总线、厂商等信息...unsigned long evbit[BITS_TO_LONGS(EV_CNT)]; // 支持事件类型unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; // 按键位图unsigned long relbit[BITS_TO_LONGS(REL_CNT)]; // 相对位移位图...struct list_head h_list; // 内核链表头struct list_head node; // 内核链表节点
};Linux设备支持的事件类型:
事件类型编码事件描述EV_SYN0x00同步事件EV_KEY0x01按键事件鼠标键盘等EV_REL0x02相对坐标(如鼠标移动报告相对最后一次位置的偏移)EV_ABS0x03绝对坐标(如触摸屏或操作杆报告绝对的坐标位置)EV_MSC0x04其它EV_SWx05开关EV_LED0x11按键/设备灯EV_SND0x12声音/警报EV_REP0x14重复EV_FFx15力反馈EV_PWR0x16电源EV_FF_STATUS0x17力反馈状态EV_MAX0x1f事件类型最大个数和提供位掩码支持
input_handler
这个结构体属于事件处理层描述一个事件处理器
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;
};input_handle
这个结构体属于核心层描述一个配对的input设备和input设备处理器
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;
};input_handle_list
struct input_handle_list {struct list_head list;struct input_handle *first;struct input_handle *last;
};两条重要链表
在 input.c 中全局维护了两条重要的链表分别是输入设备链表和事件处理器链表
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);总结
上面结构体的链表的关系如下面两图所示这个图好难画-.-就在网上找了一个,原文连接 当我们使用input_register_device()注册一个设备的时候就会将设备添加到input_dev_list链表中同时便利input_handler_list进行匹配匹配成功就会调用input_handler-connect()函数进行连接
事件相关结构体
input_event
事件的输入就是以一个input_event为基本单位的
struct input_event {struct timeval time; /* 事件发生的时间 */__u16 type; /* 事件总类型 */__u16 code; /* 事件子类型 */__s32 value; /* 事件的值 */
};evdev_client
struct evdev_client {unsigned int head;unsigned int tail;unsigned int packet_head; /* [future] position of the first element of next packet */spinlock_t buffer_lock; /* protects access to buffer, head and tail */struct fasync_struct *fasync;struct evdev *evdev;struct list_head node;int clk_type;bool revoked;unsigned int bufsize;struct input_event buffer[];
};head表示客户端缓冲区中下一个要读取的事件的索引。当客户端从缓冲区读取事件时它会从 buffer[head] 开始读取并递增 head 的值。因此head 指向的是最老的未读取事件的位置。tail表示客户端缓冲区中下一个要写入的事件的索引。当输入事件到达并需要被缓冲时它将被写入到 buffer[tail] 的位置并递增 tail 的值。因此tail 指向的是最新的未写入事件的位置。
实际上evdev_client 实现了一个环形队列head是头指针tail是尾指针这两个指针都是以 input_event 为单位移动的。
packet_head 与 head 和 tail 不同, 它以数据包多个input_event为单位主要负责记录缓冲区的入口偏移量。
buffer 就是循环队列数组即缓冲区
所以根据这些变量我们可以知道当循环队列满的时候head tail当循环队列空的时候packet_head tail。
evdev
struct evdev {int open; // 设备打开计数struct input_handle handle; wait_queue_head_t wait; // 等待队列没有事件时进程睡眠struct evdev_client __rcu *grab; // 事件响应// 客户端链表可以有多个进程访问设备struct list_head client_list;spinlock_t client_lock; /* protects client_list */struct mutex mutex;struct device dev;struct cdev cdev;bool exist;
};总结
input_event: 表示一个输入事件evdev_client: 表示一个用户空间的应用程序或实体设备与输入设备之间的连接evdev: 输入设备驱动程序的接口实现应用程序可以通过evdev与evdev_client之间的交互实现输入事件的读取和输入
流程 这部分建议阅读源码 在输入设备驱动input_dev中一般通过轮询或者中断方式获取事件的原始值经过一些处理后通过input_event()函数将数据上报给核心层input_core。
在核心层中通过input_handle_event()和input_pass_values()对数据进行处理type、code、value然后使用input_to_handler()函数将数据上报给事件处理层input_handler在input_to_handler()中使用input_handler结构体中的事件处理函数event、events、filter上报这些函数可以在evdev.c的1235行的evdev_handler中找到。
在事件处理层中通过evdev_events()和evdev_pass_values()为事件加上时间戳完成了一个完整的input_event然后使用__pass_event()将事件传递给用户空间evdev_client的buffer中
在__pass_event()中事件input_event会被填充到evdev_client的buffer中。对于用于空间的应用程序可以通过read()函数调用内核空间的evdev_read()函数然后调用evdev_fetch_next_event()函数从环形缓冲区中读取input_event事件