Android TV 长按事件在input驱动中的拦截

遇到的问题是,需要取消Linux的Input驱动对面板按键长按事件的拦截。

主体过程:

一.在Input驱动中加了log,观察event的传递情况,找出input驱动是在哪里进行长按事件拦截的。

二.分析得知在Input.c的input_get_disposition函数中有对长按事件的拦截。

三.处理:在Input驱动中屏蔽拦截代码

四.更好的处理方式:设备驱动注册事件到input_event的时候改变用于表示按键状态的参数value:1改成2。

详细过程:

1.事件注入到input_event中,有三个值type,code,value

void input_event(struct input_dev *dev,
		 unsigned int type, unsigned int code, int value)
{
	unsigned long flags;
	printk("duanliang,function=%s,type=%d,code=%d,value=%d;\n",__FUNCTION__,type,code,value);
	if (is_event_supported(type, dev->evbit, EV_MAX)) {
		printk("duanliang,function=%s,is_event_supported==true\n",__FUNCTION__);

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

2.如果按键事件类型支持,则交给input_handle_event处理

static void input_handle_event(struct input_dev *dev,
			       unsigned int type, unsigned int code, int value)
{
	int disposition;
	disposition = input_get_disposition(dev, type, code, value);
	printk("duanliang,function=%s,type=%d,code=%d,value=%d,disposition=%d;\n",__FUNCTION__,type,code,value,disposition);

	if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
	{
		printk("duanliang,function=%s,dev->event\n",__FUNCTION__);
		dev->event(dev, type, code, value);
	}
	if (!dev->vals)
	{
		printk("duanliang,function=%s,!dev->value\n",__FUNCTION__);
		return;
	}
	if (disposition & INPUT_PASS_TO_HANDLERS) {
		printk("duanliang,function=%s,disposition & INPUT_PASS_TO_HANDLERS,dev->num_vals=%d\n",__FUNCTION__,dev->num_vals);
		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;
	}

	if (disposition & INPUT_FLUSH) {
		printk("===============================\nduanliang,function=%s,disposition & INPUT_FLUSH==true,dev->num_vals=%d\n",__FUNCTION__,dev->num_vals);
		if (dev->num_vals >= 2)
			input_pass_values(dev, dev->vals, dev->num_vals);
		dev->num_vals = 0;
	} else if (dev->num_vals >= dev->max_vals - 2) {
		dev->vals[dev->num_vals++] = input_value_sync;
		printk("duanliang,function=%s,before input_pass_values\n",__FUNCTION__);
		input_pass_values(dev, dev->vals, dev->num_vals);
		dev->num_vals = 0;
	}

}


3.其中会通过input_get_disposition处理按键的状态,决定是否拦截,这里只看type==EV_KEY的情况,这里就是对长按事件是否进行拦截的代码:

static int input_get_disposition(struct input_dev *dev,
			  unsigned int type, unsigned int code, int value)
{
	int disposition = INPUT_IGNORE_EVENT;
	switch (type) {
	case EV_SYN:
		switch (code) {
		case SYN_CONFIG:
			disposition = INPUT_PASS_TO_ALL;
			break;

		case SYN_REPORT:
			printk("duanliang,function=%s,dev->event\n",__FUNCTION__);
			disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
			break;
		case SYN_MT_REPORT:
			disposition = INPUT_PASS_TO_HANDLERS;
			break;
		}
		break;

	case EV_KEY:
		if (is_event_supported(code, dev->keybit, KEY_MAX)) {
			printk("duanliang,function=%s,EV_KEY,dev->key = %0xd\n",__FUNCTION__,dev->key);
			/* auto-repeat bypasses state updates */
			if (value == 2) {
				disposition = INPUT_PASS_TO_HANDLERS;
				break;
			}
			
			if (!!test_bit(code, dev->key) != !!value) {
				printk("*************************************\nduanliang,function=%s,!!test_bit(code, dev->key) != !!value\n",__FUNCTION__);

				__change_bit(code, dev->key);
				disposition = INPUT_PASS_TO_HANDLERS;	
			}
		}
		break;

	}

	return disposition;
}

4.log分析:

如果是面板按键的长按,设备驱动会不停向input_event注册DOWN事件:

DOWN(value=1)

DOWN(value=1)

DOWN(value=1)

DOWN(value=1)

DOWN(value=1)

UP(value=0)

log如下:

[  440.804905] duanliang,function=input_event,type=1,code=106,value=1;
[  440.804909] duanliang,function=input_event,is_event_supported==true
[  440.804911] duanliang,function=input_get_disposition,EV_KEY
[  440.804915] duanliang,function=input_handle_event,type=1,code=106,value=1,disposition=0;

 

[  440.804919] duanliang,function=input_event,type=0,code=0,value=0;
[  440.804921] duanliang,function=input_event,is_event_supported==true
[  440.804923] duanliang,function=input_get_disposition,dev->event
[  440.804927] duanliang,function=input_handle_event,type=0,code=0,value=0,disposition=9;
[  440.804930] duanliang,function=input_handle_event,disposition & INPUT_PASS_TO_HANDLERS
[  440.804933] ===============================
[  440.804933] duanliang,function=input_handle_event,disposition & INPUT_FLUSH==true,dev->num_vals=1

如果是红外遥控器的长按,由于红外遥控器用连续的短按来模拟长按,所以这里红外遥控器发送的是连续的一个DOWN,一个UP的发送:

DOWN(value=1)

UP(value=0)

DOWN(value=1)

UP(value=0)

DOWN(value=1)

UP(value=0)

DOWN(value=1)

UP(value=0)

DOWN(value=1)

UP(value=0)

log如下:

/ # [ 1456.495682] duanliang,function=input_event,type=1,code=106,value=1;
[ 1456.501934] duanliang,function=input_event,is_event_supported==true
[ 1456.508173] duanliang,function=input_get_disposition,EV_KEY
[ 1456.513720] *************************************
[ 1456.513720] duanliang,function=input_get_disposition,!!test_bit(code, dev->key) != !!value
[ 1456.526620] duanliang,function=input_handle_event,type=1,code=106,value=1,disposition=1;
[ 1456.534671] duanliang,function=input_handle_event,disposition & INPUT_PASS_TO_HANDLERS

[ 1456.542551] duanliang,function=input_event,type=0,code=0,value=0;
[ 1456.548614] duanliang,function=input_event,is_event_supported==true
[ 1456.554852] duanliang,function=input_get_disposition,dev->event
[ 1456.560747] duanliang,function=input_handle_event,type=0,code=0,value=0,disposition=9;
[ 1456.568625] duanliang,function=input_handle_event,disposition & INPUT_PASS_TO_HANDLERS
[ 1456.576505] ===============================
[ 1456.576505] duanliang,function=input_handle_event,disposition & INPUT_FLUSH==true,dev->num_vals=2
[ 1456.589485] duanliang,function=input_pass_values,dev=-608648192,vals=-608975360,count=2;
[ 1456.597535] duanliang,function=input_to_handler,input_to_handler begin
[ 1456.604032] duanliang,function=input_to_handler,input_to_handler begin
[ 1456.610560] duanliang,function=input_to_handler,input_to_handler begin
[ 1456.617061] duanliang,evdev_events begin
[ 1456.621042] duanliang,evdev_read begin
[ 1456.621120] duanliang,evdev_read begin
[ 1456.621123] duanliang,evdev_read,in while
[ 1456.621130] duanliang,evdev_read,in while


[ 1456.638306] duanliang,function=input_event,type=1,code=106,value=0;
[ 1456.639698] duanliang,evdev_read,in while
[ 1456.640480] duanliang,evdev_read begin
[ 1456.640484] duanliang,evdev_read,in while
[ 1456.656696] duanliang,function=input_event,is_event_supported==true
[ 1456.663158] duanliang,function=input_get_disposition,EV_KEY
[ 1456.668724] *************************************
[ 1456.668724] duanliang,function=input_get_disposition,!!test_bit(code, dev->key) != !!value
[ 1456.681622] duanliang,function=input_handle_event,type=1,code=106,value=0,disposition=1;
[ 1456.689672] duanliang,function=input_handle_event,disposition & INPUT_PASS_TO_HANDLERS

[ 1456.697733] duanliang,function=input_event,type=0,code=0,value=0;
[ 1456.703930] duanliang,function=input_event,is_event_supported==true
[ 1456.710300] duanliang,function=input_get_disposition,dev->event
[ 1456.716210] duanliang,function=input_handle_event,type=0,code=0,value=0,disposition=9;
[ 1456.724094] duanliang,function=input_handle_event,disposition & INPUT_PASS_TO_HANDLERS
[ 1456.731976] ===============================
[ 1456.731976] duanliang,function=input_handle_event,disposition & INPUT_FLUSH==true,dev->num_vals=2
[ 1456.744963] duanliang,function=input_pass_values,dev=-608648192,vals=-608975360,count=2;
[ 1456.753021] duanliang,function=input_to_handler,input_to_handler begin
[ 1456.759518] duanliang,function=input_to_handler,input_to_handler begin
[ 1456.766044] duanliang,function=input_to_handler,input_to_handler begin
[ 1456.772547] duanliang,evdev_events begin
[ 1456.776546] duanliang,evdev_read begin
[ 1456.776562] duanliang,evdev_read begin
[ 1456.776565] duanliang,evdev_read,in while
[ 1456.776578] duanliang,evdev_read begin
[ 1456.776580] duanliang,evdev_read,in while
[ 1456.796522] duanliang,evdev_read,in while
[ 1456.800611] duanliang,evdev_read,in while

拦截代码中有两个关键代码

A:(!!test_bit(code, dev->key) != !!value)用于检测上次按键的状态是否为value,状态就存储在dev->key这个bitmap中

B:__change_bit(code, dev->key);用于反转dev->key这个bitmap中用于表示按键状态的标志位

先分析log可以知道同样的type,code,value,面板按键的第一个DOWN可以通过(!!test_bit(code, dev->key) != !!value),后续的DOWN都不可以,最后的UP可以:

面板按键:

1.第一个DOWN来的时候,value是1,此时标示为0,A成立,用B将标示改为1,

2.后续所有DOWN来的时候,A均不成立,不做处理,直到UP的到来

3.UP到来的时候,value为0,此时A成立,用B又将标示改为0,完成一次按键。

这样子,中间所有的DOWN都过滤掉了。

而红外按键,DOWN和UP都可以通过A的条件判断。

导致最后:

面板按键:

DOWN(value=1)--不拦截

DOWN(value=1)--拦截

DOWN(value=1)--拦截

DOWN(value=1)--拦截

DOWN(value=1)--拦截

UP(value=0)--不拦截

红外按键:

DOWN(value=1))--不拦截

DOWN(value=1))--不拦截

