概述
还是以gpio-keys.c为例分析
static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
{
const struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
int state;
state = gpiod_get_value_cansleep(bdata->gpiod);
if (state < 0) {
dev_err(input->dev.parent,
"failed to get gpio state: %dn", state);
return;
}
if (type == EV_ABS) {
if (state)
input_event(input, type, button->code, button->value);
} else {
input_event(input, type, *bdata->code, state);
}
input_sync(input);
}
上面的代码中只关注input_event和input_sync
先看input_event()
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);
}
}
说明一下这4个参数
dev:产生事件的设备
type: 事件的类型
code:事件的编码码
value:事件的值
首先会判断当前设备是否支持要上报的事件,然后通过input_handle_event进行事件上报
input_event() -> input_handle_event()
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value);
...
}
先看这个部署相关的
input_event() -> 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_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;
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:
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))
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;
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;
}
*pval = value;
return disposition;
}
从上面代码中可以知道有多种部署
#define INPUT_IGNORE_EVENT 0
#define INPUT_PASS_TO_HANDLERS 1
#define INPUT_PASS_TO_DEVICE 2
#define INPUT_SLOT 4
#define INPUT_FLUSH 8
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
对于INPUT_PASS_TO_HANDLERS INPUT_PASS_TO_DEVICE 可以知道是讲事件交给handler 或者device处理
加个问题 INPUT_PASS_TO_DEVICE,交个device处理做什么?什么情况下会遇到?--请看 uinput模块分析 -- 4 input设备的event方法分析
但是对于INPUT_SLOT这个点含义是在多点触摸的时候,上报数据的一个slot协议,这些不深入分析
对于INPUT_FLUSH 含义可以认为是刷新数据进行上报。
这里分析其中的EV_KEY
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;
首先有个value是否为2的判断,为什么会有这个判断?这里和设置了自动上报有关系,后面会单独分析自动上报相关代码
code:事件的编码码
value:事件的值
假如是按键按下,第一次上报,首先要判断key中有没有记录当前的事件编码,既然是首次,肯定是没有的,那么!!test_bit(code,dev->key) 就是0
对于value,0表示非活动状态,1表示活动状态,也可认为0按键抬起,1按键按下,那么!!value 就是1
所以首次2者不相等,if满足,满足状态下,会更新key中的记录,同时更新部署。
当按键松开的时候,value值变成0,这个if语句还是满足的
这里做个假设,就是一直上报某个按键按下,那么这里的if就不满足了,就会导致事件不会被记录,那么这种情况下 怎么办?
就是上面的if(value == 2)起作用了,当然前提是支持EV_REP,后面会单独分析自动上报相关代码
回到 input_event() -> input_handle_event()
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
int disposition = input_get_disposition(dev, type, code, &value);
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
add_input_randomness(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;
/*
* Reset the timestamp on flush so we won't end up
* with a stale one. Note we only need to reset the
* monolithic one as we use its presence when deciding
* whether to generate a synthetic timestamp.
*/
dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 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;
}
}
之前分析设备注册的时候,分析过hint_events_per_packet这个变量,表示每个包中支持的事件的个数
dev->vals中记录的就是当时申请的空间地址,用于记录上报的事件数据。
对于INPUT_FLASH,这个是input_sync这个函数中会执行的,当前的input_event不会执行到这个if语句,通过上面我们可以看到,在input_sync之前数据都是保存在dev->vals中的
max_vals = hint_events_per_packet + 2
这里有个 if (dev->num_vals >= dev->max_vals - 2) 的判断,为什么要加判断?
因为这里已经到达了当前包中支持的事件的上限值,添加一个sync事件,直接进行数据上报,input_pass_value后面分析
假设hint_events_per_packet 值为7,那么max_vals 值就是9
那么当num_vals值为7的时候,就满足上面的if语句了,最后num_vals值变成了8,数据就上报了,但是还没有达到空间的上限,也就是说还可以加一个事件。
有没有被填满的时候?
有的,就是INPUT_SLOT,可以看到当disposition有SLOT部署的时候,num_vals是加了2次的,所以这个时候,就有可能被填满。
佩服写input子系统的人或者提供patch的人,考虑的很周到。
看完input_event接着分析input_sync
input_sync()
static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0);
}
可以看到input_sync最后调用到还是input_event
input_sync() -> input_event() -> input_handle_event()
static void input_handle_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
...
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;
/*
* Reset the timestamp on flush so we won't end up
* with a stale one. Note we only need to reset the
* monolithic one as we use its presence when deciding
* whether to generate a synthetic timestamp.
*/
dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 0);
} else if (dev->num_vals >= dev->max_vals - 2) {
...
}
}
对于input_sync来说, 也是有INPUT_PASS_TO_HANDLERS部署的,所以dev->vals中会添加sync 事件的
这里主要分析INPUT_FLUSH部署
num_vals >= 2,就是一个key event + sync event,这是最低的上报要求。
input_sync() -> input_event() -> input_handle_event() -> input_pass_values()
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) {
count = input_to_handler(handle, vals, count);
} else {
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open) {
count = input_to_handler(handle, vals, count);
if (!count)
break;
}
}
...
}
这里的grab的意思是设备被抓或者设备被独占的意思,通过EVIOCGRAB ioctl设置,设置后当前设备变成唯一的来自设备的所有输入事件的接收者。
我们之前说过,当多个应用打开这个eventx的时候,都会接收到事件,但是当某一个应用通过EVIOCGRAB ioctl设置后,就只有当前应用可以接收到事件了
主意这里的handle可能会有多个,因为有可能有多个handler
我们分析的时候先忽略grab和多个handle,暂时认为只有一个handle
input_sync() -> input_event() -> input_handle_event() -> input_pass_values() -> input_to_handler()
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;
if (handler->filter) {
for (v = vals; v != vals + count; v++) {
if (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 != vals + count; v++)
handler->event(handle, v->type, v->code, v->value);
return count;
}
handler->filter是过滤器,类似event,用于区分普通事件。
这部分代码首先通过所有过滤器传递事件,然后,如果尚未将事件过滤掉,则通过所有打开的句柄传递事件。
evdev这个handler没有filter,所以对于filter不进行分析,这个handler是有events和event的,所以这里分析events
input_sync() -> input_event() -> input_handle_event() -> input_pass_values() -> input_to_handler() -> evdev_events()
static void evdev_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
ktime_t *ev_time = input_get_timestamp(handle->dev);
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
evdev_pass_values(client, vals, count, ev_time);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_values(client, vals, count, ev_time);
rcu_read_unlock();
}
又看到了grab,EVIOCGRAB ioctl中会设置handle和client,这里才是真正的把当前设备变成唯一的来自设备的所有输入事件的接收者。
如果grab没有指定client,会把事件发送给所有的接收者。
无论有无grab,不影响我们分析代码
input_sync() -> input_event() -> input_handle_event() -> input_pass_values() -> input_to_handler() -> evdev_events() -> evdev_pass_value()
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t *ev_time)
{
struct evdev *evdev = client->evdev;
const struct input_value *v;
struct input_event event;
struct timespec64 ts;
bool wakeup = false;
if (client->revoked)
return;
ts = ktime_to_timespec64(ev_time[client->clk_type]);
event.input_event_sec = ts.tv_sec;
event.input_event_usec = ts.tv_nsec / NSEC_PER_USEC;
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
for (v = vals; v != vals + count; v++) {
if (__evdev_is_filtered(client, v->type, v->code))
continue;
if (v->type == EV_SYN && v->code == SYN_REPORT) {
/* drop empty SYN_REPORT */
if (client->packet_head == client->head)
continue;
wakeup = true;
}
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
}
spin_unlock(&client->buffer_lock);
if (wakeup)
wake_up_interruptible(&evdev->wait);
}
对于client->revoked,其值是在 EVIOCREVOKE ioctl中只设置的,现在看起来这个作用就是app不想接收事件了,那么通过这个设置就可以了
代码中首先填充了本次事件产生的时间,sec和usec
__evdev_is_filtered中会对事件类型进行过滤。过滤的类型是通过 EVIOCSMASK ioctl设置的,比如按键 事件我要屏蔽KEY_A这个按键事件,那么通过这个ioctl就可以操作了
对于if (v->type == EV_SYN && v->code == SYN_REPORT)这个判断分析完下面的__pass_event才回来分析
另外这里可以看到把dev->vals中的数据都拷贝到了event申请的内存空间,这就是之前说过的,为什么evdev还要再去申请空间,而不是直接使用dev->vals
input_sync() -> input_event() -> input_handle_event() -> input_pass_values() -> input_to_handler() -> evdev_events() -> evdev_pass_value() -> __pass_event()
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);
client->buffer[client->tail] = (struct input_event) {
.input_event_sec = event->input_event_sec,
.input_event_usec = event->input_event_usec,
.type = EV_SYN,
.code = SYN_DROPPED,
.value = 0,
};
client->packet_head = client->tail;
}
if (event->type == EV_SYN && event->code == SYN_REPORT) {
client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
}
client->head == client->tail 说明缓冲区已经满了,在这种情况下,将tail的值更新为client->head - 2,相当于只保留1个最新的事件,同时将tail中编码值设置为SYN_DROPPED
client->packet_head下一个数据包的第一个元素的未来位置,当等于tail的时候说明缓冲区中已经没有事件
如果这个最新的事件是同步事件,就将client->head值给packet_head,这个时候tail到packet_head这部分的数据都是事件,现在看起来这部分事件中主要有2个事件
1,EV_SYN code = SYN_DROPPED
2,EV_SYN code = SYN_REPORT
以上是忽略了部分if语句的分析,现在综合起来看这个for循环
for (v = vals; v != vals + count; v++) {
if (__evdev_is_filtered(client, v->type, v->code))
continue;
if (v->type == EV_SYN && v->code == SYN_REPORT) {
/* drop empty SYN_REPORT */
if (client->packet_head == client->head)
continue;
wakeup = true;
}
event.type = v->type;
event.code = v->code;
event.value = v->value;
__pass_event(client, &event);
}
假设3个event,其中2个EV_KEY,1个EV_SYN,且bufsize = 8 , tail =2 head = 1
处理第一个EV_KEY
__pass_event中出现了,client->head == client->tail 缓冲区已经满了
那么这个时候buffer[1] 中保存第一个EV_KEY
buffer[0]中则保存了EV_SYN,code为SYN_DROPPED
继续写入第二个EV_KEY ,保存到buffer[2],最后一个EV_SYN则保存在buffer[3]中,
此时packet_head == client->head = 4,说明有 packet_head - tail = 4个事件
1. EV_SYN(SYN_DROPPED)
2. EV_KEY
3. EV_KEY
4. EV_SYN(SYN_REPORT)
没有丢失事件
假设8个event,其中7个EV_KEY,1个EV_SYN,且bufsize = 8 , tail =2 head = 1
放第一个EV_KEY,缓冲区满了,tail变成0
buffer[1~7] 存放了7个EV_KEY,在放第7个的时候又出现了缓冲区满了,这个时候tail的位置变成了6
buffer[0]存放EV_KEY,此时packet_head == client->head = 1,说明有 packet_head - tail = 3个事件
分别为
1. EV_SYN(SYN_DROPPED)
2. EV_KEY
3. EV_SYN(SYN_REPORT)
丢了6个事件
假设7个event,其中6个EV_KEY,1个EV_SYN,且bufsize = 8 , tail =2 head = 1
那么就只有2个事件
1. EV_SYN(SYN_DROPPED)
2. EV_SYN(SYN_REPORT)
丢了6个事件
ok,这个时候packet_head == client->head
if (v->type == EV_SYN && v->code == SYN_REPORT) {
/* drop empty SYN_REPORT */
if (client->packet_head == client->head)
continue;
wakeup = true;
}
那么什么情况下会执行到continue,导致不上报数据呢?
__evdev_is_filtered导致除EV_SYN之外的事件都被屏蔽了,那么这次就是一个空的上报事件,所以就不需要唤醒应用来读数据,其他的情况暂时想不到了
所以说有数据及时读,皆大欢喜。如果出现了写入比读出快,程序员就要头疼了。。。。
以上就是数据上报的代码分析
这里贴一下之前分析的读数据的代码
static ssize_t evdev_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
...
while (read + input_event_size() <= count &&
evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + read, &event))
return -EFAULT;
read += input_event_size();
}
if (read)
break;
if (!(file->f_flags & O_NONBLOCK)) {
error = wait_event_interruptible(evdev->wait,
client->packet_head != client->tail ||
!evdev->exist || client->revoked);
if (error)
return error;
}
}
return read;
}
可以看到阻塞读的时候,会有休眠操作,什么时候被唤醒呢?就是上面的evdev_pass_value中唤醒的
static void evdev_pass_values(struct evdev_client *client,
const struct input_value *vals, unsigned int count,
ktime_t *ev_time)
{
...
if (wakeup)
wake_up_interruptible(&evdev->wait);
}
最后
以上就是无奈草丛为你收集整理的linux input子系统 -- 05 数据上报的全部内容,希望文章能够帮你解决linux input子系统 -- 05 数据上报所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复