楠榕槿 发表于 2023-1-1 17:57:11

【内核】输入子系统的驱动的驱动(上)

一.输入子系统简介二.打开input.c,位于内核deivers/input

<p><pre>    <code>subsys_initcall(input_init);   //修饰入口函数
module_exit(input_exit);   //修饰出口函数</code></pre></p>
<p><pre>    <code> 1 static int __init input_init(void)
2 {
3      int err;
4      err = class_register(&input_class);   //(1)注册类,放在/sys/class
5      if (err) {
6               printk(KERN_ERR "input: unable to register input_dev class\n");
7               return err;
8      }
9
10      err = input_proc_init();    //在/proc下面建立相关的文件
11      if (err)
12               goto fail1;
13
14      err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //(2)注册驱动
15      if (err) {
16               printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR);
17               goto fail2;
18      }
19
20
21
22      return 0;
23
24
25
26fail2:   input_proc_exit();
27
28fail1:   class_unregister(&input_class);
29
30      return err;
31
32 }</code></pre></p>
更多linux内核视频教程文档资料免费领取后台私信【内核】自行获取.

上面第4行”err = class_register(&input_class);”是在/sys/class 里创建一个 input类, input_class变量如下图:

2. 上面第14行通过register_chrdev创建驱动设备,其中变量INPUT_MAJOR =13,所以创建了一个主设备为13的"input"设备。



三. 然后进入input_open_file函数(drivers/input/input.c)

<p><pre>    <code>1 static int input_open_file(struct inode *inode, struct file *file)
2{
3      struct input_handler *handler = input_table; // (1)
4      const struct file_operations *old_fops, *new_fops = NULL;
5      int err;
6
7      if (!handler || !(new_fops = fops_get(handler->fops)))//(2)
8         return -ENODEV;
9
10   if (!new_fops->open) {
11            fops_put(new_fops);
12            return -ENODEV;
13   }
14
15   old_fops = file->f_op;
16   file->f_op = new_fops;   //(3)
17
18   err = new_fops->open(inode, file);   //(4)
19   if (err) {
20         fops_put(file->f_op);
http://www.qianxianly.com/data/attachment/forum/20230101/1672567031397_0.jpg

21            file->f_op = fops_get(old_fops);
22    }
23
24    fops_put(old_fops);
25
26   return err;
27 }</code></pre></p>
第3行中,其中iminor (inode)函数调用了MINOR(inode->i_rdev);读取子设备号,然后将子设备除以32,找到新挂载的input驱动的数组号,然后放在input_handler 驱动处理函数handler中第7行中,若handler有值,说明挂载有这个驱动,就将handler结构体里的成员file_operations * fops赋到新的file_operations *new_fops里面第16行中, 再将新的file_operations *new_fops赋到file-> file_operations *f_op里, 此时input子系统的file_operations就等于新挂载的input驱动的file_operations结构体,实现一个偷天换日的效果.第18行中,然后调用新挂载的input驱动的*new_fops里面的成员.open函数四.上面代码的input_table[]数组在初始时是没有值的,

<p><pre>    <code>1 int input_register_handler(struct input_handler *handler)
2 {
3 ... ...
4 input_table = handler;   //input_table[]被赋值
5 ... ...
6 list_add_tail(&handler->node, &input_handler_list); //然后将这个input_handler放到input_handler_list链表中
7 ... ...
8 }</code></pre></p>
五.继续来搜索input_register_handler,看看这个函数被谁来调用



<p><pre>    <code>static int __init evdev_init(void)
{
       return input_register_handler(&evdev_handler);//注册
}
</code></pre></p>
六.我们来看看这个evdev_handler变量是什么结构体,:

<p><pre>    <code>1 static struct input_handler evdev_handler = {
2      .event =evdev_event,   
3      .connect =      evdev_connect,//(4)
4      .disconnect = evdev_disconnect,
5      .fops =         &evdev_fops,    //(1)
6      .minor =EVDEV_MINOR_BASE, //(2)
7      .name =         "evdev",
8      .id_table =      evdev_ids, //(3)
9 };</code></pre></p>
第5行中.fops:文件操作结构体,其中evdev_fops函数就是自己的写的操作函数,然后赋到.fops中第6行中 .minor:用来存放次设备号



3. 第8行中.id_table : 表示能支持哪些输入设备,比如某个驱动设备的input_dev->的id和某个input_handler的id_table相匹配,就会调用.connect连接函数,如下图

