生态农业网站模板,专做西餐的网站,合肥电脑培训,南通建设网站公司目录 一、input子系统二、关键数据结构和api2.1 数据结构2.1.1 input_dev2.1.2 input_handler2.1.3 input_event2.1.4 input_handle 2.2 api接口2.2.1 input_device 相关接口input_device 注册流程事件上报 2.2.2 input handle 相关接口注册 handle指定 handle 2.2.3 input han… 目录 一、input子系统二、关键数据结构和api2.1 数据结构2.1.1 input_dev2.1.2 input_handler2.1.3 input_event2.1.4 input_handle 2.2 api接口2.2.1 input_device 相关接口input_device 注册流程事件上报 2.2.2 input handle 相关接口注册 handle指定 handle 2.2.3 input handler 相关接口注册handler 三、input handler3.1 evdev handler3.1.1 handler 注册3.1.2 evdev_connect3.1.3 evdev_events3.1.4 file_operations 3.2 mousedev handler 一、input子系统
input子系统处理Linux下的输入事件。
驱动层输入设备的驱动程序负责检测和接收输入设备的输入事件将输入事件上报给核心层
核心层提供设备驱动、事件 handler 注册和操作的接口接收驱动层的输入事件并上报给事件处理层
事件处理层通过提供 sysfs 接口等方式和用户空间交互例如用户空间打开特定设备当有输入数据时就会上传给用户空间。input子系统框架结构图总结来自这里
input driver 接收到硬件的输入事件 发送到input coreinput core 根据事件类型 将事件交给对应的input handler处理 input handler 上报用户空间用户空间收收到事件后进行对应的处理。
二、关键数据结构和api
2.1 数据结构
2.1.1 input_dev
input_dev 描述输入设备结构体中的多个 bitmap 描述了输入设备的类型和支持的输入事件。这些事件类型相关的宏定义在 input-event-codes.h 头文件中。
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)]; // 设备支持的事件类型的bitmapunsigned 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)]; // ledunsigned 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;struct input_dev_poller *poller;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;ktime_t timestamp[INPUT_CLK_MAX];
};2.1.2 input_handler
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;
};以 evdev handler 为例
connect 接口通过 input_register_handle 接口实现将 input_dev 和 input_handler 绑定并创建对应输入设备的字符设备
event/events 接口将设备的输入拷贝到 buffer 中当用户空间调用字符设备的 read 接口时就可以从 buffer 中读取输入信息2.1.3 input_event
handler 上报事件到用户层的时候以 input_event 格式进行上报。
struct input_event {struct timeval time; // 事件发生事件__u16 type; // 事件类型例如 EV_KEY 按键类型__u16 code; // 事件编码例如 KEY_0 按键__s32 value; // 事件值
};2.1.4 input_handle
input_handle 实现将 input_device 和 input_handler 绑定的功能上面已经介绍到evdev handler 的 connect 接口中会调用 input_register_handle 接口实现将 input_dev 和 input_handler 绑定。
struct input_handle {void *private;int open; // 当前handle是否openconst char *name;struct input_dev *dev;struct input_handler *handler;struct list_head d_node;struct list_head h_node;
};open 记录了当前 handle 是否被 open以 evdev 为例当用户空间 open 字符设备的时候会调用到input_open_device 接口接口内部实现 input_handle-open。
2.2 api接口
2.2.1 input_device 相关接口
input核心层提供了如下一系列input device相关的接口事件input device的注册、事件的上报等功能
// 申请
struct input_dev *input_allocate_device(void);
struct input_dev *devm_input_allocate_device(struct device *dev);// 设置支持的事件类型
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)// 注册、注销
int input_register_device(struct input_dev *dev);
void input_unregister_device(struct input_dev *dev);// 释放
void input_free_device(struct input_dev *dev);// 事件上报
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value);
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value);
static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value);
static inline void input_sync(struct input_dev *dev); // 同步通知事件发送完成
static inline void input_mt_sync(struct input_dev *dev);input_device 注册流程
注册接口中主要做了以下动作
检查 bitmap 等参数设置是否正确将 device 添加到 input_device_list 链表中对于 input_handler_list 中的每一个 handler调用 input_attach_handler 接口尝试将 device 和 handler 绑定在接口内部会检查 device 和 handler 是否 matchmatch 的话则调用 handler 的 connect 接口完成绑定动作。
int input_register_device(struct input_dev *dev)
{// 检查bitmap等参数、配置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);/* Make sure that bitmasks not mentioned in dev-evbit are clean. */input_cleanse_bitmasks(dev);dev-max_vals dev-hint_events_per_packet 2;dev-vals kcalloc(dev-max_vals, sizeof(*dev-vals), GFP_KERNEL);// device_adderror device_add(dev-dev);// device 和 handler绑定error mutex_lock_interruptible(input_mutex);list_add_tail(dev-node, input_dev_list);list_for_each_entry(handler, input_handler_list, node)input_attach_handler(dev, handler);input_wakeup_procfs_readers();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;
}
EXPORT_SYMBOL(input_register_device);事件上报
input 子系统中封装了针对不同类型事件的上报接口例如 input_report_key\input_report_abs 等这些接口实际都是调用 input_event 接口完成事件上报只不过接口参数中的 type 类型不同以 input_report_key 为例
input_report_key(struct input_dev *dev, unsigned int code, int value)- input_event(dev, EV_KEY, code, !!value);- input_handle_event(dev, type, code, value);- input_get_disposition(dev, type, code, value); // 获取事件类型- input_pass_values(dev, dev-vals, dev-num_vals); - input_to_handler(handle, vals, count);- handler-events(handle, vals, count); // 通知handler处理事件在 input_handle_event 接口中会将事件缓存在 dev-vals 中并记录事件数目到 dev-num_vals当检测到 dev-num_vals dev-max_vals - 2 或者 input_sync 事件时将所有缓存事件通知 handler 处理。
static void input_handle_event(struct input_dev *dev,unsigned int type, unsigned int code, int value)
{// 获取事件类型int disposition input_get_disposition(dev, type, code, value);if (disposition ! INPUT_IGNORE_EVENT type ! EV_SYN)add_input_randomness(type, code, value);// 如果 INPUT_PASS_TO_DEVICE并且device实现了event则通知deviceif ((disposition INPUT_PASS_TO_DEVICE) dev-event)dev-event(dev, type, code, value);if (!dev-vals)return;// 记录要通知给handler的事件if (disposition INPUT_PASS_TO_HANDLERS) {struct input_value *v;if (disposition INPUT_SLOT) {v dev-vals[dev-num_vals];v-type EV_ABS;v-code ABS_MT_SLOT;v-value dev-mt-slot;}v dev-vals[dev-num_vals];v-type type;v-code code;v-value value;}// sync事件或者要超出缓存则将缓存的vals flush到handlerif (disposition INPUT_FLUSH) {if (dev-num_vals 2)input_pass_values(dev, dev-vals, dev-num_vals);dev-num_vals 0;dev-timestamp[INPUT_CLK_MONO] ktime_set(0, 0);} else if (dev-num_vals dev-max_vals - 2) {dev-vals[dev-num_vals] input_value_sync;input_pass_values(dev, dev-vals, dev-num_vals);dev-num_vals 0;}}2.2.2 input handle 相关接口
int input_register_handle(struct input_handle *handle);
void input_unregister_handle(struct input_handle *handle);注册 handle
input_register_handle 接口实现注册一个input_handle将 device 和 handler 绑定例如在 evdev handler 的 connect 接口中就调用了 input_register_handle 接口。 接口流程
int input_register_handle(struct input_handle *handle)
{// 将 handle-d_node 加入到 dev-h_list实现遍历dev-h_list就能找到所有关联的input_handle进而找到input_handlerif (handler-filter)list_add_rcu(handle-d_node, dev-h_list);elselist_add_tail_rcu(handle-d_node, dev-h_list);// 将 handle-h_node 加入到 handler-h_list实现遍历handler-h_list就能找到所有关联的input_handler进而找到input_devicelist_add_tail_rcu(handle-h_node, handler-h_list);if (handler-start)handler-start(handle);return 0;
}在 input_register_handle 接口中会将 handle-d_node 加入到 dev-h_list实现遍历dev-h_list就能找到所有关联的input_handle进而找到input_handler。 实际上在 input_pass_values 中如果未指定 input_device 的 input_handle 就是通过遍历列表的方式将事件通过所有关联的 input_handle 发送到 input_handler 中。 也就是说默认input_event的事件上报是一个广播行为 handle rcu_dereference(dev-grab);if (handle) {count input_to_handler(handle, vals, count); // 指定handle} else {list_for_each_entry_rcu(handle, dev-h_list, d_node) // 广播if (handle-open) {count input_to_handler(handle, vals, count);if (!count)break;}}指定 handle
在 input_grab_device 接口中实现了 dev-grab 与 handle 的绑定
int input_grab_device(struct input_handle *handle)
{if (dev-grab) {retval -EBUSY;goto out;}rcu_assign_pointer(dev-grab, handle);
}以 evdev handler 为例ioctl 中实现了 EVIOCGRAB用于 input_device 指定 input_handle:
// ioctl接口中调用evdev_grab或evdev_ungrab事件绑定和解绑case EVIOCGRAB:if (p)return evdev_grab(evdev, client);elsereturn evdev_ungrab(evdev, client);// evcev_grab中调用 input_grab_device 实现 dev-grab 与 handle 的绑定
static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
{int error;if (evdev-grab)return -EBUSY;error input_grab_device(evdev-handle);if (error)return error;rcu_assign_pointer(evdev-grab, client);return 0;
}2.2.3 input handler 相关接口
// 注册、注销
int input_register_handler(struct input_handler *handler);
void input_unregister_handler(struct input_handler *handler);注册handler
input_register_handler 接口中将 handler 添加到 input_handler_list 中遍历 input_dev_list执行input_attach_handler(dev, handler):
int input_register_handler(struct input_handler *handler)
{INIT_LIST_HEAD(handler-h_list);list_add_tail(handler-node, input_handler_list);list_for_each_entry(dev, input_dev_list, node)input_attach_handler(dev, handler);return 0;
}三、input handler
3.1 evdev handler
3.1.1 handler 注册
在evdev_init中调用 input_register_handler 实现 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,
};static int __init evdev_init(void)
{return input_register_handler(evdev_handler);
}3.1.2 evdev_connect
evdev_connect 接口在 input_attach_handler 中被调用接口实现以下功能
以 evdev_fops 为 file_operations 创建 cdev设备名称为 event%d调用 input_register_handle 实现 input_device 与 input_handler 的绑定
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,const struct input_device_id *id)
{minor input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true);INIT_LIST_HEAD(evdev-client_list);dev_no minor;dev_set_name(evdev-dev, event%d, dev_no);evdev-dev.devt MKDEV(INPUT_MAJOR, minor);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);cdev_init(evdev-cdev, evdev_fops);error cdev_device_add(evdev-cdev, evdev-dev);}3.1.3 evdev_events
evdev_events 接口负责处理 input_device 上报的事件并上报给用户层
handler-events(handle, vals, count); // evdev_events读时间-- evdev_pass_values(client, vals, count, ev_time); // 组input_event-- __pass_event(client, event); // 将event存在evdev_client的buffer中-- kill_fasync(client-fasync, SIGIO, POLL_IN); // 异步信号通知用户层-- wake_up_interruptible(evdev-wait); // 唤醒等待队列3.1.4 file_operations
evdev handler 的 file_operations 提供了 fasync\poll\read 等接口供用户层读取 input event。
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,
};evdev_fasync接口实现异步通知处理函数当有input_event事件时在evdev_events接口中最终会调用 kill_fasync实现发送异步通知信号用户层接收到状态变化后可知晓有input_event事件需要处理。 evdev_read接口为用户空间提供了读input_event事件的接口实际是将evdev_events接口中缓存在buffer中的数据copy到用户空间。当缓存中没有数据是调用wait_event_interruptible 等待 evdev_events 唤醒。
3.2 mousedev handler
TODO
参考链接https://www.cnblogs.com/arnoldlu/p/17952329