tty driver 赏析

简单的结构流程图

这里写图片描述

简单的结构框图

uart_register_driver 浅析

这里写图片描述

/**
    这个函数主要做了一下事情: 
                    1 : 分配tty_driver,初始化.
                    2 : 初始化 tty_port
                    3 : 注册tty_driver到设备驱动模型中(其实就是注册字符设备)
                                      将tty_driver 添加到全局链表tty_drivers上
*/
int uart_register_driver(struct uart_driver *drv)
{
        /**
            tty_driver 
            还是那个固定模式,分配内存、赋值初始化、然后注册到系统中,最后挂接到链表中
            下面详细的说明这个生命周期,有实验验证!

            alloc_tty_driver()    分配
                    -->tty_alloc_driver()
                        -->__tty_alloc_driver()
                            -->kzalloc(sizeof(struct tty_driver), GFP_KERNEL);

            ....                  赋值初始化

            tty_register_driver() 注册
                    -->alloc_chrdev_region()
                    -->tty_cdev_add()
                          --->cdev_init(...,&tty_fops)  // tty_fops 向VFS层提供的函数操作接口集.
                          --->cdev_add()
                    -->list_add(&driver->tty_drivers, &tty_drivers);

            看到了把,注册一个uart_driver 最终是变成了 注册一个tty_dirver. 哈哈!
            作者为什么要这样做 ?
        */
        struct tty_driver *normal;
        int i, retval;
        /**
            #ifndef HAVE_ARCH_BUG_ON
            #define BUG_ON(condition) 
                    do\ 
                    {\      不太可能会执行
                        if (unlikely(condition))\
                                BUG();\
                    } while(0)
            #endif
            #endif
            被调用的时候,它们会引发oops,导致栈的回溯和错误信息的打印

            用户不需要提供struct uart_state  内核下面的代码会 分配内存空间,并且初始化uart_port
            如果用户为uart_driver 提供了 uart_state,那么BUG()就会被调用了...........

            源码中也说了,这个是私有的,别动
            struct uart_driver{

                * these are private; the low level driver should not
                * touch these; they should be initialised to NULL
                * 
                struct uart_state   *state;
                struct tty_driver   *tty_driver;
            }
        */
        BUG_ON(drv->state);

        /*
            这个 uart_state 很重要 !
            申请 nr个 uart_state 空间 ,参见上图
            struct uart_state {
                ....
                struct circ_buf     xmit;         要发送数据的缓冲区 
                struct uart_port    *uart_port;   串口的物理信息
            };
            作者说 这里分配的时候 应该用一个slab 来分配内存 :
            Maybe we should be using a slab cache for this,
            especially if we have a large number of ports to handle.
        */
        drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
        if (!drv->state)
            goto out;

        /**
            底层调用kzalloc()来分配内存
            static inline struct tty_driver *alloc_tty_driver(unsigned int lines)
            {
                struct tty_driver *ret = tty_alloc_driver(lines, 0);
                                         {
                                             #define tty_alloc_driver(lines, flags) \
                                                __tty_alloc_driver(lines, THIS_MODULE, flags)
                                                {
                                                    struct tty_driver *driver;
                                                    ...
                                                    driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
                                                    ...
                                                    driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);
                                                }
                                         }  
            }

            并且还给cdev分配了内存   
            driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);

            看到这里,那么下面 cdev_init()、cdev_add()这些注册字符设备的函数 被调用也不远了把
            他们是在下面的 tty_register_driver()函数中被调用的,下面详细说
        */
        normal = alloc_tty_driver(drv->nr);
        if (!normal)
            goto out_kfree; 

        /*
            uart_driver 与 tty_driver 进行关联
        */
        drv->tty_driver = normal;

        /*
            对 tty_driver 赋值初始化.
        */  
        normal->driver_name = drv->driver_name; // 驱动的名字
        normal->name        = drv->dev_name;    // 设备的名字
        normal->major       = drv->major;       // 主设备号
        normal->minor_start = drv->minor;       // 次设备号起始
        normal->type        = TTY_DRIVER_TYPE_SERIAL;
        normal->subtype     = SERIAL_TYPE_NORMAL;
        normal->init_termios    = tty_std_termios;
        normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
        normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;  // 输入速度
        /*
            这个flags这块,比如 u_serial.c中在
            gs_tty_driver = alloc_tty_driver(MAX_U_SERIAL_PORTS);
            ....
            gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
            ....
            status = tty_register_driver(gs_tty_driver);
            上面的这个tty_register_dirver后,并没有 注册字符设备、创建设备节点、在/proc创建文件.
            只是简单的申请了一个主设备是250的设备号 
        */
        normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
        normal->driver_state    = drv;  // uart_state 在这里给tty_driver进行赋值
        /*
            ops赋值,不知道这里的uart_ops 在什么时候 会被调用 ?
            normal->ops = &uart_ops;
        */
        tty_set_operations(normal, &uart_ops);

        /*
            看完这个for之后,你会发现,他就是为了初始化tty_port
        */
        for (i = 0; i < drv->nr; i++) 
        {
            /*
                见上图
                上面已经为drv->state分配了内存空间,现在它指向一块内存空间,
                空间大小是 sizeof(struct uart_state) * nr
            */
            struct uart_state *state = drv->state + i;
            /*
                获取tty_port,下面就是为他赋值初始化了,
            */
            struct tty_port *port = &state->port;
            /*
                三个等待队列的初始化  open_wait  close_wait  delta_msr_wait
                二个互斥锁 + 一个自旋锁的初始化
                三个等待队列有什么用,下面说
            */
            tty_port_init(port);

            port->ops = &uart_port_ops;
            port->close_delay     = HZ / 2;   
            port->closing_wait    = 30 * HZ;  
        }
        /**
            1 :设备号的申请  alloc_chrdev_region()
            2 : 字符设备的注册
            3 : 将tty_driver 添加到 全局链表tty_drivers上去

            int tty_register_driver(struct tty_driver *driver)
            {
                if (!driver->major) {
                    alloc_chrdev_region()
                }else{
                    register_chrdev_region()
                }
                ...
                error = tty_cdev_add(driver, dev, 0, driver->num);
                        {
                            用户空间opne、poll,下面对应的函数会被首先调用
                            在这些函数中,会调用对应线路规程提供的对应函数
                            static const struct file_operations tty_fops = {
                                .llseek     = no_llseek,
                                .read       = tty_read,
                                .write      = tty_write,
                                .poll       = tty_poll,
                                .unlocked_ioctl = tty_ioctl,
                                .compat_ioctl   = tty_compat_ioctl,
                                .open       = tty_open,
                                .release    = tty_release,
                                .fasync     = tty_fasync,
                            };
                            cdev_init(&driver->cdevs[index], &tty_fops);   //字符设备的注册  
                            cdev_add();
                        }
                ...     
                list_add(&driver->tty_drivers, &tty_drivers);   添加到全局链表上
                ...
                proc_tty_register_driver(driver);   
            }
            如果这个函数调用成功,在这里就返回了,
        */
        retval = tty_register_driver(normal);
        if (retval >= 0)
            return retval;
        /**
            这行代码被执行的是在 
            1  : drv->state = kzalloc()失败
            2  : tty_register_driver()调用失败的时候 执行的代码

        正常的流程 不会执行下面的代码
        */
        for (i = 0; i < drv->nr; i++)
            tty_port_destroy(&drv->state[i].port);
        put_tty_driver(normal);
}

整个函数的结构
这里写图片描述

tty_ldisc分析

打开的过程

读取、写入的过程

内核中的示例

小实验验证

猜你喜欢

转载自blog.csdn.net/leesagacious/article/details/54670735
tty
今日推荐