4. 第3行中.connect:连接函数,将设备input_dev和某个input_handler建立连接,如下图

七.我们先来看看上图的input_register_device()函数,如何创建驱动设备的7.1然后进入input_register_device()函数,代码如下:

<p><pre>    <code>1 int input_register_device(struct input_dev *dev)   //*dev:要注册的驱动设备
2 {
3... ...
4      list_add_tail(&dev->node, &input_dev_list);   //(1)放入链表中
5... ...
6      list_for_each_entry(handler, &input_handler_list, node)//(2)
7      input_attach_handler(dev, handler);
8... ...
9 }</code></pre></p>
第4行中,将要注册的input_dev驱动设备放在input_dev_list链表中第6行中,其中input_handler_list在前面讲过,就是存放每个input_handle驱动处理结构体,然后list_for_each_entry()函数会将每个input_handle从链表中取出,放到handler中最后会调用input_attach_handler()函数,将每个input_handle的id_table进行判断,若两者支持便进行连接。7.2然后我们在回过头来看注册input_handler的input_register_handler()函数,如下图所示

7.3我们来看看input_attach_handler()如何实现匹配两者id的:

<p><pre>    <code>static int input_attach_handler(struct input_dev *dev, struct input_handler *handler)
{
... ...
id = input_match_device(handler->id_table, dev);//匹配两者
if (!id)                                     //若不匹配,return退出
return -ENODEV;
error = handler->connect(handler, dev, id);//调用input_handler ->connect函数建立连接
... ...
http://www.qianxianly.com/data/attachment/forum/20230101/1672567031397_1.png

}</code></pre></p>
八.我们还是以evdev.c(事件驱动) 的evdev_handler->connect函数



8.1 evdev_handler的.connect函数是evdev_connect(),代码如下:

<p><pre>    <code>1 static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id)   
2 {
3 ... ...
4 for (minor = 0; minor < EVDEV_MINORS && evdev_table; minor++); //查找驱动设备的子设备号
5   if (minor == EVDEV_MINORS) {// EVDEV_MINORS=32,所以该事件下的驱动设备最多存32个,
6         printk(KERN_ERR "evdev: no more free evdev devices\n");
7         return -ENFILE;                //没找到驱动设备
8   }
9... ...
10evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);   //分配一个input_handle全局结构体(没有r)
11... ...
12evdev->handle.dev = dev;            //指向参数input_dev驱动设备
13 evdev->handle.name = evdev->name;
14 evdev->handle.handler = handler;    //指向参数 input_handler驱动处理结构体
15 evdev->handle.private = evdev;
16 sprintf(evdev->name, "event%d", minor);    //(1)保存驱动设备名字, event%d
17 ... ...
18 devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),//(2) 将主设备号和次设备号转换成dev_t类型
19 cdev = class_device_create(&input_class, &dev->cdev, devt,dev->cdev.dev, evdev->name);
                                                         // (3)在input类下创建驱动设备
20
21 ... ...
22 error = input_register_handle(&evdev->handle); //(4)注册这个input_handle结构体
23
24 ... ...
25 }</code></pre></p>
第16行中,是在保存驱动设备名字,名为event%d, 比如下图(键盘驱动)event1: 因为没有设置子设备号,默认从小到大排列,其中event0是表示这个input子系统,所以这个键盘驱动名字就是event1第18行中,是在保存驱动设备的主次设备号,其中主设备号INPUT_MAJOR=13,因为EVDEV_MINOR_BASE=64,所以此设备号=64+驱动程序本事子设备号, 比如下图(键盘驱动)event1: 主次设备号就是13,65在之前在2小结里就分析了input_class类结构,所以第19行中,会在/sys/class/input类下创建驱动设备event%d,比如下图(键盘驱动)event1:



4. 最终会进入input_register_handle()函数来注册,代码在下面

8.2 input_register_handle()函数如下:

<p><pre>    <code> 1 int input_register_handle(struct input_handle *handle)
2 {
3       struct input_handler *handler = handle->handler; //handler= input_handler驱动处理结构体
4
5       list_add_tail(&handle->d_node, &handle->dev->h_list); //(1)
6       list_add_tail(&handle->h_node, &handler->h_list);    // (2)
7
8       if (handler->start)
9            handler->start(handle);
10       return 0;
11 }</code></pre></p>
在第5行中, 因为handle->dev指向input_dev驱动设备,所以就是将handle->d_node放入到input_dev驱动设备的h_list链表中,

