zoukankan      html  css  js  c++  java
  • Linux TTY驱动--Serial Core层【转】

    接上一节:

    Linux TTY驱动--Uart_driver底层

    一. 为了给USB-Serial类型的串口打基础(USB-Serial和Serial Core一样,构造了一个tty_driver和tty_operations,叫做usb-serial层),这里仔细分析Serial Core层完成的工作,实现代码为/drivers/serial/serial_core.c(kernel 2.6.28)。从哪里讲起呢,还是找找module_init,发现没有,在/drivers/serial/*众多文件里寻找没有,怀疑Serial Core层不是一个驱动模块,只能导出了一些上篇文章的函数而已。那就只有从uart_register_driver函数开始了,不过首先看看serial_core.c文件中使用的结构体或者常量,发现了如下:

    static const struct tty_operations uart_ops = {
        .open        = uart_open,
        .close        = uart_close,
        .write        = uart_write,
        .put_char    = uart_put_char,
        .flush_chars    = uart_flush_chars,
        .write_room    = uart_write_room,
        .chars_in_buffer= uart_chars_in_buffer,
        .flush_buffer    = uart_flush_buffer,
        .ioctl        = uart_ioctl,
        .throttle    = uart_throttle,
        .unthrottle    = uart_unthrottle,
        .send_xchar    = uart_send_xchar,
        .set_termios    = uart_set_termios,
        .set_ldisc    = uart_set_ldisc,
        .stop        = uart_stop,
        .start        = uart_start,
        .hangup        = uart_hangup,
        .break_ctl    = uart_break_ctl,
        .wait_until_sent= uart_wait_until_sent,
    #ifdef CONFIG_PROC_FS
        .read_proc    = uart_read_proc,
    #endif
        .tiocmget    = uart_tiocmget,
        .tiocmset    = uart_tiocmset,
    #ifdef CONFIG_CONSOLE_POLL
        .poll_init    = uart_poll_init,
        .poll_get_char    = uart_poll_get_char,
        .poll_put_char    = uart_poll_put_char,
    #endif
    };

    该结构体非常重要,是Core层和TTY层沟通的接口。在uart_register_driver的过程为:

    1. 调用alloc_tty_driver(tty层函数)初始化一个tty_driver

    2. 调用tty_set_operations(tty层) 给tty_driver 赋值上面的 uart_ops

    3.  通过tty_register_driver向tty层注册tty_driver(/drivers/char/tty_io.c中实现kernel2.6.28)。以上的uart_ops被tty层在需要时调用。

     

    二. 那么重点看一下 tty_driver、tty_operations两个结构体

    struct tty_driver {
        int    magic;        /* magic number for this structure */
        struct kref kref;    /* Reference management */
        struct cdev cdev;
        struct module    *owner;
        const char    *driver_name;
        const char    *name;
        int    name_base;    /* offset of printed name */
        int    major;        /* major device number */
        int    minor_start;    /* start of minor device number */
        int    minor_num;    /* number of *possible* devices */
        int    num;        /* number of devices allocated */
        short    type;        /* type of tty driver */
        short    subtype;    /* subtype of tty driver */
        struct ktermios init_termios; /* Initial termios */
        int    flags;        /* tty driver flags */
        struct proc_dir_entry *proc_entry; /* /proc fs entry */
        struct tty_driver *other; /* only used for the PTY driver */

        /*
         * Pointer to the tty data structures
         */
        struct tty_struct **ttys;
        struct ktermios **termios;
        struct ktermios **termios_locked;
        void *driver_state;

        /*
         * Driver methods
         */

        const struct tty_operations *ops;
        struct list_head tty_drivers;
    };

    struct tty_operations {
        struct tty_struct * (*lookup)(struct tty_driver *driver,
                struct inode *inode, int idx);
        int  (*install)(struct tty_driver *driver, struct tty_struct *tty);
        void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
        int  (*open)(struct tty_struct * tty, struct file * filp);
        void (*close)(struct tty_struct * tty, struct file * filp);
        void (*shutdown)(struct tty_struct *tty);
        int  (*write)(struct tty_struct * tty,
                  const unsigned char *buf, int count);
        int  (*put_char)(struct tty_struct *tty, unsigned char ch);
        void (*flush_chars)(struct tty_struct *tty);
        int  (*write_room)(struct tty_struct *tty);
        int  (*chars_in_buffer)(struct tty_struct *tty);
        int  (*ioctl)(struct tty_struct *tty, struct file * file,
                unsigned int cmd, unsigned long arg);
        long (*compat_ioctl)(struct tty_struct *tty, struct file * file,
                     unsigned int cmd, unsigned long arg);
        void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
        void (*throttle)(struct tty_struct * tty);
        void (*unthrottle)(struct tty_struct * tty);
        void (*stop)(struct tty_struct *tty);
        void (*start)(struct tty_struct *tty);
        void (*hangup)(struct tty_struct *tty);
        int (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
        void (*set_ldisc)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, int timeout);
        void (*send_xchar)(struct tty_struct *tty, char ch);
        int (*read_proc)(char *page, char **start, off_t off,
                  int count, int *eof, void *data);
        int (*tiocmget)(struct tty_struct *tty, struct file *file);
        int (*tiocmset)(struct tty_struct *tty, struct file *file,
                unsigned int set, unsigned int clear);
        int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty,
                    struct winsize *ws);
        int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
    #ifdef CONFIG_CONSOLE_POLL
        int (*poll_init)(struct tty_driver *driver, int line, char *options);
        int (*poll_get_char)(struct tty_driver *driver, int line);
        void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
    #endif
    };

    三. 上面实现的函数有点多,而且出现了上层struct tty_struct的结构体我们先缓一缓,在这里先看看注册tty_driver干了什么事情。tty_register_driver在drivers/char/tty_io.c中实现,跟踪了一下没啥东西,无,不过发现了我们熟悉的字符设备的几个函数(用颜色标注起来了),色的tty_drivers为字符设备的fops操作函数指针,代码:

    int tty_register_driver(struct tty_driver *driver)
    {
        int error;
        int i;
        dev_t dev;
        void **p = NULL;

        if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
            p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
            if (!p)
                return -ENOMEM;
        }

        if (!driver->major) {
            error = alloc_chrdev_region(&dev, driver->minor_start,
                            driver->num, driver->name);
            if (!error) {
                driver->major = MAJOR(dev);
                driver->minor_start = MINOR(dev);
            }
        } else {
            dev = MKDEV(driver->major, driver->minor_start);
            error = register_chrdev_region(dev, driver->num, driver->name);
        }
        if (error < 0) {
            kfree(p);
            return error;
        }

        if (p) {
            driver->ttys = (struct tty_struct **)p;
            driver->termios = (struct ktermios **)(p + driver->num);
        } else {
            driver->ttys = NULL;
            driver->termios = NULL;
        }

        cdev_init(&driver->cdev, &tty_fops);
        driver->cdev.owner = driver->owner;
        error = cdev_add(&driver->cdev, dev, driver->num);
        if (error) {
            unregister_chrdev_region(dev, driver->num);
            driver->ttys = NULL;
            driver->termios = NULL;
            kfree(p);
            return error;
        }

        mutex_lock(&tty_mutex);
        list_add(&driver->tty_drivers, &tty_drivers);
        mutex_unlock(&tty_mutex);

        if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
            for (i = 0; i < driver->num; i++)
                tty_register_device(driver, i, NULL);
        }
        proc_tty_register_driver(driver);
        driver->flags |= TTY_DRIVER_INSTALLED;
        return 0;
    }

    找到了TTY层字符设备的常量

    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,
    };

    四. tty_io.c文件中,另外还有两个和上面tty_fops类似的两个结构体,这两个结构体和fops共用一些函数,如tty_open:


    static const struct file_operations console_fops = {
        .llseek        = no_llseek,
        .read        = tty_read,
        .write        = redirected_tty_write,
        .poll        = tty_poll,
        .unlocked_ioctl    = tty_ioctl,
        .compat_ioctl    = tty_compat_ioctl,
        .open        = tty_open,
        .release    = tty_release,
        .fasync        = tty_fasync,
    };

    static const struct file_operations hung_up_tty_fops = {
        .llseek        = no_llseek,
        .read        = hung_up_tty_read,
        .write        = hung_up_tty_write,
        .poll        = hung_up_tty_poll,
        .unlocked_ioctl    = hung_up_tty_ioctl,
        .compat_ioctl    = hung_up_tty_compat_ioctl,
        .release    = tty_release,
    };

    先放这里,一会儿在解释。

    五. 接着上面的三,这里注册了一个字符的设备,关于注册字符设备,tty是一个模块,在模块入口函数static int __init tty_init(void)中,发现了同样有两个字符设备注册,一个是tty_fops,一个是console_fops(对应上面“四”中的结构体),这两个设备主设备号一样,从设备号一个是0,一个是1(/dev/tty、/dev/console):

    static int __init tty_init(void)
    {
        cdev_init(&tty_cdev, &tty_fops);
        if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
            panic("Couldn't register /dev/tty driver ");
        device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
                      "tty");

        cdev_init(&console_cdev, &console_fops);
        if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
            panic("Couldn't register /dev/console driver ");
        device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
                      "console");

    #ifdef CONFIG_VT
        vty_init(&console_fops);
    #endif
        return 0;
    }

    六.  再往上追溯tty_fops,以tty_open为例,但首先知道在tty层有一个全局的链表  LIST_HEAD(tty_drivers); 用来存储tty_register_driver时候注册的各个tty驱动(见tty_register_driver),因此tty中有一个函数 static struct tty_driver *get_tty_driver(dev_t device, int *index)用索引符号和dev_t来获取tty_driver,现在来看tty_open函数的调用:

    tty_open调用了_tty_open函数,

    static int __tty_open(struct inode *inode, struct file *filp)
    {
        struct tty_struct *tty = NULL;
        int noctty, retval;
        struct tty_driver *driver;
        int index;
        dev_t device = inode->i_rdev;
        unsigned short saved_flags = filp->f_flags;

        nonseekable_open(inode, filp);

    retry_open:
        noctty = filp->f_flags & O_NOCTTY;
        index  = -1;
        retval = 0;

        mutex_lock(&tty_mutex);

      if (device == MKDEV(TTYAUX_MAJOR, 0)) {    //  /dev/tty 设备
            tty = get_current_tty();
            if (!tty) {
                mutex_unlock(&tty_mutex);
                return -ENXIO;
            }
            driver = tty_driver_kref_get(tty->driver);
            index = tty->index;
            filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
            /* noctty = 1; */
            /* FIXME: Should we take a driver reference ? */
            tty_kref_put(tty);
            goto got_driver;
        }
    #ifdef CONFIG_VT
        if (device == MKDEV(TTY_MAJOR, 0)) {           // 
            extern struct tty_driver *console_driver;
            driver = tty_driver_kref_get(console_driver);
            index = fg_console;
            noctty = 1;
            goto got_driver;
        }
    #endif
        if (device == MKDEV(TTYAUX_MAJOR, 1)) {   /dev/console设备
            struct tty_driver *console_driver = console_device(&index);
            if (console_driver) {
                driver = tty_driver_kref_get(console_driver);
                if (driver) {
                    /* Don't let /dev/console block */
                    filp->f_flags |= O_NONBLOCK;
                    noctty = 1;
                    goto got_driver;
                }
            }
            mutex_unlock(&tty_mutex);
            return -ENODEV;
        }   

        driver = get_tty_driver(device, &index);                     //在全局tty_drivers链表中获取Core注册的tty_driver
        。。。。。。   

        if (!tty) {
            /* check whether we're reopening an existing tty */
            tty = tty_driver_lookup_tty(driver, inode, index);   //driver->ops->lookup(driver, inode, idx); 调用了Core层实现的tty_driver中的lookup函数。

            if (IS_ERR(tty))
                return PTR_ERR(tty);
        }

       tty = tty_init_dev(driver, index, 0);    //     tty 为tty_struct 结构体,利用tty_driver初始化tty_struct,在初始化的时候tty_driver的所有tty_operations赋值给了tty_struct的tty_operations变量,因此下面的tty->ops->open(tty, flip)事实上是调用了Core层注册的tty_driver的  int  (*open)(struct tty_struct * tty, struct file * filp)函数,见“二”。 

         。。。。。        

        filp->private_data = tty;    //添加到private_data中以备使用。

        。。。。。。

        retval = tty->ops->open(tty, filp);

    至于tty_read,tty_write,要涉及到tty_struct中的struct tty_ldisc ldisc中的read,write方法,这两者和tty_driver注册open等方法关系如何,后面再分析。

  • 相关阅读:
    dotnet core 获取 MacAddress 地址方法
    dotnet core 获取 MacAddress 地址方法
    dotnet core 发布只带必要的依赖文件
    dotnet core 发布只带必要的依赖文件
    Developing Universal Windows Apps 开发UWA应用 问答
    Developing Universal Windows Apps 开发UWA应用 问答
    cmd 如何跨驱动器移动文件夹
    cmd 如何跨驱动器移动文件夹
    C++ 驱动开发 error LNK2019
    C++ 驱动开发 error LNK2019
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5016203.html
Copyright © 2011-2022 走看看