如果我们要写一个gpio 的按键驱动,可以有两种写法
- 写个字符设备,利用中断 ,阻塞读写就能实现。
- 注册成input 设备,利用中断向上报告按键。
如果只是测试,第一种写法是可行的,也更加简洁,它的优点就在于高效,不依靠输入子系统。
但是,这种按键实现复杂一点的按键功能就要写很多底层代码了,而且它有个最大的缺点:上层的界面系统很难友好的使用这种驱动,除非把上层界面系统的按键接口做相应的修改。
所以,input 设备,依靠了输入子系统,上层界面系统对它的使用很方便了。
input 按键驱动的工作机制大概如下:
按键产生中断,在中断中向上报告键值:
这里要注意两点:
-
input_event(input, type, button->code, !!state); 这个函数 如果第一次报告了 input_event(input, type, button->code, 1); 第二次又报告了 input_event(input, type, button->code, 1); 那么第二次是报告不上的,也就是说 只有键值变化了报告才有效。这也是按键驱动为什么都是双边延触发,就是为了产生按键按下 和 按键抬起 ,如果每次只报告一次按键按下,那么 驱动只会报告一次按键。
-
如果 设置了 __set_bit(EV_REP, input->evbit); 也就是重复报告,它的工作机制是这样的:
如果按键报告了input_event(input, type, button->code, 1); 之后, 在250ms (可以改)后,依然没有报告 input_event(input, type, button->code, 0); 则 input 会每隔 33ms 继续报告一次 input_event(input, type, button->code, 2); 直到 报告了 input_event(input, type, button->code, 0); 才停止 ,这就是我们按住一个按键不松开时,会一直打印键值的原因;
这段代码在 drivers/input/input.c 中
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don’t do it in input.c.
*/
init_timer(&dev->timer);
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
//dev->rep[REP_DELAY] = 250;
dev->rep[REP_DELAY] = 2500;
dev->rep[REP_PERIOD] = 33;
}
这里要注意红色的说明文字,也就说如果我们自己的驱动里自己定义了 dev->rep[REP_DELAY] = 2500; 那么就不会使用input 的timer ,而要使用自己编写的timer 。
原文链接:https://blog.csdn.net/sxw0624/article/details/7569449