1 input输入子系统整体流程
本节分析input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层。由于上节代码讲解了设备驱动层的写法,因此在开头部分会从设备驱动层做为线索,分析输入子系统和事件处理层是如何配合的,最后从用户角度出发,从“/dev/input/*”接口如何使用输入子系统提供的服务。
既然需要详细分析,有一个这样的流程图能够帮助我们在被绕进代码的过程中,找到出口,你能够知道你现在位于代码框架的什么位置,不会忘记正在分析的代码的“身份”。其实在初识输入子系统中已经贴出了这张图,我们再把它拿出来参考一下吧,见下图6。
图6 linux输入子系统事件处理机制
1.1 设备驱动层注册到input子系统
在上一节分析了S3C2440触摸屏驱动的代码,初始化函数定义了struct input_devinput结构体,它用于描述一个输入子系统设备,任何驱动设备如果想标明自己是输入设备,都应该通过初始化这样的结构体,并且调用input_allocate_device()函数进行注册。
了解这一过程,需要先看一下structinput_dev结构体的内容:
- struct input_dev {
-
- void *private;
-
- const char *name;
- const char *phys;
- const char *uniq;
- struct input_id id;
-
- unsigned long evbit[NBITS(EV_MAX)];
- unsigned long keybit[NBITS(KEY_MAX)];
- unsigned long relbit[NBITS(REL_MAX)];
- unsigned long absbit[NBITS(ABS_MAX)];
- unsigned long mscbit[NBITS(MSC_MAX)];
- unsigned long ledbit[NBITS(LED_MAX)];
- unsigned long sndbit[NBITS(SND_MAX)];
- unsigned long ffbit[NBITS(FF_MAX)];
- unsigned long swbit[NBITS(SW_MAX)];
-
- unsigned int keycodemax;
- unsigned int keycodesize;
- void *keycode;
- int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
- int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);
-
- struct ff_device *ff;
-
- unsigned int repeat_key;
- struct timer_list timer;
-
- int state;
-
- int sync;
-
- int abs[ABS_MAX + 1];
- int rep[REP_MAX + 1];
-
- unsigned long key[NBITS(KEY_MAX)];
- unsigned long led[NBITS(LED_MAX)];
- unsigned long snd[NBITS(SND_MAX)];
- unsigned long sw[NBITS(SW_MAX)];
-
- int absmax[ABS_MAX + 1];
- int absmin[ABS_MAX + 1];
- int absfuzz[ABS_MAX + 1];
- int absflat[ABS_MAX + 1];
-
- int (*open)(struct input_dev *dev);
- void (*close)(struct input_dev *dev);
- int (*flush)(struct input_dev *dev, struct file *file);
- int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
-
- struct input_handle *grab;
-
- struct mutex mutex;
- unsigned int users;
-
- struct class_device cdev;
- union {
- struct device *parent;
- } dev;
-
- struct list_head h_list;
- struct list_head node;
- };
也许就这样赤裸裸的看上面的结构体,会觉得摸不着头脑,但是有一点是确定的,我们在写输入设备驱动时会定义这样一个输入设备结构体,并调用input_allocate_device()函数,这个函数的功能是为新添加的输入设备分配内存,如果成功,将返回input_dev *的指针结构,因此在写驱动的时候应该接受返回值,作为驱动层获得了一个新的输入设备操作的接口。
那么input_allocate_device()函数做了什么呢?打开函数看一下(input.c中实现):
- struct input_dev *input_allocate_device(void)
- {
- struct input_dev *dev;
-
-
- dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
-
- if (dev) {
- dev->cdev.class = &input_class;
- dev->cdev.groups = input_dev_attr_groups;
- class_device_initialize(&dev->cdev);
- mutex_init(&dev->mutex);
- INIT_LIST_HEAD(&dev->h_list);
- INIT_LIST_HEAD(&dev->node);
-
- }
- }
通过input_allocate_device()函数,我们设备驱动现在持有的input_dev里面就被赋予了input的“形象”,但是还需要我们去充实一下“内在”,因此,设备驱动程序,还需要为自己的设备增加自己的特性,才能创造独有的设备“形象”。拿上一节的代码举例如下:
- struct input_dev *input_dev = input_allocate_device();
- input_dev->name = "s3c2410 Touchscreen";
- input_dev->phys = "s3c2410ts/input0";
- input_dev->id.bustype = BUS_HOST;
- input_dev->id.vendor = 0x0001;
- input_dev->id.product = 0x0002;
- input_dev->id.version = 0x0100;
- input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN, PRESSURE_MAX, 0, 0);
以上的内容在前一节触摸屏驱动的初始化代码中也有分析,完成了输入设备的初始化工作。但是这仅是初始化自己的“特点”,还需要通知输入子系统有这样一个新设备诞生了,这就需要调用输入子系统的注册函数input_register_device(input_dev)来完成。
input_register_device()用于注册一个输入设备。那么注册过程是怎样的呢?这是一个重点,我们在下面的代码中进行注释分析:
- int input_register_device(struct input_dev *dev)
- {
-
- static atomic_t input_no = ATOMIC_INIT(0);
-
- struct input_handler *handler;
- const char *path;
- int error;
-
-
- set_bit(EV_SYN, dev->evbit);
-
-
-
-
-
-
- init_timer(&dev->timer);
- if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
- dev->timer.data = (long) dev;
- dev->timer.function = input_repeat_key;
- dev->rep[REP_DELAY] = 250;
- dev->rep[REP_PERIOD] = 33;
- }
-
-
- if (!dev->getkeycode)
- dev->getkeycode = input_default_getkeycode;
-
-
- if (!dev->setkeycode)
- dev->setkeycode = input_default_setkeycode;
-
-
- list_add_tail(&dev->node, &input_dev_list);
-
-
- snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
- "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
-
-
- if (!dev->cdev.dev)
- dev->cdev.dev = dev->dev.parent;
-
-
- error = class_device_add(&dev->cdev);
- if (error)
- return error;
-
-
- path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
- printk(KERN_INFO "input: %s as %s\n",
- dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
- kfree(path);
-
-
-
-
-
-
- list_for_each_entry(handler, &input_handler_list, node)
- input_attach_handler(dev, handler);
-
- input_wakeup_procfs_readers();
-
- return 0;
- }
上面的代码主要的功能有以下几个功能,也是设备驱动注册为输入设备委托内核做的事情:
- 进一步初始化输入设备,例如连击事件;
- 注册输入设备到input类中;
- 把输入设备挂到输入设备链表input_dev_list中;
- 查找并匹配输入设备对应的事件处理层,通过input_handler_list链表
我们需要再分析下这个匹配的过程,“相亲”这种事情还是很有意思的,但是需要注意的是下面分析的代码是我们暂时无法分析的,因为那样会使得情况变得更加复杂,当我们从应用层往下分析的时候一切都会明白。input_attach_handler匹配过程如下:
- const struct input_device_id *id;
- int error;
-
-
- if (handler->blacklist && input_match_device(handler->blacklist, dev))
- return -ENODEV;
-
-
- id = input_match_device(handler->id_table, dev);
- if (!id)
- return -ENODEV;
-
-
-
-
- error = handler->connect(handler, dev, id);
- if (error && error != -ENODEV)
- printk(KERN_ERR
- "input: failed to attach handler %s to device %s, "
- "error: %d\n",
- handler->name, kobject_name(&dev->cdev.kobj), error);
-
- return error;
我们先来看下input_match_device()函数,看一下这个匹配的条件是什么,如何匹配的过程是怎样的,匹配的结果会是什么?
-
- for (; id->flags || id->driver_info; id++) {
-
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_BUS)
- if (id->bustype != dev->id.bustype)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR)
- if (id->vendor != dev->id.vendor)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT)
- if (id->product != dev->id.product)
- continue;
-
- if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION)
- if (id->version != dev->id.version)
- continue;
-
-
-
-
-
- MATCH_BIT(evbit, EV_MAX);
- MATCH_BIT(keybit, KEY_MAX);
- MATCH_BIT(relbit, REL_MAX);
- MATCH_BIT(absbit, ABS_MAX);
- MATCH_BIT(mscbit, MSC_MAX);
- MATCH_BIT(ledbit, LED_MAX);
- MATCH_BIT(sndbit, SND_MAX);
- MATCH_BIT(ffbit, FF_MAX);
- MATCH_BIT(swbit, SW_MAX);
-
- return id;
- }
-
- return NULL;
既然证明是合适的,接下来就应该登记注册,并公证了。还记得handler->connect(handler, dev, id)函数吧,当input_match_device()找到最合适的事件处理层驱动时,便执行handler->connect函数进行公证了,看下面这部分代码(假如说找到了evdev类型的驱动,在input/evdev.c中):
- struct evdev *evdev;
- struct class_device *cdev;
- dev_t devt;
- int minor;
- int error;
-
-
- for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
-
- if (minor == EVDEV_MINORS) {
- printk(KERN_ERR "evdev: no more free evdev devices\n");
- return -ENFILE;
- }
-
- evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
- if (!evdev)
- return -ENOMEM;
-
-
- INIT_LIST_HEAD(&evdev->client_list);
- init_waitqueue_head(&evdev->wait);
-
-
- evdev->exist = 1;
- evdev->minor = minor;
- evdev->handle.dev = dev;
- evdev->handle.name = evdev->name;
- evdev->handle.handler = handler;
- evdev->handle.private = evdev;
- sprintf(evdev->name, "event%d", minor);
-
-
- evdev_table[minor] = evdev;
-
-
- devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
-
-
- cdev = class_device_create(&input_class, &dev->cdev, devt,
- dev->cdev.dev, evdev->name);
- if (IS_ERR(cdev)) {
- error = PTR_ERR(cdev);
- goto err_free_evdev;
- }
-
-
- error = sysfs_create_link(&input_class.subsys.kobj,
- &cdev->kobj, evdev->name);
- if (error)
- goto err_cdev_destroy;
-
-
- error = input_register_handle(&evdev->handle);
通过上述代码的执行,最终,输入设备在input_register_handle()的关联下与已经匹配上的handler结合,代码如下:
- struct input_handler *handler = handle->handler;
-
-
-
-
- list_add_tail(&handle->d_node, &handle->dev->h_list);
- list_add_tail(&handle->h_node, &handler->h_list);
-
-
- if (handler->start)
- handler->start(handle);
以上是输入设备驱动注册的全过程,牵涉的代码比较多,需要从宏观上理顺。纵观整个过程,输入设备驱动最终的目的就是能够与事件处理层的事件驱动相互匹配,但是在drivers/input目录下有evdev.c事件驱动、mousedev.c事件驱动、joydev.c事件驱动等等,我们的输入设备产生的事件应该最终上报给谁,然后让事件驱动再去处理呢?知道了这么个原因再看上面代码就会明白,其实evdev.c、mousedev.c等根据硬件输入设备的处理方式的不同抽象出了不同的事件处理接口帮助上层去调用,而我们写的设备驱动程序只不过是完成了硬件寄存器中数据的读写,但提交给用户的事件必须是经过事件处理层的封装和同步才能够完成的,事件处理层提供给用户一个统一的界面来操作。
由于以上的这些原因,才有了上述代码的关联过程,通过下图7,看一下整个关联注册的过程:
图7 input_dev与handler关联图
通过上图我们可以看到input输入设备匹配关联的关键过程以及涉及到的关键函数和数据。这一节主要是从input设备驱动程序的角度去看输入子系统的注册过程和三层之间的关联,下一节将从应用层的角度分析事件的接受过程和处理过程以及三层之间是如何配合处理输入事件的。
1.2 从应用层的角度出发看input子系统
以上部分已经借助input子系统把input设备驱动层与事件驱动层进行了关联,我们以前面几节所介绍的s3c2440_ts.c(输入设备层驱动)和evdev.c(事件处理层驱动)为例,来分析这一过程。
由于s3c2440_ts.c中上报的事件类型为按键、绝对值坐标,而evdev事件驱动程序是全匹配的,因此早在s3c2440_ts.c注册的过程中,就会创建设备节点/dev/input/event0(假设内核中没有其他的event类型的输入设备,这里就是event0),当然由于我的内核是2.6.22,这个节点的名字是/dev/event0。因此需要注意你的内核版本,以确定设备节点的位置,我们以“/dev/event0”为例来说明。
我们知道,应用层使用设备的第一步,是open(“/dev/event0”),因此这里event0的主设备号成为关键,因为主设备号将表明你是什么设备,我们ls -l查看/dev/event0发现:
crw-r-----1 root root 13, 64 2012-07-26 14:32 /dev/input/event0
由此可见主设备是13,输入命令cat /proc/devices查看主设备为13的是input设备,因此可以确定当我们执行open函数打开event0设备的时候,会调用input设备的open驱动函数,这个函数在input.c中,为了说明这一问题,需要从input驱动注册过程开始,还是input.c文件:
-
- static int __init input_init(void)
- {
- class_register(&input_class);
- input_proc_init();
- register_chrdev(INPUT_MAJOR,"input", &input_fops);
- }
可以看到,输入设备初始化的过程首先建立了input类,初始化input在proc下的节点,然后注册input设备,设备名称为input,操作接口是input_fops,主设备号是INPUT_MAJOR=13。
由以上可知,只要是主设备号为13的设备驱动程序,都是用input_fops接口,即当event0设备使用open函数打开时,会调用到input_fops接口中的open驱动函数,这个结构体的初始化为:
- static const struct file_operations input_fops = {
- .owner = THIS_MODULE,
- .open = input_open_file,
- };
可以看到,只实现了一个open功能字段,再看input_open_file的实现:
- static int input_open_file(struct inode *inode, struct file *file)
- {
- struct input_handler *handler =input_table[iminor(inode) >> 5];
- const struct file_operations *old_fops,*new_fops = NULL;
- if (!handler || !(new_fops =fops_get(handler->fops)))
- return -ENODEV;
- old_fops = file->f_op;
- file->f_op = new_fops;
- new_fops->open(inode, file);
- }
以上代码的功能为找到对应事件驱动层的fops,即进行fops的接口转换,指向对应设备的事件处理接口。其中input_table[iminor(inode)]>>5的input_table是一个全局的input_handler类型的数组,iminor(inode)取得次设备号,并且右移5位索引input_table表中对应的位置,为什么这样做呢?这是因为这个表格中填写的就是事件处理的指针,我们待会分析,先继续查看下面的代码。if中将判断是否为空并且事件处理层中的fops有没有初始化,如果没有就不能进行接口转换,报出设备不存在的错误,如果设备存在则把input设备的f_op驱动接口指向input_table表中存在的接口,并调用其open函数。那么这个input_table里面到底存放了什么呢?我们还是拿触摸屏驱动来讲解。由于触摸屏驱动已经完成了和evdev.c事件处理层的匹配,且次设备号为64,设备名称为/dev/event0,这是我们通过分析驱动注册中获得的内容,既然input核心设备注册了,s3c2440触摸屏驱动也注册了,那会不会evdev设备也会注册了呢?答案是肯定的,要想知道input_table里面放了什么,必须要去查看evdev设备的注册过程,打开input/evdev.c查看它的注册过程:
- 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,
- };
-
- static int __init evdev_init(void)
- {
- return input_register_handler(&evdev_handler);
- }
由以上的内容可以知道evdev_handler也被作为一个设备来操作,但是它属于input handler事件处理设备,然而我们在evdev_handler结构体的.fops字段又发现它的驱动接口为字符设备类型,在input中,如果input_table匹配到了evdev_handler,将会把file->f_op=&evdev_fops,那么如果使用read、write等函数操作,将会调用到evdev_fops中的read、write。
为了进一步查看input_table表中的内容是如何填充的,还需要查看这个注册的过程:
- int input_register_handler(struct input_handler *handler)
- {
- ……
- input_table[handler->minor>> 5] = handler;
- ……
- }
当然这个注册过程并不是只有这么一句话,看到这条语句,相信你应该知道什么意思了。在input的open函数执行之前,即我们的open代码打开之前,input_table中的字段已经被事件处理层填充了。由于evdev的次设备号在初始化的时候就设置成了64,因此这里相当于:
- input_table[2]=&evdev_handler;
回到input_open_file函数查看new_fops->open(inode, file)你便知道了调用的是:
- evdev_handler.evdev_fops.open(inode, file);
在分析open函数之前,解释一下为什么要右移5位,这说明一个问题,次设备号的低5位被忽略,这说明evdev的最大支持的输入设备驱动个数为2^5次方等于32个,你可能会看到你的/dev目录下面有event0、event1、event2等设备,他们的次设备号分别为64、65、66等等。但最大是64+32-1,因此input_table为这些输入设备增加的一个统一接口,通过上层打开设备时,只要次设备号在64+32-1之间的设备都会重新定位到evdev_handler中,即event*设备打开后执行的底层函数将被重新定义到evdev_handler中。
相信上面的问题已经描述清楚,如果还是不明白,你最起码应该知道的是,input设备中的open函数只是一个接口,通过次设备号才找到了真正的事件处理接口。接下来要看新的open接口的实现了,evdev_handler-> fops->open实现如下:
-
- 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_client *client;
- struct evdev *evdev;
-
- int i = iminor(inode) - EVDEV_MINOR_BASE;
- int error;
-
- if (i >= EVDEV_MINORS)
- return -ENODEV;
-
- evdev = evdev_table[i];
-
- if (!evdev || !evdev->exist)
- return -ENODEV;
-
- client = kzalloc(sizeof(struct evdev_client),GFP_KERNEL);
- if (!client)
- return -ENOMEM;
-
-
- client->evdev = evdev;
-
- list_add_tail(&client->node,&evdev->client_list);
-
- if (!evdev->open++ &&evdev->exist) {
- error =input_open_device(&evdev->handle);
- if (error) {
- list_del(&client->node);
- kfree(client);
- return error;
- }
- }
-
- file->private_data = client;
- return 0;
- }
- 由上的代码可以看出,最终是要执行input_open_device去执行设备驱动程序中的代码,然而我们在定义设备驱动的时候并没有给input_dev中的open字段填充内容,因此可以看到input_open_device函数的执行过程:
- if(!dev->users++ && dev->open)
- err = dev->open(dev);
-
- if (err)
- handle->open--;
上面截取了片段,并没有执行到open函数,open进行自减操作,表示没有调用过open,这个值主要是为了close中判断open为0时释放资源使用。
不仅如此,我们在触摸屏驱动中也没有定义read、write,那当触摸屏上报事件时,是如何处理的呢?我们需要先到触摸屏驱动程序中找到上报事件的函数再做进一步分析。
1.3 输入设备上报事件的处理过程
触摸屏驱动程序上报事件的函数为:
- input_report_abs(dev,ABS_X, s3c2440_ts->tc.xp);
- input_report_abs(dev,ABS_Y, s3c2440_ts->tc.yp);
- input_report_abs(dev,ABS_PRESSURE, s3c2440_ts->tc.pressure);
- input_report_key(dev,BTN_TOUCH, s3c2440_ts->pendown);
- input_sync(dev);
然而他们其实是input_event函数的封装,调用的都是input_event函数,这一函数在input.c中实现如下:
- void input_event(struct input_dev *dev, unsigned int type, unsigned int code, intvalue)
- {
- struct input_handle *handle;
-
- if (type > EV_MAX || !test_bit(type,dev->evbit))
- return;
- switch (type) {
- case EV_SYN:
- switch (code) {
- case SYN_CONFIG:
- if(dev->event)
- dev->event(dev,type, code, value);
- break;
- case SYN_REPORT:
- if(dev->sync)
- return;
- dev->sync= 1;
- break;
- }
- break;
- case EV_KEY:
- case EV_SW:
- case EV_ABS:
- case EV_REL:
- case EV_MSC:
- case EV_LED:
- case EV_SND:
- case EV_REP:
- case EV_FF:
- }
-
- if (type != EV_SYN)
- dev->sync = 0;
-
- if (dev->grab)
- dev->grab->handler->event(dev->grab,type, code, value);
- else
- list_for_each_entry(handle,&dev->h_list, d_node)
- if (handle->open)
- handle->handler->event(handle,type, code, value);
- }
代码被做了精简,其中就是在匹配上报的事件,并根据事件的类型调用驱动程序中相应的函数来完成,但是由于我们并,没有定义过这些函数,因此执行最后的handle_handler_event函数,由事件处理层evdev_event函数来完成事件的保存工作,具体过程如下:
- list_for_each_entry(client,&evdev->client_list, node) {
- client->buffer[client->head].type= type;
- client->buffer[client->head].code= code;
- client->buffer[client->head].value= value;
- client->head= (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);
- }
这里列举了关键代码,即上报的事件被保存到了client_buffer中,其中client_buffer是一个循环缓冲区,client->head表示当前数据的位置,因此每次都写到client->head的位置,而读数据时需要到client_tail中读取。因为在open的时候,client已经被链入到了evdev->client_list中,因此通过可以通过list_for_each_entry重evdev->client_list中找到对应的client。
事件的上报都会把数据保存到client->buffer中,以便上层通过read和write进行读去和写入。
1.4 通过设备节点读取输入事件
还是以触摸屏驱动程序和evdev事件处理层驱动来分析:
- 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;
- int retval;
-
-
- if (count < evdev_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;
-
-
- while (client->head != client->tail&& retval + evdev_event_size() <= count) {
-
- struct input_event *event =(struct input_event *) client->buffer + client->tail;
-
- if (evdev_event_to_user(buffer +retval, event))
- return -EFAULT;
-
- client->tail = (client->tail+ 1) & (EVDEV_BUFFER_SIZE - 1);
- retval += evdev_event_size();
- }
-
- return retval;
- }
这里如果没有数据,进程会睡眠,那由谁来唤醒呢?细心的话可以发现,当设备驱动层调用input_event上报事件调用相应的event函数进行事件写入时,是会唤醒阻塞等待的进程的。
1.5 通过设备节点写入输入事件
写入过程:
- static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count,loff_t *ppos)
- {
-
- while (retval < count) {
-
- if (evdev_event_from_user(buffer +retval, &event))
- return -EFAULT;
- input_inject_event(&evdev->handle,event.type, event.code, event.value);
- retval += evdev_event_size();
- }
-
- return retval;
- }
上述代码中的event是input_event数组,包含了事件的类型、键值,通过input_inject_event把数据写入循环数组client->buffer中,input_inject_event调用的是input_event函数。
1.6 总结
本节对input子系统的整个过程做了分析,并从两个角度进行考虑,对于写输入设备驱动程序的来说,需要掌握的是设备应该上报事件的类型,这样才能匹配到对应的事件层驱动帮助你保存对应的数据,而对于设备上层开发者来说,应该先使用cat /proc/bus/input/devices查看你操作的设备类型和处理接口,以帮助你更好的对设备操作。