Linux 输入子系统分析(二)

news/2025/2/26 19:54:18

Linux 输入子系统分析(一)
Linux 输入子系统分析(二)
分析一个内核提供的input_handler

input_dev驱动

input_dev驱动程序的工作主要是:申请一些硬件资源,如注册中断等,申请input_dev并设置,然后调用核心层提供的input_register_device函数进行注册。

设备有数据时,调用input_event函数向核心层报告事件,如键盘按下,读取按键值,然后上报事件。

前面提到input_dev会与input_handler进行匹配,具体怎么匹配呢?调用input_register_device函数注册input_dev时会进行匹配的,该函数定义如下:

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;

	......

	/* 所有的输入设备都要支持同步事件 */
	__set_bit(EV_SYN, dev->evbit);

	......


	/* 键盘类设备的长按支持,通过定时器实现,这里设置定时时间、周期等
     默认定时器的处理函数为input_repeat_key
   */
	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);
	if (error)
		goto err_free_vals;

	......
	//把input_dev插入全局链表input_dev_list,所有注册的input_dev都会插入该链表
	list_add_tail(&dev->node, &input_dev_list);

	/* 遍历全局链表input_handler_list,取出每个input_handler,调用input_attach_handler函数
     进行匹配,所有注册的input_handler都会插入该链表	   
	*/ 
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);

	......

}

input_dev与input_handler的匹配:

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);
	if (!id)
		return -ENODEV;

	//匹配成功,调用input_handler->connect
	error = handler->connect(handler, dev, id);
	
	......

	return error;
}

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;
	
	//设置了input_device_id->flags或driver_info才匹配
	for (id = handler->id_table; id->flags || id->driver_info; id++) {

		//设置了input_device_id->flags,表示进行bus类型、供应商、产品等信息

		//bus类型匹配
		if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
			if (id->bustype != dev->id.bustype)
				continue;

		//供应商匹配
		if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
			if (id->vendor != dev->id.vendor)
				continue;

		//产品匹配
		if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
			if (id->product != dev->id.product)
				continue;

		//版本匹配
		if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
			if (id->version != dev->id.version)
				continue;

		/* 如果input_device_id->evbit、input_device_id->keybit等位图是input_dev对应位图
		   的子集则表示匹配
		*/ 

		if (!bitmap_subset(id->evbit, dev->evbit, EV_MAX))
			continue;

		if (!bitmap_subset(id->keybit, dev->keybit, KEY_MAX))
			continue;

		if (!bitmap_subset(id->relbit, dev->relbit, REL_MAX))
			continue;

		if (!bitmap_subset(id->absbit, dev->absbit, ABS_MAX))
			continue;

		if (!bitmap_subset(id->mscbit, dev->mscbit, MSC_MAX))
			continue;

		if (!bitmap_subset(id->ledbit, dev->ledbit, LED_MAX))
			continue;

		if (!bitmap_subset(id->sndbit, dev->sndbit, SND_MAX))
			continue;

		if (!bitmap_subset(id->ffbit, dev->ffbit, FF_MAX))
			continue;

		if (!bitmap_subset(id->swbit, dev->swbit, SW_MAX))
			continue;

		//前面信息都匹配的话,调用input_handler->match函数进一步判断是否匹配
		if (!handler->match || handler->match(handler, dev))
			return id;
	}

	return NULL;
}

总结下input_register_device函数,执行流程图如下:
在这里插入图片描述
设备上报事件后,核心层会转交给input_handler处理,下面通过分析input_event函数看看其中的一些细节,input_event函数的整体执行流程如下:
在这里插入图片描述
源码分析

/* type,上报的事件类型
   code,上报的事件
	 value,事件的value,如按键按下value为1,按键松开value为0
*/
void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;

	//判断该事件类型是否支持
	if (is_event_supported(type, dev->evbit, EV_MAX)) {

		spin_lock_irqsave(&dev->event_lock, flags);
		input_handle_event(dev, type, code, value);
		spin_unlock_irqrestore(&dev->event_lock, flags);
	}
}

input_handle_event:

static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{

	/* 根据type、code,判断对该event做怎么样的处理
     INPUT_IGNORE_EVENT,表示忽略对该event的处理
     INPUT_PASS_TO_HANDLERS,表示该event交由input_handler处理
	 INPUT_PASS_TO_DEVICE,表示该event由设备自身来处理
     INPUT_PASS_TO_ALL,表示该event会由设备以及input_handler处理
   */
	int disposition = input_get_disposition(dev, type, code, &value);

	......

	//对于INPUT_PASS_TO_DEVICE、INPUT_PASS_TO_ALL,会调用input_dev->event函数处理 
	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
		dev->event(dev, type, code, value);

	......

	input_pass_values(dev, dev->vals, dev->num_vals);

	......
}

input_pass_values:

static void input_pass_values(struct input_dev *dev,
			      struct input_value *vals, unsigned int count)
{
	struct input_handle *handle;
	struct input_value *v;

	if (!count)
		return;

	rcu_read_lock();

	//如果input_dev->grab设置了,表示设备的所有上报事件由该input_handle来处理 
	handle = rcu_dereference(dev->grab);
	if (handle) {
		count = input_to_handler(handle, vals, count);
	} else {
		/* input_dev->grab未设置,遍历input_dev的h_list链表,得到与该设备关联的input_handle
       调用input_to_handler
     */
		list_for_each_entry_rcu(handle, &dev->h_list, d_node)
			//应用层open设备,才会调用input_to_handler,如"open /dev/eventX"
			if (handle->open) { 
				//事件交由input_handler处理
				count = input_to_handler(handle, vals, count);
				if (!count)
					break;
			}
	}

