上一篇博客中曾使用input_report_key函数来上报键值和按键状态,现在我们就分析一下它的上报流程。
可以看系统提供了很多来上报各种信息的函数(适用,键盘,鼠标,触摸屏等各种上报信息)
它们都是掉用同一的接口input_enent来实现的,所以input_event才是我们分析的重点。
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_KEY, code, !!value); /* 注意这里value是两次取反,即输入给input_event的只可能是0或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);
}
static inline void input_mt_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_MT_REPORT, 0);
}
看一下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;
/*调用 is_event_supported()函数检查输入设备是否支持该事件*/
if (is_event_supported(type, dev->evbit, EV_MAX)) {
/* 调用 spin_lock_irqsave()函数对将事件锁锁定 */
spin_lock_irqsave(&dev->event_lock, flags);
/* add_input_randomness()函数对事件发送没有一点用处,只是用来对随机数熵池增加一些贡献,因为按键输入是一种随机事件,所以对熵池是有贡献的 */
add_input_randomness(type, code, value);
/* 函数来继续输入子系统的相关模块发送数据。该函数较为复杂,下面单独进行分析 */
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
}
上面用到的两个工具函数
/* 测试addr数组中第nr位是不是为1,是则返回1,不是则返回0 */
static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
{
return ((1UL << (nr % BITS_PER_LONG)) &
(((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
}
static inline int is_event_supported(unsigned int code,
unsigned long *bm, unsigned int max)
{
return code <= max && test_bit(code, bm);
}
按键上报的处理函数。
#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)
{
/* 定义了一个 disposition 变量,该变量表示使用什么样的方式处理事件。此处初始化为 INPUT_IGNORE_EVENT,表示如果后面没有对该变量重新赋值,则忽略这个事件 */
int disposition = INPUT_IGNORE_EVENT;
switch (type) {
case EV_SYN:
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT:
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case SYN_MT_REPORT:
dev->sync = 0;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
/* 函数判断是否支持该按键,同时判断按键状态是否改变过 */
if (is_event_supported(code, dev->keybit, KEY_MAX) &&
!!test_bit(code, dev->key) != value) { /* 判断value是不是改变过,比如某个键长按,则它在按下过程一致是1 */
/* EV_KEY正常是不会有0和1之外的value值的 */
if (value != 2) {
__change_bit(code, dev->key); /* 把key中code位取反 */
if (value)
/* 如果刚按下键,则开始重复上报数据(具体报不报还要看dev的配置) */
input_start_autorepeat(dev, code);
else
/* 松开按键则停止重复上报 */
input_stop_autorepeat(dev);
}
disposition = INPUT_PASS_TO_HANDLERS; /* 更新disposition */
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
!!test_bit(code, dev->sw) != value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
}
break;
case EV_ABS:
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
if (test_bit(code, input_abs_bypass)) {
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
value = input_defuzz_abs_event(value,
dev->abs[code], dev->absfuzz[code]);
if (dev->abs[code] != value) {
dev->abs[code] = value;
disposition = INPUT_PASS_TO_HANDLERS;
}
}
break;
case EV_REL:
if (is_event_supported(code, dev->relbit, REL_MAX) && value)
disposition = INPUT_PASS_TO_HANDLERS;
break;
case EV_MSC:
if (is_event_supported(code, dev->mscbit, MSC_MAX))
disposition = INPUT_PASS_TO_ALL;
break;
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
!!test_bit(code, dev->led) != value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_SND:
if (is_event_supported(code, dev->sndbit, SND_MAX)) {
if (!!test_bit(code, dev->snd) != !!value)
__change_bit(code, dev->snd);
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_REP:
if (code <= REP_MAX && value >= 0 && dev->rep[code] != value) {
dev->rep[code] = value;
disposition = INPUT_PASS_TO_ALL;
}
break;
case EV_FF:
if (value >= 0)
disposition = INPUT_PASS_TO_ALL;
break;
case EV_PWR:
disposition = INPUT_PASS_TO_ALL;
break;
}
/* 处理 EV_SYN 事件,这里并不对其进行关心。*/ */
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0;
/* 一些特殊事件需要对dev也上报(比如led点灯等),通常我们的dev里面不会设置event事件 */
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
/* 真正的向上层handler上报事件 */
if (disposition & INPUT_PASS_TO_HANDLERS)
input_pass_event(dev, type, code, value);
}
上报事件的处理(这里主要是区分是固定给某个handler上报,还是查询目前打开着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();
/* grab 是强制为 input device 绑定的 handler,如果存在就直接调用绑定的handler里面的event即可 */
handle = rcu_dereference(dev->grab);
if (handle)
handle->handler->event(handle, type, code, value);
else {
bool filtered = false;
/* 如果没有为device绑定具体的handle,则遍历这个dev上的所有handle,同时会向应用层已经 open打开过的发送具体的事件信息,
handle里面的open是做打开次数统计的,非0即有应用程序打开。这里要注意的是向所有的打开的都发送事件 */
list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
if (!handle->open)
continue;
handler = handle->handler;
if (!handler->filter) { /* 看handler要不要进行再次筛选,通常evdev和mousedev等都是不需要进行筛选了 */
if (filtered)
break;
/* 真正的调用handler里面的event向应用程序上报数据 */
handler->event(handle, type, code, value);
} else if (handler->filter(handle, type, code, value))
filtered = true;
}
}
rcu_read_unlock();
}
具体的handler(以evdev为例)层的上报我在input子系统学习四已经分析了。这里我直接留拷贝过来了。完整的过程还是要这几篇都看的。
/*
* Pass incoming event to all connected clients.
*/
static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private; /* evdev本身绑定在handle里面的private */
struct evdev_client *client;
struct input_event event;
struct timespec ts;
/* 输入信息打包成标准格式 */
ktime_get_ts(&ts);
event.time.tv_sec = ts.tv_sec;
event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
/* 如果该evdev有个专用的client,那么就将事件发给它如果该evdev不存在专用的client,那个就把该事件发送给evdev上client_list链表上所有的client */
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_event(client, &event);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event); /* 打包数据 */
rcu_read_unlock();
/* 唤醒等到队列上的进程,注意这里是唤醒,肯定有别的地方定义(connect)和让它睡眠(read),注意我会标记的 */
wake_up_interruptible(&evdev->wait);
}
使用异步通知通知上层。
static void evdev_pass_event(struct evdev_client *client,
struct input_event *event)
{
/*
* Interrupts are disabled, just acquire the lock
*/
spin_lock(&client->buffer_lock);
wake_lock_timeout(&client->wake_lock, 5 * HZ); /* 设置一个5s的超时锁,5s后自动解锁,超时后内核可以进入休眠模式(即5s没输入则进入休眠) */
client->buffer[client->head++] = *event; /* 把键值写入client缓冲队列中,方便app层读(app层可能一次读多个输入信息) */
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
if (event->type == EV_SYN)
kill_fasync(&client->fasync, SIGIO, POLL_IN); /* 发送一个异步通知,通知该打开该client的应用程序,执行信号处理函数*/
上面就是所有的从按键发生到按键事件发生给应用程序的整个流程。