昨天在做有关Linux input子系统实验的时候,被一个问题困扰了很久,到第二天才发现原因,最后的问题是一个小细节导致实验的失败。
当时的实验代码如下:
static int key_probe(struct platform_device *pdev) // 按键初始化函数
{
int ret = 0;
/*...................前面省略初始化GPIO口的代码....................................... */
// 申请input_dev 结构体内存
keycdev.keyinputdev = devm_input_allocate_device(&pdev->dev);
if(NULL == keycdev.keyinputdev){
DEBUG_SFLR("devm_input_allocate_device
keycdev.keyinputdev->name = "key_input_dev";
//keycdev.keyinputdev->dev.parent = &pdev->dev;
__set_bit(KEY_DOWN ,keycdev.keyinputdev->keybit); // 设置按键键值为KEY_DOWN
__set_bit(EV_KEY, keycdev.keyinputdev->evbit); // 设置按键类型为EV_KEY
// 注册inputdevice
if(input_register_device(keycdev.keyinputdev)){
DEBUG_SFLR("input_register_device error\r\n" );
ret = -1;
goto end;
}
keycdev.key_timer.function = key_timer_func;
keycdev.key_timer.data = &keycdev;
init_timer(&keycdev.key_timer); // 初始化定时器
end:
return ret;
}
当时实验代码在定时器中断中上报键值
void key_timer_func(unsigned long arg)
{
struct key_dev_type *pdata = (struct key_dev_type *)arg;
int value = 0;
value = gpio_get_value(pdata->gpio);
if(0 == value) // 当按键按下时上报事件
{
input_report_key(pdata->keyinputdev,KEY_0,0); // 上报事件
input_sync(pdata->keyinputdev);
}
}
但是当把驱动加载到内核之后,按下按键内核并没有把键值上报到应用层。反复检查了很多遍都没有找到是什么问题。后来再次认真检查一遍,终于发现了问题所在。原来是在按键初始化函数中向内核注册的按键键值为KEY_DOWN,但是在定时器中断函数中向内核上报按键的键值却是KEY_0,这就导致了内核注册的键值与上报的键值不一致,导致上报失败
至于为什么当内核注册的按键键值与上报的键值不一致会导致上传失败,这需要在内核中找答案
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);
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (!dev->vals)
return;
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;
}
if (disposition & INPUT_FLUSH) {
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;
input_pass_values(dev, dev->vals, dev->num_vals);
dev->num_vals = 0;
}
}
input_handle_event函数负责处理上报的内容,其中函数input_get_disposition负责检测键值的有效性
static int input_get_disposition(struct input_dev *dev,
unsigned int type, unsigned int code, int *pval)
{
int disposition = INPUT_IGNORE_EVENT;
int value = *pval;
switch (type) {
/*****************前后省略其他的事件****************************/
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX)) {
/* auto-repeat bypasses state updates */
if (value == 2) {
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
if (!!test_bit(code, dev->key) != !!value) {
__change_bit(code, dev->key);
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
/********************************************************************/
*pval = value;
return disposition;
}
在input_get_disposition函数中我们选择EV_KEY来分析,在函数被执行时disposition预先被设置为INPUT_IGNORE_EVENT,也就是预先设置为忽略事件。is_event_supported负责判断上报的键值是否与内核注册的键值一致,如果一致就将disposition设置为INPUT_PASS_TO_HANDLERS并往后选择一个input事件处理层来处理事件,如果不相等就直接退出,不进行事件上报
所以这就是为什么当内核注册的按键键值与上报键值不一致时会导致数据没有上报的原因。最后将定时器中断函数中需要上报的键值修改为KEY_DOWN,与注册的键值一致就能上报成功
void key_timer_func(unsigned long arg)
{
struct key_dev_type *pdata = (struct key_dev_type *)arg;
int value = 0;
value = gpio_get_value(pdata->gpio);
if(0 == value) // 当按键按下时上报事件
{
input_report_key(pdata->keyinputdev,KEY_DOWN); // 上报事件
input_sync(pdata->keyinputdev);
}
}