您的位置:首页 > 运维架构 > Linux

Linux input子系统分析 事件传递过程

2013-11-28 21:20 344 查看
三. 事件传递过程(以s3c2410_ts为例)

1. 事件产生

当按下触摸屏时,进入触摸屏按下中断,开始ad转换,ad转换完成进入ad完成中断,在这个终端中将事件发送出去,调用

input_report_abs(dev, ABS_X, xp);

input_report_abs(dev, ABS_Y, yp); 这两个函数调用了 input_event(dev, EV_ABS, code, value)

所有的事件报告函数都调用这个函数。

2. 事件报告

(1) input_event 函数分析,这个函数定义在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);
add_input_randomness(type, code, value);
//对系统随机熵池有贡献,因为这个也是一个随机过程

input_handle_event(dev, type, code, value);
//这个函数是事件处理的关键函数,下面详细分析

spin_unlock_irqrestore(&dev->event_lock, flags);
}
}

(2) input_handle_event 函数分析,这个函数定义在input.c中



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;
......
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0;

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);
}

这个函数主要是根据事件类型的不同,做相应的处理。这里之关心EV_KEY类型,其他函数和事件传递关系不大,只要关心,disposition这个是事件处理的方式,默认的是INPUT_IGNORE_EVENT,忽略这个事件,如果是INPUT_PASS_TO_HANDLERS则是传递给事件处理器,如果是INPUT_PASS_TO_DEVICE,则是传递给设备处理,触摸屏驱动没有定义这个。下面分析input_pass_event函数。



static void input_pass_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;

rcu_read_lock();

handle = rcu_dereference(dev->grab); //如果是绑定的handle,则调用绑定的handler->event函数

if (handle)
handle->handler->event(handle, type, code, value);
else
//如果没有绑定,则遍历dev的h_list链表,寻找handle,如果handle已经打开,说明有进程读取设备关联的evdev。

list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,
type, code, value);
// 调用相关的事件处理器的event函数,进行事件的处理

rcu_read_unlock();
}

下面分析 evdev事件处理器的event函数



static void evdev_event(struct input_handle *handle,
unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;

do_gettimeofday(&event.time);
event.type = type;
event.code = code;
event.value = value;
//将传过来的事件,赋值给input_event结构

rcu_read_lock();

client = rcu_dereference(evdev->grab);
//如果evdev绑定了client那么,处理这个客户端,触摸屏驱动没有绑定

if (client)
evdev_pass_event(client, &event);
else
//遍历client链表,调用evdev_pass_event函数

list_for_each_entry_rcu(client, &evdev->client_list, node)
evdev_pass_event(client, &event);

rcu_read_unlock();

wake_up_interruptible(&evdev->wait); //唤醒等待的进程

}

下面分析 evdev_pass_event 函数



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);
client->buffer[client->head++] = *event; //将事件赋值给客户端的input_event 数组

client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);

kill_fasync(&client->fasync, SIGIO, POLL_IN);
}

可以看出, evdev_pass_event函数最终将事件传递给了用户端的client结构中的input_event数组中,只需将这个input_event数组复制给用户空间,进程就能收到触摸屏按下的信息了。具体处理由具体的应用程序来完成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: