Linux Kernel 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;
};
/**
 * 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;
};
<think>我们正在讨论Linux内核中如何检测系统是否正在使用触屏输入。根据提供的引用,我们可以从输入子系统的角度来考虑。 引用[1]提到触摸事件的上报流程和时间戳问题,引用[2]和[3]则涉及触摸屏驱动的具体实现,特别是上报触摸事件的机制。 在Linux内核中,触摸屏作为输入设备,其事件上报遵循输入子系统的框架。我们可以通过以下方式检测触屏状态: 1. **输入事件监听**:内核中可以通过注册输入事件通知链(input event notifier)来监听输入事件。当有触摸事件发生时,会触发回调函数。 2. **触摸点状态**:对于多点触摸,内核中会跟踪每个触摸点的状态(如按下、移动、抬起)。我们可以检查当前是否有至少一个触摸点处于活动状态(即按下或移动)来判断是否正在使用触屏。 3. **定时器检测**:如果没有触摸事件发生一段时间,我们可以认为触屏未被使用。但这种方法需要结合事件监听。 具体实现思路: - 我们可以使用`input_register_handler`或`input_register_notifier`来注册一个输入事件处理器或通知链,从而捕获触摸事件。 - 在事件处理函数中,我们可以根据事件类型(如`EV_ABS`(绝对坐标事件),`EV_KEY`(按键事件,可能用于触摸屏的按下/抬起))来更新一个全局状态变量。 - 对于触摸屏,我们通常关注`ABS_MT_SLOT`(多点触摸的槽位)和`ABS_MT_TRACKING_ID`(触摸点的ID,当ID为非负整数时表示触摸点被按下,-1表示抬起)等事件。 注意:由于触摸屏事件可能非常频繁,我们需要高效地处理,避免影响性能。 以下是一个简化的示例代码,展示如何在内核模块中注册一个输入通知链来检测触摸状态: ```c #include <linux/input.h> #include <linux/module.h> static int touch_state = 0; // 0表示没有触摸,1表示有触摸 static int input_touch_event(struct notifier_block *nb, unsigned long type, void *data) { struct input_event *event = data; // 我们只处理绝对坐标事件(触摸屏)和同步事件(用于标记一个事件序列的结束) if (type == EV_ABS) { // 根据具体事件更新状态 // 例如,如果检测到ABS_MT_TRACKING_ID事件,且其值为非负,则表示有触摸点按下 if (event->code == ABS_MT_TRACKING_ID) { if (event->value != -1) { touch_state = 1; // 有触摸点被按下 } else { touch_state = 0; // 所有触摸点抬起?注意:这里可能是一个触摸点抬起,但可能有多个点,所以需要更复杂的逻辑 } } } else if (type == EV_SYN && event->code == SYN_REPORT) { // 同步事件,表示一个事件序列结束,可以在这里更新状态 // 但通常我们已经在每个事件中更新了 } return 0; } static struct notifier_block touch_notifier = { .notifier_call = input_touch_event, }; static int __init touch_detector_init(void) { int ret; ret = input_register_notifier(&touch_notifier); if (ret) { printk(KERN_ERR "Failed to register input notifier\n"); return ret; } return 0; } static void __exit touch_detector_exit(void) { input_unregister_notifier(&touch_notifier); } module_init(touch_detector_init); module_exit(touch_detector_exit); MODULE_LICENSE("GPL"); ``` 但是,上述代码只是一个非常简化的示例,实际应用中需要考虑多点触摸的情况,即可能有多个触摸点,我们需要跟踪所有触摸点的状态(例如,使用一个数组记录每个槽位的状态)。当至少有一个触摸点处于按下状态时,认为正在使用触屏。 另外,我们也可以考虑使用`input_get_timestamp`函数来获取事件的时间戳,然后通过检查最近一段时间内是否有触摸事件发生来判断当前是否在使用触屏(比如,如果最近100ms内有触摸事件,则认为正在使用)。 注意:在实现时,需要确保对共享数据(如`touch_state`)的访问是线程安全的(使用锁保护)。 根据引用[3],触摸屏驱动在上报触摸点坐标后,会调用`input_sync`发送一个`SYN_REPORT`事件,这标志着一组触摸事件的结束。因此,我们可以通过监听`SYN_REPORT`事件来确认一次触摸动作的完成,但并不能直接判断是否“正在使用”,因为一次触摸可能包含多个事件(按下、移动、抬起)。所以,我们需要维护一个状态机。 更准确的方法是: - 当收到一个`ABS_MT_TRACKING_ID`事件且值非负时,表示一个新的触摸点开始,此时设置状态为“有触摸”。 - 当收到一个`ABS_MT_TRACKING_ID`事件且值为-1时,表示一个触摸点结束。但是,当所有触摸点都结束时(即所有槽位的`ABS_MT_TRACKING_ID`都为-1),才设置状态为“无触摸”。 由于多点触摸可能有多个点同时存在,我们需要为每个槽位(slot)记录其触摸点是否存在(即`ABS_MT_TRACKING_ID`是否非负)。我们可以使用一个位图或数组来记录每个槽位的状态。 因此,改进后的思路: 1. 定义一个数组`slot_active[]`,大小等于最大槽位数(通常为10,根据触摸屏支持的最大点数)。 2. 在事件处理函数中,当处理`ABS_MT_SLOT`事件时,记录当前操作的槽位索引。 3. 当处理`ABS_MT_TRACKING_ID`事件时,根据当前槽位索引更新`slot_active`数组:如果`event->value>=0`,则`slot_active[current_slot]=1`;如果`event->value==-1`,则`slot_active[current_slot]=0`。 4. 然后,检查整个`slot_active`数组:如果至少有一个槽位是激活的(值为1),则设置全局状态为“有触摸”;否则为“无触摸”。 注意:由于输入事件可能来自多个设备,我们需要过滤出触摸屏设备。可以通过检查输入设备的ID(如`input_dev->id`)或者设备名称(如`input_dev->name`)来判断是否是触摸屏设备。 另外,我们也可以考虑使用`input_dev`的`event`回调,但需要修改驱动,不太通用。而使用通知链可以不用修改现有驱动。 综上所述,通过注册输入事件通知链,并跟踪触摸点状态,我们可以检测系统是否正在使用触屏输入。 但是,请注意,这种方法需要在内核模块中实现,并且可能会对系统性能产生一定影响(因为所有输入事件都会经过我们的处理函数)。因此,在实际产品中需要谨慎使用,并优化事件处理逻辑(例如,快速过滤非触摸事件)。 最后,如果只是用户空间的需求,建议在用户空间通过读取`/dev/input/eventX`设备文件来监听触摸事件,这样更安全且易于实现。 以上是内核层面的检测方法。</think>在 Linux 内核中检测触屏使用状态,可以通过输入子系统框架实现。以下是核心机制和方法: ### 检测原理 1. **输入事件监听** 内核通过 `input_event` 结构体接收触屏事件: ```c struct input_event { struct timeval time; // 时间戳 __u16 type; // 事件类型 (EV_ABS, EV_KEY等) __u16 code; // 具体代码 (ABS_X, ABS_MT_TRACKING_ID等) __s32 value; // 事件值 }; ``` 关键事件类型: - `EV_ABS`:绝对坐标事件(触屏坐标) - `EV_KEY`:按键事件(触屏按下/抬起) - `EV_SYN`:同步事件(标记事件组结束) 2. **触摸状态判定** 检测 `ABS_MT_TRACKING_ID` 的值: ```c if (event->type == EV_ABS && event->code == ABS_MT_TRACKING_ID) { if (event->value >= 0) { // 触摸点激活 (手指按下) touch_active = true; } else { // 触摸点释放 (手指抬起) touch_active = false; } } ``` 当 `value >= 0` 表示有有效触摸点,`value = -1` 表示触摸结束[^3]。 3. **同步事件确认** 每次触摸事件序列以 `SYN_REPORT` 结束: ```c if (event->type == EV_SYN && event->code == SYN_REPORT) { // 完成一组触摸事件上报 } ``` 通过判断 `SYN_REPORT` 前是否有有效触摸点,可确认触屏状态[^3]。 ### 实现方法 #### 1. 内核驱动层检测 在触摸驱动中直接维护状态变量: ```c static bool is_touch_active = false; static void touch_event_handler(struct input_dev *dev, unsigned int type, unsigned int code, int value) { if (type == EV_ABS && code == ABS_MT_TRACKING_ID) { is_touch_active = (value >= 0); // 更新触摸状态 } input_event(dev, type, code, value); // 继续上报事件 } ``` #### 2. 输入子系统拦截 通过注册输入事件处理器全局监听: ```c static int input_touch_filter(struct notifier_block *nb, unsigned long type, void *data) { struct input_event *event = data; if (event->type == EV_ABS && event->code == ABS_MT_TRACKING_ID) { global_touch_state = (event->value >= 0); } return NOTIFY_OK; } static struct notifier_block touch_notifier = { .notifier_call = input_touch_filter, }; // 初始化时注册 input_register_notifier(&touch_notifier); ``` ### 关键注意事项 1. **时间戳处理** 需确保事件时间戳正确(使用 `ktime_get_real_ts64` 获取时间),避免因时间滞后导致状态判断错误[^1]。 2. **多点触摸处理** 对于多点触屏,需遍历所有槽位(slot): ```c for (int slot = 0; slot < max_slots; slot++) { input_mt_get_value(touch_dev, slot, ABS_MT_TRACKING_ID, &id); if (id >= 0) return true; // 任一有效触点即表示使用中 } ``` 3. **性能优化** 避免在中断上下文中进行复杂处理,可通过: - 使用原子变量存储状态 (`atomic_t`) - 延迟工作队列处理非紧急逻辑 ### 用户空间查询接口 可通过 `/sys` 或 `/proc` 暴露状态: ```c static ssize_t touch_state_show(struct device *dev, struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", atomic_read(&touch_active)); } DEVICE_ATTR_RO(touch_state); ``` 用户空间通过 `cat /sys/class/input/touchscreen/state` 获取当前状态。 --- **总结**:Linux 内核通过输入子系统的 `ABS_MT_TRACKING_ID` 事件值检测触屏使用状态,结合同步事件确保状态准确性。开发者可在驱动层或通过输入通知链实现状态跟踪,并注意时间戳和多点触摸的处理[^3][^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值