转自: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. java计算器实验报告

    一.实验目的 1.熟悉java图形用户界面的设计原理和程序结构 2.能设计复核问题要求的图形用户界面程序 3.熟悉java awt和swing的组合 4.掌握常用组建的事件借口 5.会应用awt和sw ...

  2. Vue 和 angular

    vue适合移动端的项目,而angular更适合运用于Pc端的项目.

  3. PAT 1048 数字加密

    https://pintia.cn/problem-sets/994805260223102976/problems/994805276438282240 本题要求实现一种数字加密方法.首先固定一个加 ...

  4. 原生 js 封装get ,post, delete 请求

    现在的项目中都在用VUE 以及react 等MVC, MVVM  框架. 丢弃了原始的JQ .不可能为了个$.ajax();而把JQ引进来吧. 在vue1的开发中 提供了 vueResouce, vu ...

  5. Linux命令(一) pwd ,cd

    1.pwd命令 以绝对路径的方式显示当前所处的工作目录,从根目录 / 开始,每一级目录用 / 分隔.第一个 / 表示根目录,最后一个目录是当前目录.当不知道当前处于哪个目录的时候,使用 pwd 命令就 ...

  6. vue组件间的数据和方法传递

    方法 1.父组件调用子组件:ref(在子组件中加上ref即可通过this.$refs.ref.method调用) 2.子组件调用父组件:emit(this.$emit(调用的方法名,传递的参数)) 数 ...

  7. 深入理解Java反射+动态代理

    答:   反射机制的定义: 是在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为j ...

  8. 【题解】 [ZJOI2008] 泡泡堂(贪心/二分图/动态规划)

    懒得复制,戳我戳我 Solution: 就是有一个贪心策略:(以下假设使\(A\)队分数更高) \(First:\)比较两个分值的最小值,如果\(A\)最小分比\(B\)最小分大就直接比较两个最小的, ...

  9. 【题解】 [SCOI2010]连续攻击游戏 (二分图匹配)

    原题目戳我 Solution: 方法很巧妙,我们把每个装备的属性 与 装备编号连起来 从1-10000跑二分图,如果出现断层,就退出,输出答案就好. memset清理bool快一点,int洛谷上超时了 ...

  10. 第一天进入博客这个神奇的领域 在此%%%erosun

    第一条博客水一水    等会要找一下调博客模板 独立动手,丰衣足食