前言:上一篇中,我们掌握一些必要的基础知识,这一篇就来分析当我们点击一个按键时,按键上报的具体流程。
一、按键按下 涉及到的层级
按键按下 会通过以下系统层级:
Driver(设备驱动层)->Inputcore(输入子系统核心层)
->Event handler(事件处理层)->userspace(用户空间)

整个过程涉及到内核层、Framework层以及应用层.
本文主要分析输入事件内核层的流程
二、键值上报具体流程
1.中断注册 kpd_pdrv_probe()
路径:kernel-3.18/drivers/input/keyboard/mediatek/kpd.c
static int kpd_pdrv_probe(struct platform_device *pdev){
//省略代码...
/* initialize and register input device (/dev/input/eventX) */
kpd_input_dev = input_allocate_device();//申请input设备
if (!kpd_input_dev) {
kpd_print("input allocate device fail.\n");
return -ENOMEM;
}
//input设备初始化 填充kpd_input_dev 设备驱动结构体*
kpd_input_dev->name = KPD_NAME;
kpd_input_dev->id.bustype = BUS_HOST;
kpd_input_dev->id.vendor = 0x2454;
kpd_input_dev->id.product = 0x6500;
kpd_input_dev->id.version = 0x0010;
kpd_input_dev->open = kpd_open;
//解析dts中keypad节点的信息,赋值给kpd_dts_data结构体
kpd_get_dts_info(pdev->dev.of_node);
//input设备支持EV_KEY事件
__set_bit(EV_KEY, kpd_input_dev->evbit);
//注册input设备
r = input_register_device(kpd_input_dev);
//设置按键消抖
kpd_set_debounce(kpd_dts_data.kpd_key_debounce);
//申请中断处理
r = request_irq(kp_irqnr, kpd_irq_handler,
IRQF_TRIGGER_NONE, KPD_NAME, NULL);
}
分析:可以看到,在probe()函数中,调用
request_irq();注册了中断处理函数kpd_irq_handle()
2.中断处理函数 kpd_irq_handler()
当我们按键按下了,就会触发中断,进入中断服务子程序,系统会调用相应的中断处理函数kpd_irq_handler()去上报事件。
路径:kernel-3.18/drivers/input/keyboard/mediatek/kpd.c
//中断处理函数
static irqreturn_t kpd_irq_handler(int irq, void *dev_id)
{
/* use _nosync to avoid deadlock */
disable_irq_nosync(kp_irqnr);//禁止中断,无需进行同步,防止死锁
tasklet_schedule(&kpd_keymap_tasklet);//调度tasklet
return IRQ_HANDLED;
}
分析:先disable中断(防止死锁),在调度tasklet。
补充知识:
######中断处理的 tasklet 机制
中断服务程序一般都是在中断请求关闭的条件下执行的,
以避免嵌套而使中断控制复杂化。(具体机制就不是本文重点啦)
######tasklet 使用方法
/*用静态方式声明并定义一个tasklet,动态方式一样*/
DECLARE_TASKLET(my_tasklet,&tasklet_func,0);
/*直接调度我们的函数*/
tasklet_schedule(&my_tasklet);
因此,跟踪DECLARE_TASKLET()方法
/* for keymap handling */
static void kpd_keymap_handler(unsigned long data);
static DECLARE_TASKLET(kpd_keymap_tasklet, kpd_keymap_handler, 0);
分析:可以看到,中断服务程序里面执行tasklet_schedule(&kpd_keymap_tasklet);实际上会去调用这个函数kpd_keymap_handler()
static void kpd_keymap_handler(unsigned long data)
{
int i, j;
bool pressed;
u16 new_state[KPD_NUM_MEMS], change, mask;
u16 hw_keycode, linux_keycode;
//mtk通过5组寄存器来保存按键的状态,这里回读寄存器并保存为new_state
kpd_get_keymap_state(new_state);
//激活锁唤醒系统,500ms后就释放掉
wake_lock_timeout(&kpd_suspend_lock, HZ / 2);
for (i = 0; i < KPD_NUM_MEMS; i++) {
//每组中按键状态未改变则对比下一组,按位处理
change = new_state[i] ^ kpd_keymap_state[i];
if (!change)
continue;
for (j = 0; j < 16; j++) {
//每组(16位)中对比按位查看是否状态发生改变
mask = 1U << j;
if (!(change & mask))
continue;
hw_keycode = (i << 4) + j;
/* bit is 1: not pressed, 0: pressed */
//按键是否按下,寄存器中0表示按键处于按下状态
pressed = !(new_state[i] & mask);
if (kpd_show_hw_keycode)
kpd_print("(%s) HW keycode = %u\n", pressed ?
"pressed" : "released", hw_keycode);
BUG_ON(hw_keycode >= KPD_NUM_KEYS);
linux_keycode = kpd_keymap[hw_keycode];
if (unlikely(linux_keycode == 0)) {
kpd_print("Linux keycode = 0\n");
continue;
}
kpd_aee_handler(linux_keycode, pressed);
//上报键值
input_report_key(kpd_input_dev, linux_keycode, pressed);
//同步用于告诉input core子系统报告结束
input_sync(kpd_input_dev);
kpd_print("report Linux keycode = %u\n", linux_keycode);
}
}
//kpd_keymap_state保存new_state,用于下轮对比
memcpy(kpd_keymap_state, new_state, sizeof(new_state));
kpd_print("save new keymap state\n");
//按键处理完毕,打开中断
enable_irq(kp_irqnr);
}
分析:该函数中,最重要的是
//向INPUT子系统上报键值
input_report_key(kpd_input_dev, linux_keycode, pressed);
//同步用于告诉input core子系统报告结束
input_sync(kpd_input_dev);
也就是上篇文章中抓取到的adb信息,因为按键按下到松开,会产生2次中断,因此会调用2次
按键按下
0001 0072 00000001//input_report_key();
0000 0000 00000000 // input_sync(kpd_input_dev);
按键松开
0001 0072 00000000//input_report_key();
0000 0000 00000000// input_sync(kpd_input_dev);
接下来,就通过input子系统一层层调用,最终调用input_event_to_user()上报到用户空间。
我们来跟踪以下具体流程
路径:kernel-3.18/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);
}
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
可以看到,无论是input_report_key还是input_sync,都会调用input_event继续上报
路径:kernel-3.18/drivers/input/input.c
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);//内核锁
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);//内核锁
}
}
EXPORT_SYMBOL(input_event);
分析,可以看到,代码又继续调用了input_handle_event()进一步上报数值,
路径:kernel-3.18/drivers/input/input.c
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);
//如果事件处理者身份为INPUT_PASS_TO_HANDLERS表示交给input hardler处理
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
if (!dev->vals)
return;
//如果事件处理者身份为INPUT_PASS_TO_DEVICE表示交给input device处理
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;
}
//如果事件处理者身份为INPUT_FLUSH表示需要handler立即处理
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_get_disposition()获得事件处理者身份。
input_handle_event函数向输入子系统传送事件信息,
参数1是输入设备input_dev,参数2是事件类型,
参数3是键码,参数4是键值。
input_get_disposition()确定事件的处理方式,返回值主要有以下几种:
INPUT_IGNORE_EVENT:表示忽略事件,不进行处理。
INPUT_PASS_TO_HANDLERS:表示事件交给handler处理。
INPUT_PASS_TO_DEVICE:表示将事件交给input_dev处理。
INPUT_PASS_TO_ALL:表示将事件交给handler和 input_dev共同处理。
那到底返回的disposition是啥的?那就的继续跟一下
input_get_disposition()函数去看
路径:kernel-3.18/drivers/input/input.c
static int input_get_disposition(struct input_dev *dev,
unsigned int type, unsigned int code, int *pval)
{
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
//传进来的参数是:EV_SYN, SYN_REPORT,因此 代码会走这里
case SYN_REPORT:
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)) {
/* 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;
case EV_SW :
//..........省略..................
分析:从前面的分析,每上报一个事件,就会调用input_sync,告诉Input子系统事件上报完毕,
可以看到,传的参数是EV_SYN, SYN_REPORT,0
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
所以 最后的 disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
从input_handle_event(),可以看出,会接着去调用
input_pass_values()继续上报
路径:kernel-3.18/drivers/input/input.c
static void input_pass_values(struct input_dev *dev,
struct input_value *vals, unsigned int count)
{
struct input_handle *handle;
struct input_value *v;
if (!count)
return;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
if (handle) {//如果是绑定的handle,则调用绑定的handler->event函数
// 调用input_to_handler,进行事件的处理
count = input_to_handler(handle, vals, count);
} else {
//如果没有绑定,则遍历dev的h_list链表,寻找handle,
//如果handle已经打开,说明有进程读取设备关联的evdev。
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
// 调用input_to_handler,进行事件的处理
count = input_to_handler(handle, vals, count);
}
rcu_read_unlock();
//.......................省略...................................
}
分析:input_pass_values()函数用于确定input_dev的handler,并通过input_to_handler()调用handler的event函数。在上述代码中,rcu_dereference(),该接口用来获取RCU protected pointer。reader要访问RCU保护的共享数据,当然要获取RCU protected pointer,然后通过该指针进行dereference的操作。dev->grab是强制为input device的handler,如果rcu_dereference()函数返回值不为空,说明有为input_device强制指定handler,就直接调用handler的event函数。如果为NULL,表示没有为input_device强制指定handler,就会通过遍历input device->h_list上的handle成员。如果该handle被打开,表示该设备已经被一个用户进程使用,就会调用与输入设备对应的handler的event函数。
注:只有在handle被打开的情况下才会接收到事件,这就是说,只有设备被用户程序使用时,才有必要向用户空间导出信息。
接下来会去调用input_to_handler()继续上报
路径:kernel-3.18/drivers/input/input.c
static unsigned int input_to_handler(struct input_handle *handle,
struct input_value *vals, unsigned int count)
{
struct input_handler *handler = handle->handler;
struct input_value *end = vals;
struct input_value *v;
//通过过滤器进行事件过滤
for (v = vals; v != vals + count; v++) {
if (handler->filter &&
handler->filter(handle, v->type, v->code, v->value))
continue;
if (end != v)
*end = *v;
end++;
}
count = end - vals;
if (!count)
return 0;
if (handler->events)
//继续上报数据
handler->events(handle, vals, count);
else if (handler->event)
for (v = vals; v != end; v++)
handler->event(handle, v->type, v->code, v->value);
return count;
}
分析:首先会通过handler->filter去过滤事件,接着调用 handler->event()【handle->handler->event()】或者handler->events继续上报,那这个event()或者events()函数在哪定义呢
那就得看input_handler 结构体
路径:kernel-3.18/drivers/input/evdev.c

路径:kernel-3.18/drivers/input/evdev.c
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct input_value vals[] = { { type, code, value } };
//可以看到 evdev_event调用了evdev_events
evdev_events(handle, vals, 1);
}
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
//......省略代码
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)//如果evdev绑定了client那么,处理这个客户端
evdev_pass_values(client, vals, count, time_mono, time_real);
else
//遍历client链表,调用evdev_pass_values函数
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count,
time_mono, time_real);
rcu_read_unlock();
//...........省略代码.................
}
分析:从代码中可以看到evdev_event()去调用了evdev_events(),
所以最终都走evdev_events()方法,该方法里面又去evdev_pass_values()进一步上报数据
路径:kernel-3.18/drivers/input/evdev.c
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t mono, ktime_t real)
{
//.....................省略........................
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
for (v = vals; v != vals + count; v++) {
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
if (v->type == EV_SYN && v->code == SYN_REPORT)
wakeup = true;
}
spin_unlock(&client->buffer_lock);
if (wakeup)
wake_up_interruptible(&evdev->wait);
}
分析:先调用__pass_event()进一步上报数据,然后调用wake_up_interruptible唤醒中断
路径:kernel-3.18/drivers/input/evdev.c
static void __pass_event(struct evdev_client *client,
const struct input_event *event)
{
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
if (unlikely(client->head == client->tail)) {
/*
* This effectively "drops" all unconsumed events, leaving
* EV_SYN/SYN_DROPPED plus the newest event in the queue.
*/
client->tail = (client->head - 2) & (client->bufsize - 1);
/*将event装入client的buffer中,buffer是一个环形缓存区*/
client->buffer[client->tail].time = event->time;
client->buffer[client->tail].type = EV_SYN;
client->buffer[client->tail].code = SYN_DROPPED;
client->buffer[client->tail].value = 0;
client->packet_head = client->tail;
if (client->use_wake_lock)
wake_unlock(&client->wake_lock);
}
}
分析:到这里,最终将事件传递给了用户端的client结构中buffer中,buffer是一个环形缓存区,等待用户空间来读取
读取大致过程:先调用evdev_open_device打开设备,然后调用evdev_read去读取
路径:kernel-3.18/drivers/input/evdev.c
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
/*这个就是刚才在open函数中*/
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
int retval;
if (count < input_event_size())
return -EINVAL;
/*如果client的环形缓冲区中没有数据并且是非阻塞的,那么返回-EAGAIN,
也就是try again*/
if (client->head == client->tail && evdev->exist &&
(file->f_flags & O_NONBLOCK))
return -EAGAIN;
/*如果没有数据,并且是阻塞的,则在等待队列上等待吧*/
retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);
if (retval)
return retval;
if (!evdev->exist)
return -ENODEV;
/*如果获得了数据则取出来,调用evdev_fetch_next_event*/
while (retval + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
/*input_event_to_user调用copy_to_user传入用户程序中,这样读取完成*/
if (input_event_to_user(buffer + retval, &event))
return -EFAULT;
retval += input_event_size();
}
return retval;
}
路径:kernel-3.18/drivers/input/input-compat.c
int input_event_to_user(char __user *buffer,
const struct input_event *event)
{
if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) {
struct input_event_compat compat_event;
compat_event.time.tv_sec = event->time.tv_sec;
compat_event.time.tv_usec = event->time.tv_usec;
compat_event.type = event->type;
compat_event.code = event->code;
compat_event.value = event->value;
if (copy_to_user(buffer, &compat_event,
sizeof(struct input_event_compat)))
return -EFAULT;
} else {
if (copy_to_user(buffer, event, sizeof(struct input_event)))
return -EFAULT;
}
return 0;
}
分析:该函数中,最终调用input_event_to_use()->copy_to_user()把数据拷贝到用户空间,到此按键上报事件内核流程分析就结束了。
总结

