您的位置:首页 > 运维架构 > Linux

Linux TTY驱动--Uart_driver底层

2015-06-08 21:31 501 查看
原文地址:http://blog.csdn.net/sharecode/article/details/9197567

接上一节:


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\n");

    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\n");

    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等方法关系如何,后面再分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tty linux