没错,到目前为止,akm input设备也注册了,evdev handler大叔的全貌也搞明白了,他们的女儿也亭亭玉立了……是不是该干正事的时候了?
好吧,我们就来看看如何来干这份正事:
还是列出那份操作集函数(handler大叔为我们这些笑的最后的弟兄精心准备的泡妹妹大法,不过现实生活中可没有这样的爸爸):
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
};
还记得吗?这就是我们精心填表后得到的那个u盾。
要了解evdev,咱先得打开她的心扉,来了解了解她。记得一位情圣曾经说过的一句话:要获得一位美女的好感,要做的第一件事就是能打开她的话匣子,要聊那些她感兴趣的事儿,彻底打开她的心扉,这样她就会对你产生信任感….接着呢?接着就不说了,有兴趣的兄弟可以找我聊聊(研发楼二楼进门就可以看到一个长的不是很搓的瘦瘦哥们)。
好了,在应用层,我们可以通过调用open函数然后转到我们这个操作集函数中的.open = evdev_open,。是不是很简单。嗯,确实简单的一B。继续,看看具体打开过程:
static int evdev_open(struct inode *inode, struct file *file)
{
1 struct evdev *evdev;
2 struct evdev_client *client;
3 int i = iminor(inode) - EVDEV_MINOR_BASE;
4 int error;
5 if (i >= EVDEV_MINORS)
6 return -ENODEV;
7 error = mutex_lock_interruptible(&evdev_table_mutex);
8 if (error)
9 return error;
10 evdev = evdev_table[i];
11 if (evdev)
12 get_device(&evdev->dev);
13 mutex_unlock(&evdev_table_mutex);
14 if (!evdev)
15 return -ENODEV;
16 client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
17 if (!client) {
18 error = -ENOMEM;
19 goto err_put_evdev;
20 }
21 spin_lock_init(&client->buffer_lock);
22 wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, "evdev");
23 client->evdev = evdev;
24 evdev_attach_client(evdev, client);
25 error = evdev_open_device(evdev);
26 if (error)
27 goto err_free_client;
28 file->private_data = client;
29 return 0;
30 err_free_client:
31 evdev_detach_client(evdev, client);
32 kfree(client);
33 err_put_evdev:
34 put_device(&evdev->dev);
35 return error;
}
俗话说:话说三遍淡如水,再说三遍打驴嘴。相信看过我前面章节的哥们要来读懂这个函数,简直是 a piece of cake。不过还是有几行代码需要提一提:
3、10两行,第3行:通过设备节点号找到摇篮位置编号;第10行:通过这个摇篮编号找到那个孩子,对了,已经不是孩子了,是个美少女了。
16行,创建struct evdev_client,还记得吗,她是在struct evdev的基础上改造出来的品牌机。再三强调:不是山寨。
24行,进行绑定。
25行,关键的25行,怎么关键了,跟踪进去:
static int evdev_open_device(struct evdev *evdev)
{
int retval;
retval = mutex_lock_interruptible(&evdev->mutex);
if (retval)
return retval;
if (!evdev->exist)
retval = -ENODEV;
else if (!evdev->open++) {
retval = input_open_device(&evdev->handle);
if (retval)
evdev->open--;
}
mutex_unlock(&evdev->mutex);
return retval;
}
眼神不是很差的哥们马上会看到,有一行代码加粗了,他就是我们这个函数的核心。就好比小时候老师经常让我们写文章段落大意,找中心句子,如果找不出来,就去操场罚跑步。天生缺乏语言细胞,那时的我步可没少跑,连操场上捡垃圾的大妈都认识我了,偶尔一天侥幸过关没去跑,第二天大妈会说“小娃子,昨天是不是没来上课啊”。估计长不胖的一个重大原因就是小时候语文课上中心句子找少了。搞得现在一看到一堆代码,就想找出那个中心来。
好了继续跟踪进来,看看input_open_device(&evdev->handle)这个中心干了什么事情:
int input_open_device(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
int retval;
retval = mutex_lock_interruptible(&dev->mutex);
if (retval)
return retval;
if (dev->going_away) {
retval = -ENODEV;
goto out;
}
handle->open++;
if (!dev->users++ && dev->open)
retval = dev->open(dev);
if (retval) {
dev->users--;
if (!--handle->open) {
/*
* Make sure we are not delivering any more events
* through this handle
*/
synchronize_rcu();
}
}
out:
mutex_unlock(&dev->mutex);
return retval;
我只能说:兄弟你太有才了,马上又一眼看穿中心句了;对就这里这个加粗部分:
handle->open++;。哥们对它有没有印象,不记得的话,打开第10节,看里面的第一段函数中的加粗部分,就是因为它,我们的数据才得以传递下去……
28行 file->private_data = client;把这个client付给文件的私有数据,为什么赋给他,请看下一章节。