2010-08-13 19:18:55 阅读219 评论0 字号:大中小 订阅
当一个事件被触发设,备将向上层报告发生了什么事。为表述这个事件的传递过程我们
以触摸屏为例。触摸屏的源程序在前面博文中已有详述,这里就不赘言了。
当在触摸屏上按下时会发出这样的报告:
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; 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()来实现罢了。这里就不详述了。