UP(value=0))--不拦截

DOWN(value=1))--不拦截

UP(value=0))--不拦截

DOWN(value=1))--不拦截

UP(value=0))--不拦截

DOWN(value=1))--不拦截

UP(value=0))--不拦截

DOWN(value=1))--不拦截

UP(value=0))--不拦截

所以要想面板按键达到和红外按键一样的效果,可以取消拦截代码,使得面板按键的拦截状况变成

DOWN(value=1)--不拦截

DOWN(value=1)--不拦截

DOWN(value=1)--不拦截

DOWN(value=1)--不拦截

DOWN(value=1)--不拦截

UP(value=0)--不拦截

这样子,上层Android系统就可以在一次面板长按过程中收到多个DOWN事件,而不是只有一个DOWN事件了。

 

tip:不过本身对于按键长按就有不同的处理方式:

1.长按时设备驱动只在按下和抬起的两个时间点向input_event注册一个DOWN,一个UP,上层Android系统根据DOWN,UP之间的时间间隔来判断是否长按,即利用Android自带的长按补发机制来实现长按效果。

2长按时设备驱动只要处于按下状态就向input_event不停注册DOWN,在抬起时注册一个UP,上层Android系统看来这一个长按虽然是连续的短按,但也可以达到长按效果。

我这里面板按键用的就是方式1,而红外遥控器用的是方式2,取消拦截,面板中间一系列的DOWN就不被拦截了

 

而四提到的更好的方式,其实是将value改为2 来表示这个按键不需要拦截,来代替屏蔽代码以取消拦截,这样input驱动就不需要做修改,所以更为合理。

(这个从拦截代码中也能看出来,为2的话,后面的A,B两段代码就不起作用了)

你可能感兴趣的:(Android底层驱动)