Linux输入子系统分析(三)

Linux输入子系统事件报告传递机制

        上一节我们分析了输入设备如何与handler连接的过程,当两者连接上后,剩下的工作就是如何报告事件即事件报告如何传递, 输入子系统设备报告各种事件通过 input_report_XXX族函数。
       常用的输入报告函数有按键相对坐标绝对坐标同步事件报告的函数,下面是他们各自的源码:include/linux/input.h中

static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value);
            //将按键的value转化为布尔类型的值。所以按键传给input corevalue0(释放)或者1(按下)
}

static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_REL, code, value);
}

static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}

static inline void input_report_ff_status(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_FF_STATUS, code, value);
}

static inline void input_report_switch(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_SW, code, !!value); 
}

static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
上面几个函数都是调用了input_event()函数,下面就让我们看看input_event的实现
/**
 * input_event() - report new input event
 * @dev: device that generated the event
 * @type: type of the event
 * @code: event code
 * @value: value of the event
 *
 * This function should be used by drivers implementing various input
 * devices to report input events. See also input_inject_event().
 *
 * NOTE: input_event() may be safely used right after input device was
 * allocated with input_allocate_device(), even before it is registered
 * with input_register_device(), but the event will not reach any of the
 * input handlers. Such early invocation of input_event() may be used
 * to 'seed' initial state of a switch or initial position of absolute
 * axis, etc.
 */
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);
add_input_randomness(type, code, value);//由于输入事件具有随机性,因此用输入事件来增加内核熵池的熵
input_handle_event(dev, type, code, value);//调用事件分发函数input_handle_event,做进一步的传递
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
下面看看事件分发函数的实现input_handle_event

#define INPUT_IGNORE_EVENT 0
#define INPUT_PASS_TO_HANDLERS 1
#define INPUT_PASS_TO_DEVICE 2
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)

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

switch (type) {
        
        ......................................
case EV_KEY:
if (is_event_supported(code, dev->keybit, KEY_MAX) && //检查按键是否为驱动所支持,只有之前注册过的按键才会继续传递
    !!test_bit(code, dev->key) != value) { //检查报告的按键状态是否和上次相同。如果连续多次报告按键按下,则只处理第一次


if (value != 2) { //如果不是连击事件

__change_bit(code, dev->key); //翻转按键的当前状态(按下和释放)
if (value)   //如果是按下,则开始连击计时
               input_start_autorepeat(dev, code);
else 
               input_stop_autorepeat(dev);

}

disposition = INPUT_PASS_TO_HANDLERS;//标记消息传递方向
}
break;

case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX))
disposition = input_handle_abs_event(dev, code, &value);

break;

case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;

break;

............................
}

if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = false;

if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);

if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);

}
可以看到input_handle_event分发事件有两个方向:驱动的回调函数dev->eventinput coreinput_pass_event
接着看看input_pass_event实现,
/*
 * Pass event first through all filters and then, if event has not been
 * filtered out, through all open handles. This function is called with
 * dev->event_lock held and interrupts disabled.
 */
static void input_pass_event(struct input_dev *dev,
    unsigned int type, unsigned int code, int value)
{
struct input_handler *handler;
struct input_handle *handle;

rcu_read_lock();

handle = rcu_dereference(dev->grab); //获取独占设备的handle的指针。如果有独占设备的handle,则仅仅将事件传给独占的handle对应的handler
if (handle)
handle->handler->event(handle, type, code, value);//这里直接调用了handler事件驱动对应的XX_event函数,这个XX_event函数把事件数据包传递给了handler,当应用程序使用XX_read时就可以读取到这些数据包。
else {
bool filtered = false;

list_for_each_entry_rcu(handle, &dev->h_list, d_node) { //遍历与此设备连接的每一个handle
if (!handle->open) //如果hnadle已经被打开
continue;

handler = handle->handler;
if (!handler->filter) {
if (filtered)
break;

handler->event(handle, type, code, value); //将事件分发给handler的事件处理函数

} else if (handler->filter(handle, type, code, value))
filtered = true;
}
}
rcu_read_unlock();
}
到这里,input core分发事件的任务已经完成,接下来由各个handler处理接收到的事件,也就是事件处理驱动。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
adc_keys_probe函数是Linux内核中与ADC按键相关的设备树探测函数。在设备树中,如果有ADC按键的相关信息(如所使用的ADC控制器、引脚等),Linux内核会自动调用该函数进行探测。 其主要功能包括: 1. 读取设备树中ADC按键节点的相关信息,如所用ADC控制器和引脚号; 2. 根据以上信息初始化ADC控制器,并将其与对应的GPIO引脚进行绑定; 3. 注册Linux输入系统的按键输入设备,并将其与初始化好的ADC控制器进行关联。 下面是该函数的代码实现: static int adc_keys_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct adc_keys_platform_data *pdata = dev_get_platdata(dev); const char *adc_name = pdata ? pdata->adc_name : NULL; struct input_dev *input_dev; struct adc_keys *keys; int ret, i; if (!adc_name) { dev_err(dev, "no ADC controller specified in platform data\n"); return -EINVAL; } input_dev = devm_input_allocate_device(dev); if (!input_dev) return -ENOMEM; keys = devm_kzalloc(dev, sizeof(*keys), GFP_KERNEL); if (!keys) return -ENOMEM; platform_set_drvdata(pdev, keys); keys->input = input_dev; keys->adc = devm_iio_channel_get(&pdev->dev, "iio"); if (IS_ERR(keys->adc)) { dev_err(dev, "failed to get ADC channel\n"); ret = PTR_ERR(keys->adc); goto err_free_mem; } input_dev->name = pdev->name; input_dev->phys = "keys/input0"; input_dev->id.bustype = BUS_HOST; input_dev->id.vendor = 0x0001; input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; input_set_capability(input_dev, EV_KEY, KEY_POWER); input_set_capability(input_dev, EV_KEY, KEY_VOLUMEUP); input_set_capability(input_dev, EV_KEY, KEY_VOLUMEDOWN); keys->min_val = pdata ? pdata->min_val : ADC_KEYS_DEFAULT_MAX; keys->max_val = pdata ? pdata->max_val : ADC_KEYS_DEFAULT_MIN; ret = input_register_device(input_dev); if (ret) { dev_err(dev, "failed to register input device\n"); goto err_free_mem; } ret = adc_keys_init_dev(keys, adc_name); if (ret) { dev_err(dev, "failed to init ADC controller\n"); goto err_free_dev; } for (i = 0; i < ARRAY_SIZE(keys->keymap); i++) { ret = input_register_keycode(input_dev, keys->keymap[i].type, keys->keymap[i].code, NULL); if (ret) { dev_err(dev, "failed to register input keycode\n"); goto err_free_dev; } } ret = adc_keys_set_timer_interval(keys); if (ret) dev_warn(dev, "Failed to initialize the polling timer\n"); dev_info(dev, "registered ADC keys input device\n"); return 0; err_free_dev: input_unregister_device(input_dev); err_free_mem: return ret; } 该函数通过dev_get_platdata函数读取设备树节点的平台数据信息,获取相关参数。接下来,分别进行输入系统的相关初始化、iio_channel获取、ADC控制器的初始化、按键注册及关联操作,最终成功时输出相关信息并返回0,失败则进行相应的错误处理操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值