2. 在第6行中, 同样, input_handler驱动处理结构体的h_list也指向了handle->h_node



九.建立了连接后,又如何读取evdev.c(事件驱动) 的evdev_handler->.fops->.read函数?

<p><pre>    <code>static ssize_t evdev_read(struct file *file, char __user *      buffer, size_t count, loff_t *ppos)
{
... ...
/*判断应用层要读取的数据是否正确*/
if (count < evdev_event_size())
return -EINVAL;
/*在非阻塞操作情况下,若client->head == client->tail|| evdev->exist时(没有数据),则return返回*/
if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))
return -EAGAIN;

/*若client->head == client->tail|| evdev->exist时(没有数据),等待中断进入睡眠状态*/
http://www.qianxianly.com/data/attachment/forum/20230101/1672567031397_2.png

retval = wait_event_interruptible(evdev->wait,client->head != client->tail || !evdev->exist);
... ...         //上传数据
}</code></pre></p>
十.若read函数进入了休眠状态,又是谁来唤醒?

<p><pre>    <code>static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
... ...
wake_up_interruptible(&evdev->wait);   //有事件触发,便唤醒等待中断
}</code></pre></p>


十一.分析下,是谁调用evdev_event()这个.event事件驱动函数

<p><pre>    <code>static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
/*获取按键值,赋到state里*/
... ...
/*上报事件*/
input_event(input, type, button->code, !!state);
input_sync(input);                        //同步信号通知,表示事件发送完毕
}</code></pre></p>
<p><pre>    <code>void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;
... ...
/* 通过input_dev ->h_list链表找到input_handle驱动处理结构体*/
list_for_each_entry(handle, &dev->h_list, d_node)   
if (handle->open)//如果input_handle之前open 过,那么这个就是我们的驱动处理结构体
    handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数
}</code></pre></p>


十二.本节总结分析:1. 注册输入子系统,进入put_init():

<p><pre>    <code>err = register_chrdev(INPUT_MAJOR, "input", &input_fops);</code></pre></p>
2. open打开驱动,进入input_open_file():

<p><pre>    <code>file->f_op=fops_get(handler->fops);</code></pre></p>
<p><pre>    <code>err = new_fops->open(inode, file);</code></pre></p>
3. 注册input_handler,进入input_register_handler():

<p><pre>    <code>input_table = handler;</code></pre></p>
<p><pre>    <code>list_add_tail(&handler->node, &input_handler_list);</code></pre></p>
<p><pre>    <code>list_for_each_entry(dev, &input_dev_list, node)   //遍历查找input_dev_list链表里所有input_dev
input_attach_handler(dev, handler);             //判断两者id,若两者支持便进行连接。</code></pre></p>
4. 注册input_dev,进入input_register_device():

<p><pre>    <code>list_add_tail(&dev->node, &input_dev_list);</code></pre></p>
<p><pre>    <code>list_for_each_entry(handler, &input_handler_list, node)//遍历查找input_handler_list链表里所有input_handler
input_attach_handler(dev, handler);                      //判断两者id,若两者支持便进行连接。</code></pre></p>
5. 判断input_handler和input_dev的id,进入input_attach_handler():

<p><pre>    <code>input_match_device(handler->id_table, dev);      //匹配input_handler和dev的id,不成功退出函数</code></pre></p>
<p><pre>    <code>handler->connect(handler, dev, id);            //建立连接</code></pre></p>
6. 建立input_handler和input_dev的连接,进入input_handler->connect():

<p><pre>    <code>evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);    //创建两者连接的input_handle全局结构体
list_add_tail(&handle->d_node, &handle->dev->h_list); //连接input_dev->h_list
list_add_tail(&handle->h_node, &handler->h_list);    // 连接input_handle->h_list</code></pre></p>
7. 有事件发生时,比如按键中断,在中断函数中需要进入input_event()上报事件:

<p><pre>    <code>list_for_each_entry(handle, &dev->h_list, d_node)   // 通过input_dev ->h_list链表找到input_handle驱动处理结构体
if (handle->open)//如果input_handle之前open 过,那么这个就是我们的驱动处理结构体(有可能一个驱动设备在不同情况下有不同的驱动处理方式)
    handle->handler->event(handle, type, code, value); //调用evdev_event()的.event事件函数
</code></pre></p>
页: [1]
查看完整版本: 【内核】输入子系统的驱动的驱动(上)