Handler兄果然是handler兄,很给力,这不,刚和人家好上,就有了爱情的种子。有位仁兄要问了:“他是怎么做到的呢?说出来让我学习学习一下,哥这么多年了,还是一直和自己的左手生活着”,好吧,我们就来看看事情是怎样发生的:
没有错,就在第六节的error = handler->connect(handler, dev, id);这行代码中,发生了那么点事儿,也就是那么点事儿,让他们最终走到了一起了,有时候你不得不佩服那句话的力量“生米煮成熟饭”,不过在当今社会,貌似这话也不再那么给力了……可以看到,此行代码调用了我们这位evdev handler兄的connect函数。好了,见证奇迹的时刻到了:
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
1 struct evdev *evdev;
2 int minor;
3 int error;
4 for (minor = 0; minor < EVDEV_MINORS; minor++)
5 if (!evdev_table[minor])
6 break;
7 if (minor == EVDEV_MINORS) {
8 printk(KERN_ERR "evdev: no more free evdev devices/n");
9 return -ENFILE;
10 }
11 evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
12 if (!evdev)
13 return -ENOMEM;
14 INIT_LIST_HEAD(&evdev->client_list);
15 spin_lock_init(&evdev->client_lock);
16 mutex_init(&evdev->mutex);
17 init_waitqueue_head(&evdev->wait);
18 snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
19 evdev->exist = 1;
20 evdev->minor = minor;
21 evdev->handle.dev = input_get_device(dev);
22 evdev->handle.name = evdev->name;
23 evdev->handle.handler = handler;
24 evdev->handle.private = evdev;
25 dev_set_name(&evdev->dev, evdev->name);
26 evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
27 evdev->dev.class = &input_class;
28 evdev->dev.parent = &dev->dev;
29 evdev->dev.release = evdev_free;
30 device_initialize(&evdev->dev);
31 error = input_register_handle(&evdev->handle);
32 if (error)
33 goto err_free_evdev;
34 error = evdev_install_chrdev(evdev);
35 if (error)
36 goto err_unregister_handle;
37 error = device_add(&evdev->dev);
38 if (error)
39 goto err_cleanup_evdev;
40 return 0;
41 err_cleanup_evdev:
42 evdev_cleanup(evdev);
43 err_unregister_handle:
44 input_unregister_handle(&evdev->handle);
45 err_free_evdev:
46 put_device(&evdev->dev);
47 return error;
48 }
估计兄弟你看到这么长的一个函数(其实不算很长,48行代码的函数在内核中算很短的了,当然中间省略了好多的空行),马上会拖动鼠标的滑轮一直往下走,如果你真的这么干了,说明兄弟你很有远见,因为我接下来会一行行的和你来谈论这个事件的经过。如果你没有这样,说明你是一个自由探索精神很强的哥们,好好保持,linux内核开发就需要你这样的人才。闲话不多说,我们来慢慢欣赏这小两口的那点事儿。
1-3行,定义一些基本变量,以后我们会用到的,注意这里来了一新的面孔,struct evdev *evdev,他是谁呢?这儿等会我们再讨论,先来看看这个家伙长什么样:
struct evdev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client *grab;
struct list_head client_list;
spinlock_t client_lock; /* protects client_list */
struct mutex mutex;
struct device dev;
};
现在我们不需要搞清楚里面每一个成员的含义,到了后面你自然就明白了。就是这样,有些东西你在某一个时侯怎么想也想不明白,只要一旦到了某个阶段,再一想想,其实就那么回事。怪自己当时想的多了,考虑的太复杂了,完全没必要。所以人啊,还是要活在当下,好好做好的当前的每一件事情,未来不可遇见,也不知会有什么即将发生在你身上,比如说2012。
4-6行 注意第5行中的evdev_table[minor]数组,她是一个专门用来装evdev的数组,可以把它想象成一个摇篮,只不过这个摇篮很大,一次可以装很多个孩子。注意到34行加粗部分evdev_install_chrdev(evdev)跟踪进去:
static int evdev_install_chrdev(struct evdev *evdev)
{
/*
* No need to do any locking here as calls to connect and
* disconnect are serialized by the input core
*/
evdev_table[evdev->minor] = evdev;
return 0;
}
这个函数很简单,把一个孩子evdev放到我们的摇篮中去。现在可以来讨论evdev这个结构是个啥东东了,没有错,他正是evdev handler 和我们那个input 设备美眉两个人在那些激情燃烧的岁月里创造出来的爱情结晶。而evdev_table[minor]这个摇篮是给他们装孩子用的。注意了,前面提过,evdev handler兄比较有福气,女人多,所以linux内核给他准备了一个很大的摇篮,不过摇篮的不同位置有标号的。记住,这位仁兄和一个女人只有一个孩子(计划生意在内核里也有体现的),不过男人毕竟在社会上还是主力军,还得为社会贡献自己的价值,所以老婆不能太多,最多也只可以有EVDEV_MINORS个女人,所以给他分配的那个摇篮最多只能装EVDEV_MINORS个孩子。好了现在回过头来看第4-6行代码。遍历整个摇篮的所有位置。找到一个没有孩子的空位,等下好放孩子。
7-10行,对不起,姐姐,你来晚了,摇篮都满了,说明什么,对,说明handler兄已经有EVDEV_MINORS个老婆了,那么对不起,虽然你们两情同意和,但咱还得遵守婚姻法,走吧,姐姐,当二奶一般转正的可能性不大,何况这位handler兄女人如云。
11-13行,很幸运,姐姐你来的够早,这儿还有位子,所以你们两个可以结婚,还可以生孩子。所以这一行代码比较关键,孩子的生命体在这里诞生了。至于怎么诞生的?这还问我?相信每一个智商正常的哥们都明白如何让他诞生,不明白的话,到两性网上去好好学习一下相关知识。12-13行,很不幸,孩子这个生命体是诞生了,不够还没出娘肚,就夭折了。这是很不幸的。
14-20行,初始化evdev的一些内部成员。比如锁,互斥量,等待队列等等。这里要注意第20行:evdev->minor = minor;把前面内核给孩子分配的摇篮位置号给孩子挂上,等下好对号入座。
21-24行,前面的三大数据结构,好像一直还有一个没出现,对!就是input_handle。这里终于出现了。这三行就是来填充这个数据结构的。input_handle怎么理解呢?对,他就是把孩子和他们的爸爸妈妈绑在一起的一个户口信息本。孩子的爸爸是谁,孩子的妈妈是谁,孩子叫什么名字,从此整个孩子的信息就到我们的handle里面去了。
25-29行,每一个dev设备,最终都要在linux系统中的/dev或者它的递归子目录生成一个文件,以后我们就可以对它进行open,read….操作了,这里几行再加上37行就是专干这事的。
31-33行 提交户口信息本给相关政府单位,让我们可爱的政府知道你们两有孩子了。
后面都是一些错误处理函数,这里就不一一分析,有兴趣的哥们可以去研究一下。
好了,该讲的都讲了,不该讲的好像也讲了。整个故事貌似结束了。如意郎君找到了,婚姻也结了,孩子也生了,户口也注册了。可是当你当回到这个故事的开头,你发现好像什么都还没开始。我们现在确实创造了一个设备文件evdevX(X代表摇篮位置编号)。可是用它来干什么呢?曾哥要说话了:七月份的尾巴,我是狮子座……这还不简单!这里的evdev就是拿来给我们应用开发者操作的,她是她妈akm这个input 设备对应的设备文件实体。怎么用她?那么我们得先搞定她老爸,evdev handler叔(毕竟有女儿的人了,不能再称兄了)。