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

Linux TTY驱动--Serial Core层【转】的更多相关文章

  1. Linux TTY驱动--Uart_driver底层【转】

    转自:http://blog.csdn.net/sharecode/article/details/9196591 版权声明:本文为博主原创文章,未经博主允许不得转载. Linux 中将串口驱动进行了 ...

  2. Linux tty驱动架构

    Linux tty子系统包含:tty核心,tty线路规程和tty驱动.tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱 ...

  3. linux nandflash驱动之MTD层

    MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口.MTD将文件系统与底层FLASH存储 ...

  4. linux SPI驱动——spi core(四)

    一: SPI核心,就是指/drivers/spi/目录下spi.c文件中提供给其他文件的函数,首先看下spi核心的初始化函数spi_init(void). 1: static int __init s ...

  5. Smart210学习记录------linux串口驱动

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...

  6. linux串口驱动分析

    linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...

  7. linux串口驱动分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/11946591 硬件资源及描述 s3c2440A 通用异步接收器和发送器(UART)提供了 ...

  8. Linux TTY介绍

    1. TTY介绍 TTY(TeleType)指Linux中的一类终端(Terminal)设备, 是一种字符设备 在Linux中, tty可分为如下几类- 串行端口终端(serial port term ...

  9. Linux设备驱动中的软件架构思想

    目录 更新记录 一.Linux驱动的软件架构 1.1 出发点 1.2 分离思想 1.3 分层思想 二.platform设备驱动 2.1 platform设备 2.2 platform驱动 2.3 pl ...

随机推荐

  1. page = new String(request.getQueryString().getBytes("ISO-8859-1"),"GBK");解决前台传后台乱码问题

    page = new String(request.getQueryString().getBytes("ISO-8859-1"),"GBK");解决前台传后台 ...

  2. Android 获取加速传感器的值,并去除杂音

    1.注册和注销传感器 private void registerSensor() { manager.registerListener(this, manager.getDefaultSensor(S ...

  3. 01 Maven构建的项目中,把.xml等配置文件添加到编译目录

    Maven构建的项目,默认只会把src/main/resources目录下的xml配置文件添加到编译目录. 如果需要把src/main/java目录下的xml配置文件也添加到编译目录,需要在pom.x ...

  4. Oil Skimming HDU - 4185(匹配板题)

    Oil Skimming Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tota ...

  5. set, unordered_set模板类

    template< class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key> ...

  6. 【转】安全加密(一):这些MCU加密方法你都知道吗?

    本文导读 随着物联网和边缘计算的出现,五花八门的MCU也被应用其中,如何保证我们的程序安全和知识产权不受侵犯呢,本文我们将对主流MCU的程序加密进行讲解,希望能够帮助你选择最适合自己应用的微处理器. ...

  7. 学习7__STM32--SPI外设之双机通信---

    <target> # 整透stm32之spi双机通信(包括双机同为stm32,stm32& others) <概念> # 双机通信(全双工) 在主机的MOSI管脚输出1 ...

  8. Libre 6004 「网络流 24 题」圆桌聚餐(网络流,最大流)

    Libre 6004 「网络流 24 题」圆桌聚餐(网络流,最大流) Description 假设有来自n个不同单位的代表参加一次国际会议.每个单位的代表数分别为 ri.会议餐厅共有m张餐桌,每张餐桌 ...

  9. POJ 2387 Til the Cows Come Home (图论,最短路径)

    POJ 2387 Til the Cows Come Home (图论,最短路径) Description Bessie is out in the field and wants to get ba ...

  10. Python 基础数据类型之set

    set是一个无序且不重复的元素集合,相当于字典的键,不重复,不可变 一.set变量初始化 A = set() #注意在创建空集合的时候只能使用s=set(),因为s={}创建的是空字典 B = {&q ...