输入事件的传递过程
当一个事件被触发设,备将向上层报告发生了什么事。为表述这个事件的传递过程我们
以触摸屏为例。触摸屏的源程序在前面博文中已有详述,这里就不赘言了。
当在触摸屏上按下时会发出这样的报告:
input_report_abs(dev, ABS_X, xp); //报告x坐标值
input_report_abs(dev, ABS_Y, yp); //报告y坐标值
input_report_key(dev, BTN_TOUCH, 1); //报告触摸屏被按下
input_report_abs(dev, ABS_PRESSURE, 1); //报告触摸屏被按下
input_sync(dev); //报告结束。
/**********************************************************************************************************************/
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
{
input_event(dev, EV_ABS, code, value);
}
/************************************************************************************************************************/
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);
}
}
/************************************************************************************************************************/
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_SYN: //同步事件的处理,一次同步事件的发送表明一次报告的结束。
switch (code) {
case SYN_CONFIG:
disposition = INPUT_PASS_TO_ALL;
break;
case SYN_REPORT: //常用的是该类同步事件报告函数input_sync(dev);
if (!dev->sync) {
dev->sync = 1;
disposition = INPUT_PASS_TO_HANDLERS; //对这一按键事件交给handler处理。
}
break;
case SYN_MT_REPORT:
dev->sync = 0;
disposition = INPUT_PASS_TO_HANDLERS; //对这一按键事件交给handler处理。
break;
}
break;
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) //如果按键被按下
//如果支持重复按键,就开启定时器dev->timer进行重复按键检测,直到按键抬起。
input_start_autorepeat(dev, code);
else
input_stop_autorepeat(dev); //如果按键抬起就停止定时器
}
disposition = INPUT_PASS_TO_HANDLERS; //对这一按键事件交给handler处理。
}
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; //对这一按键事件交给handler处理。
}
break;
case EV_ABS: //绝对坐标事件处理
if (is_event_supported(code, dev->absbit, ABS_MAX)) {
//绝对坐标事件类型有很多子事件,这些子事件分为两类,一类是包含在数组 input_abs_bypass中
//另一类当然就是数组 input_abs_bypass之外了。包含在数组input_abs_bypass中的子事件直接
//break;交给handler处理。数组 input_abs_bypass之外的子事件要经过滤除干扰等处理后再交给handler处理
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: //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; //设定与重复按键检测相关的时间。REP_DELAY和REP_PERIOD。
disposition = INPUT_PASS_TO_ALL; //这个事件既要发向input_dev又要发向handler。
}
break;
case EV_FF: //受力事件处理
if (value >= 0)
disposition = INPUT_PASS_TO_ALL; //这个事件既要发向input_dev又要发向handler。
break;
case EV_PWR: //电源相关的事件
disposition = INPUT_PASS_TO_ALL; //这个事件既要发向input_dev又要发向handler。
break;
}
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
dev->sync = 0; //如果该事件不应被忽略也不是同步事件就清零dev->sync,这一举动与上面同步时间有关。
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); //如果该事件要传递给handler就调用该函数。
}
/************************************************************************************************************************/
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);
//如果input_dev得dev->grab指向了一个当前使用的handle ,就将该事件交给这个handle 对应的handler处理,
//否则就将该事件交给挂在dev->h_list上的所有handle 对应的handler处理。
if (handle)
handle->handler->event(handle, type, code, value);
else
list_for_each_entry_rcu(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle,type, code, value);
rcu_read_unlock();
}
假如此时的input_dev匹配的handler是evdev_handler,那么函数
handle->handler->event(handle, type, code, value);的调用就是调用的函数
evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)。
在讨论这个函数之前先看两个结构体:
(1)
每一个事件都会被包装成这样一个结构体。
struct input_event {
struct timeval time; //事件发生的时间
__u16 type; //事件类型
__u16 code; //子事件
__s32 value; //事件发生的相关value
};
(2)
一个结构体input_event 就代表了一个事件,结构体evdev_client则是对这些事件进行存储管理。
struct evdev_client {
struct input_event buffer[EVDEV_BUFFER_SIZE]; //可以同时管理EVDEV_BUFFER_SIZE(64)个事件
int head; //取出事件从head开始
int tail; //存储事件从tail开始。
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
struct fasync_struct *fasync; //异步通知事件发生
struct evdev *evdev; //指向本evdev_client归属的evdev。
struct list_head node; //用于挂接到evdev的链表头client_list上。
};
上面调用的函数 handle->handler->event(handle, type, code, value);既是下面函数
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;
//将事件包装成结构体input_event。
do_gettimeofday(&event.time);
event.type = type;
event.code = code;
event.value = value;
rcu_read_lock();
client = rcu_dereference(evdev->grab);
//一个input_dev 对应了一个evdev结构体。
//如果evdev的dev->grab指向了一个当前使用的client ,就将该事件插入到该client得buffer中,
//否则就将该事件插入到evdev->client_list上的所有client 的buffer中。
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();
wake_up_interruptible(&evdev->wait);
}
/**************************************************************************************************************/
static void evdev_pass_event(struct evdev_client *client, struct input_event *event)
{
spin_lock(&client->buffer_lock);
client->buffer[client->head++] = *event;
client->head &= EVDEV_BUFFER_SIZE - 1;
spin_unlock(&client->buffer_lock);
kill_fasync(&client->fasync, SIGIO, POLL_IN); //通知相关进程。
}
/**************************************************************************************************************/
让我们来看一看将事件插入了client->buffer[]后有将做何处理。
我们还是讨论输入设备匹配的 input_handler 是evdev_handler
static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};
该设备对应的文件处理函数集evdev_fops在文件evdev.c中实现。
static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};
先来看看文件打开函数:
static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev *evdev;
struct evdev_client *client;
int i = iminor(inode) - EVDEV_MINOR_BASE;
。。。。。。
evdev = evdev_table[i]; //根据次设备号取出数组evdev_table[]中对应的evdev 。
if (evdev)
get_device(&evdev->dev); //增加引用计数
mutex_unlock(&evdev_table_mutex);
。。。。。。
client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); //创建事件管理结构体evdev_client
。。。。。。
client->evdev = evdev; //指向client所归属的evdev。
//以下函数主要完成的工作是list_add_tail_rcu(&client->node, &evdev->client_list);
evdev_attach_client(evdev, client);
//下面打开函数主要是evdev->open++,等等。
error = evdev_open_device(evdev);
if (error)
goto err_free_client;
file->private_data = client; //将file->private_data 指向刚创建的client。
。。。。。。
}
client是一个事件管理结构体,当一个事件发生时就会将该事件结构体插入client->buffer[]中。
而事件的读取是通过函数 evdev_read()来完成的。
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
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;
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(client, &event)是取出事件存于event中
while (retval + input_event_size() <= count && evdev_fetch_next_event(client, &event)) {
if (input_event_to_user(buffer + retval, &event)) //将取出的事件拷到用户空间。
return -EFAULT;
retval += input_event_size();
}
return retval;
}
取出事件:
static int evdev_fetch_next_event(struct evdev_client *client,
struct input_event *event)
{
int have_event;
spin_lock_irq(&client->buffer_lock);
have_event = client->head != client->tail;
if (have_event) {
*event = client->buffer[client->tail++]; //从client->buffer[]的client->tail处取事件
client->tail &= EVDEV_BUFFER_SIZE - 1;
}
spin_unlock_irq(&client->buffer_lock);
return have_event;
}
将事件拷到用户空间:
int input_event_to_user(char __user *buffer, const struct input_event *event)
{
//如果设置了标识INPUT_COMPAT_TEST就将事件event包装成结构体compat_event
if (INPUT_COMPAT_TEST) {
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;
//将包装成input_event_compat的事件拷到用户空间
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;
}
这就是一个事件的产生到传递到用户空间的过程。当然传递到用户空间还可以用函数
evdev_ioctl()来完成。还有从用户空间将一个事件传递到输入设备,大体过程也都相仿
不过是调用函数evdev_write()或是evdev_ioctl()来实现罢了。这里就不详述了。