	rcu_read_unlock();

	/* input_dev的evbit设置了EV_REP和EV_KEY,表示该输入设备是键盘类设备,且支持长按
		 什么情况下表示长按呢?如下例子:
		 input_event(dev, EV_KEY, KEY_L, 1),传入的value为1,表示按下,之后会通过定时器实现不断
     地继续上报该事件,直到input_event(dev, EV_KEY, KEY_L, 0)为止
		*/
	if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) {
		for (v = vals; v != vals + count; v++) {
			if (v->type == EV_KEY && v->value != 2) {
				if (v->value)    
					input_start_autorepeat(dev, v->code);
				else
					input_stop_autorepeat(dev);   //value为0,停止上报事件
			}
		}
	}
}

input_to_handler:

static unsigned int input_to_handler(struct input_handle *handle,
			struct input_value *vals, unsigned int count)
{
	struct input_handler *handler = handle->handler;
	struct input_value *end = vals;
	struct input_value *v;

	//对于有些event,input_handle不处理,调用input_handle->filter函数过滤掉
	if (handler->filter) {
		for (v = vals; v != vals + count; v++) {
			if (handler->filter(handle, v->type, v->code, v->value))
				continue;
			if (end != v)
				*end = *v;
			end++;
		}
		count = end - vals;
	}

	if (!count)
		return 0;

	//最终调用input_handle->events函数进行处理事件
	if (handler->events)
		handler->events(handle, vals, count);
	else if (handler->event)
		for (v = vals; v != vals + count; v++)
			handler->event(handle, v->type, v->code, v->value);

	return count;
}

设备上报的事件交由input_handler处理后,如果该是键盘类设备会涉及长按等情况,内核通过定时器实现长按。
input_start_autorepeat:

static void input_start_autorepeat(struct input_dev *dev, int code)
{
	//启动定时器,定时器默认处理函数为input_repeat_key
	if (test_bit(EV_REP, dev->evbit) &&
	    dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
	    dev->timer.data) {
		dev->repeat_key = code;
		mod_timer(&dev->timer,
			  jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
	}
}


static void input_repeat_key(unsigned long data)
{
	struct input_dev *dev = (void *) data;
	unsigned long flags;

	spin_lock_irqsave(&dev->event_lock, flags);

	if (test_bit(dev->repeat_key, dev->key) &&
	    is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
		struct input_value vals[] =  {
			{ EV_KEY, dev->repeat_key, 2 }, 
			input_value_sync
		};
		
		//继续执行input_pass_values
		input_pass_values(dev, vals, ARRAY_SIZE(vals));

		//重新修改时间
		if (dev->rep[REP_PERIOD])
			mod_timer(&dev->timer, jiffies +
					msecs_to_jiffies(dev->rep[REP_PERIOD]));
	}

	spin_unlock_irqrestore(&dev->event_lock, flags);
}

//直到input_event(..., 0)
static void input_stop_autorepeat(struct input_dev *dev)
{
	
	del_timer(&dev->timer);
}

http://www.niftyadmin.cn/n/3657297.html

相关文章

分析一个内核提供的input_handler

Linux 输入子系统分析(一) Linux 输入子系统分析(二) 分析一个内核提供的input_handler 对应一些常用的输入设备,如键盘、鼠标等,内核提供的对应的input_handler,用来统一处理这类设备的事件。…

Tips - Web UI 资源索引

Basic (CSS,Javascript,AJAX...) - http://www.realsoftwaredevelopment.com/2007/08/ultimate-web-de.htmlAdvanced CSS - http://www.smashingmagazine.com/2007/01/19/53-css-techniques-you-couldnt-live-without/

GPIO子系统架构分析(一)

GPIO子系统架构分析(一) GPIO子系统架构分析(二) 通过sysf操作gpio GPIO子系统架构 在设备驱动中对GPIO的操作是非常普遍的,linux内核提供了GPIO子系统,方便用户使用,它为用户提供了GPIO的统一…

Tip - SQL报表 - 按每N行记录将报表分页

报表可以通过设置其Layout的大小来控制分页,但是如何更精确的按照每N行记录对报表分页?see - http://msdn2.microsoft.com/en-us/library/ms157328.aspxPage BreaksIn some reports, you may want to place a page break at the end of a specified numb…

GPIO子系统架构分析(二)

GPIO子系统架构分析(一) GPIO子系统架构分析(二) 通过sysf操作gpio gpio lib gpio lib(drivers\gpio\gpiolib.c)中提供了一组不依赖于硬件平台的接口函数,理解其中的主要函数非常关键。gpio l…

通过sysfs操作gpio

GPIO子系统架构分析(一) GPIO子系统架构分析(二) 通过sysf操作gpio 在gpio-sysfs.c驱动文件的入口函数: static struct class_attribute gpio_class_attrs[] {__ATTR(export, 0200, NULL, export_store),__ATTR(une…

Hot Tips: CSS 资源库 / CSS利器 - X光透视

CSS 资源库 - http://www.dynamicdrive.com/style/CSS X光透视 - http://westciv.com/xray/

pinctrl子系统分析(一)

pinctrl子系统分析(一) pinctrl子系统分析(二) pinctrl子系统分析(三) pinctrl子系统的软件架构 许多SoC的内部都包含了pin控制器,通过pin控制器,我们可以匹配引脚的状态和功能特性…