总结一下事件的传递过程:
首先在驱动层中,调用inport_report_key和input_sync,
然后他调用了input core层的input_event,
input_event调用了input_handle_event对事件进行分派,调用input_pass_event,
在这里他会把事件传递给具体的handler层,
然后在相应handler的event处理函数中,封装一个event,
然后把它投入evdev的那个client_list上的client的事件buffer中,
等待用户空间来读取。
最后通过evdev_read->input_event_to_user->copy_to_user把数据拷贝到用户空间
调用函数如下:
input_event()->input_handle_event() ->input_pass_values()
->input_to_handler->handle->handler->event(handle,type, code, value)
->evdev_events() ->evdev_pass_values() ->__pass_event()
把数据存在客户端的buffer中,等待读取
最后evdev_read->input_event_to_user->copy_to_user
拷贝到用户空间
总体来说:
Driver(设备驱动层)->Inputcore(输入子系统核心层)
->Event handler(事件处理层)->userspace(用户空间)
Stay hungry,Stay foolish!
荆轲刺秦王
参考文档:
https://blog.csdn.net/u010142953/article/details/46680529
https://www.linuxidc.com/Linux/2011-09/43187p5.htm
https://blog.csdn.net/wh8_2011/article/details/51678336
https://blog.csdn.net/sdkdlwk/article/details/71108226
https://blog.csdn.net/hb9312z/article/details/78414334
http://www.cnblogs.com/myblesh/articles/2367648.html
https://blog.csdn.net/u013604527/article/details/53432623
http://www.cnblogs.com/ant-man/p/9204977.html
https://blog.csdn.net/u010177751/article/details/38520245
https://blog.csdn.net/liyanfei123456/article/details/53196693
网友评论