input 子系统的分析与学习(一)

来源:互联网 发布:秦朝灭亡 知乎 编辑:程序博客网 时间:2024/06/05 17:09


依然不明白struct device 中的parent是什么意思?

最近在写一个光感传感器设备的驱动。不知为何上层的接口是通过input设备实现的。

其实我们自己也可以讲某一个输入设备 如传感器等写为一个字符设备,但是那样的话,我们需要自己实现open\read\write等方法,需要自己实现阻塞。但是通过实现成一个input设备,我们可以把注意力集中到input设备要汇报的事件上面,其他的工作系统已经帮我们完成了。

经过研究,input设备提供给我们的便利 就是它剥离了设备、设备产生的事件、事件的处理 这三者之间的逻辑。

三者的关系本来如下: 设备 ------- >事件----------> 事件的处理-------->上层应用。 其中设备驱动程序负责产生事件,产生的事件由 事件的handler处理,通常handler处理后,再向用户态发送消息,也就说与用户态的交互都有handler完成。设备驱动程序只负责检测并产生事件,产生事件后调用input_event 函数汇报事件。 然后,对于每一个input_dev,都有相应的handler负责处理。 input_dev与handler之间是独立的,也就是说一个input_dev 提交的 事件,会由这个设备上注册的每个handler都进行处理,而一个handler可以注册给多个设备。

 

input_dev和handler之间的挂钩是通过input_attach_handler函数来实现的,在这个函数中有很严格的匹配条件。内核中实现了evdev_handler和evbug_handler,这两个handler因为限制条件很宽松,所以可以挂接到所有的input_dev。 其中,evbug_handler用于打印消息,相当于是一个调试器,每当input_dev提交事件的时候,事件提交给evbug_handler处理时,evbug_handler都会把事件相关的消息打印出来。

evdev_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,};


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

 


 在evdev_handler中实现了connect、disconne函数等,分别实现handler与input_dev的挂接。其中,evdev_fops实现了完整的文件打开、读写等操作。值得重点一提。

 

首先,在input_init函数中, 有:  err = register_chrdev(INPUT_MAJOR, "input", &input_fops); 其中,input_fops中有:

static int input_open_file(struct inode *inode, struct file *file){struct input_handler *handler;const struct file_operations *old_fops, *new_fops = NULL;int err;lock_kernel();/* No load-on-demand here? */handler = input_table[iminor(inode) >> 5];if (!handler || !(new_fops = fops_get(handler->fops))) {err = -ENODEV;goto out;}/* * That's _really_ odd. Usually NULL ->open means "nothing special", * not "no device". Oh, well... */if (!new_fops->open) {fops_put(new_fops);err = -ENODEV;goto out;}old_fops = file->f_op;file->f_op = new_fops;err = new_fops->open(inode, file);if (err) {fops_put(file->f_op);file->f_op = fops_get(old_fops);}fops_put(old_fops);out:unlock_kernel();return err;}

 

也就是说,当我们通过open  /dev/input/ 这个目录下的设备时,都会调用input_open_file这个函数。因为通过register_chrdev这种方法注册的函数体 对于所有的主设备号是INPUT_MAJOR的设备都是通用的然后,在这个input_open_file中,通过这个 handler = input_table[iminor(inode) >> 5]; 可以得到evdev_handler。这里之所以得到的handler是evdev_handler 而不是其他的handler,是因为其他的handler都没有实现fops函数,内核在handler_register函数中,可以实现过滤。保证这里得到的就是evdev_handler。 因此,在这个input_open_file中,将file->f_ops进行了修改,把file->f_ops 指向了 evdev_handler->ev_fops。这样在 用户态以后调用 read\write等方法时,就会调用到evdev_handler中的方法中。

 

这里有些像是手动实现了 C++中的虚函数和多态。内核中有许多面向对象的思想,还需要多多领悟。

 

下面是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;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;}

 

在这个函数中实现了read时的阻塞。

 


 

原创粉丝点击