依然不明白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时的阻塞。