内核驱动上报EV_LED事件失败原因分析

之前在写驱动时使用如下方式上报一次EV_LED类型事件,使用getevent命令测试却一直接收不到事件。

input_event(input_dev, EV_LED, LED_MISC, 1);
input_sync(input_dev);

因为赶工无法深究原因,只能先参考普通按键上报方式(在上报抬起、按下两个事件后再上报同步事件)试着修改,结果上层收到了事件,测试通过。

input_event(input_dev, EV_LED, LED_MISC, 0);
input_event(input_dev, EV_LED, LED_MISC, 1);
input_sync(input_dev);

事后我猜想event事件是被过滤掉了:
1. 被getevent命令过滤;
2. 被内核input子系统过滤。

对于第一种猜想,我自己采用最简单的阻塞式读取方法,实现了一个C程序去轮询设备节点,却依然无法获取event事件,再结合getevent源码分析,可以排除getevnt命令过滤的可能性;
剩下的可能就是input子系统本身过滤了event事件,于是我从事件上报的入口函数input_event()分析,将事件上报流程梳理了一遍,希望从中找出答案。

代码分析流程:

input_event() -> input_handle_event() -> input_get_disposition()
                   -> input_pass_values()

首先从入口函数input_event()开始分析:

void input_event(struct input_dev *dev,
         unsigned int type, unsigned int code, int value)
{
    unsigned long flags;

    /* 检测设备evbit位图是否支持事件总类型 */
    if (is_event_supported(type, dev->evbit, EV_MAX)) {

        spin_lock_irqsave(&dev->event_lock, flags);
        /* 处理event事件 */
        input_handle_event(dev, type, code, value);
        spin_unlock_irqrestore(&dev->event_lock, flags);
    }
}

input_event()接口参数列表:

参数描述
dev上报事件的设备
type事件总类型
code事件子类型
value事件值

经过设备evbit位图判断后,event事件被传递至input_handle_event()函数进一步处理:

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

    /* 获取event事件的描述符 */
    disposition = input_get_disposition(dev, type, code, value);

    /* 如果描述符包含“INPUT_PASS_TO_DEVICE”,调用设备自定义接口处理event事件 */
    if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
        dev->event(dev, type, code, value);

    if (!dev->vals)
        return;

    /* 如果描述符包含“INPUT_PASS_TO_HANDLERS”,event事件继续走通用上报流程 */
    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;
        }

        /* 缓存event事件 */
        v = &dev->vals[dev->num_vals++];
        v->type = type;
        v->code = code;
        v->value = value;
    }

    /* 如果描述符包含“INPUT_FLUSH”,刷新设备缓冲区,上报之前缓存的event事件 */
    if (disposition & INPUT_FLUSH) {
        if (dev->num_vals >= 2)
            /* 上报event事件 */
            input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    /* 如果缓存的event事件超过上限,也进行上报处理 */  
    } else if (dev->num_vals >= dev->max_vals - 2) {
        dev->vals[dev->num_vals++] = input_value_sync;
        /* 上报event事件 */    
        input_pass_values(dev, dev->vals, dev->num_vals);
        dev->num_vals = 0;
    }

}

描述符宏定义:

描述符宏定义作用
INPUT_IGNORE_EVENT0忽略该事件
INPUT_PASS_TO_HANDLERS1事件由handler处理
INPUT_PASS_TO_DEVICE2事件由设备处理
INPUT_SLOT4多点触摸事件标记
INPUT_FLUSH8刷新设备的事件缓冲区
INPUT_PASS_TO_ALL(INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)事件由handler与设备同时处理

在input_handle_event()函数里,描述符disposition决定了event事件的后续处理流程,很有可能是因为描述符的原因,event事件被过滤不做任何处理,接下来我们分析input_get_disposition()函数,看看描述符是怎么生成的:

static int input_get_disposition(struct input_dev *dev,
              unsigned int type, unsigned int code, int value)
{
    /* 描述符默认值为“INPUT_IGNORE_EVENT” */
    int disposition = INPUT_IGNORE_EVENT;

    /* 根据event事件的总类型决定描述符 */
    switch (type) {

    ......

    /* 同步事件 */
    case EV_SYN:
        switch (code) {
        case SYN_CONFIG:
            disposition = INPUT_PASS_TO_ALL;
            break;
        case SYN_REPORT:
            disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
            break;
        case SYN_MT_REPORT:
            disposition = INPUT_PASS_TO_HANDLERS;
            break;
        }
        break;

    /* EV_LED事件 */
    case EV_LED:
        if (is_event_supported(code, dev->ledbit, LED_MAX) &&
            !!test_bit(code, dev->led) != !!value) {
            /*  对led位图中LED_MISC事件的位值进行反置 */
            __change_bit(code, dev->led);
            disposition = INPUT_PASS_TO_ALL;
        }
        break;

    /* 其他事件 */  
    ......

    }

    return disposition;
}

在处理EV_LED事件时,可以看到必须满足两个条件才能获得描述符“INPUT_PASS_TO
_ALL”:

  • is_event_supported(code, dev->ledbit, LED_MAX)
    检测设备ledbit位图是否支持事件子类型(LED_MISC);
  • !!test_bit(code, dev->led) != !!value
    (小技巧:运算符“!!”将任意数值变成布尔值“true”或“false”)
    读取设备led位图LED_MISC位对应的位值,并与“value”的布尔值比较。

第一个条件的结果毫无疑问为“true”,我们重点分析第二个条件:
!!test_bit(code, dev->led)读取led位图位置,__change_bit(code, dev->led)反置led位图位置,这两个相呼应的操作要求:在一次上报事件的周期内,EV_LED事件的“value”布尔值必须经过“true”和“false”的变化才能获取到描述符“INPUT_PASS_TO_ALL”,否则事件将被过滤不做任何处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值