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等方法关系如何,后面再分析。

  • 相关阅读:
    jquery $.getJSON()跨域请求
    JQuery 字符串转时间格式
    php Function ereg() is deprecated的解决方法
    ucenter 验证码看不到的解决办法
    C#通过UserAgent判断智能设备(Android,IOS,PC,Mac)
    gpio_irq出现错误genirq: Setting trigger mode 6 for irq 168 failed (gpio_set_irq_type+0x0/0x230)
    驱动模块的加载(linux4.1.15)!
    电压环控制逻辑!
    用电阻检测大电流时2线电阻的PCB画法。
    比较两点压差(比如两点温度相差太大),超过范围,做出动作!
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5016203.html
Copyright © 2011-2022 走看看