转自: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. 团队项目:安卓端用百度地图api定位显示跑道

    因为安卓调用api对我来说是一个完全陌生的领域,我在经过很长时间终于弄出来了,这段时间还是很有成效的,我得到了历练. 第一步:注册成为百度开发者 在百度地图开放平台创建应用.地址http://lbsy ...

  2. HDU 2051 Bitset

    http://acm.hdu.edu.cn/showproblem.php?pid=2051 Problem Description Give you a number on base ten,you ...

  3. Jenkins配置匿名用户拥有只读权限

    场景:查看cucumber reporting测试报告时需要登陆,比较麻烦 解决:允许匿名用户拥有只读权限 操作:Jenkins->系统管理->全局安全配置->授权策略,勾选“All ...

  4. JSP生成静态Html页面

    [转载]JSP生成静态Html页面 在网站项目中,为了访问速度加快,为了方便百度爬虫抓取网页的内容,需要把jsp的动态页面转为html静态页面.通常有2种常用的方式: 1.伪静态,使用URL Rewr ...

  5. 详细理解servlet实现的三种方式和生命周期

    阅读目录 开发servlet的三种方式 理解实现servlet接口的方式,理解servlet生命周期 Servlet接口有五个方法 继承GenericServlet 继承HttpServlet 现在很 ...

  6. 数位DP复习小结

    转载请注明原文地址http://www.cnblogs.com/LadyLex/p/8490222.html 之前学数位dp的时候底子没打扎实 虚的要死 这次正好有时间……刷了刷之前没做的题目 感觉自 ...

  7. MT【88】抽象函数

    分析:此类题一般有两种做法,第一种按解答题做法, 第二种作为填空题找对应的特殊函数,比如这里可以根据三角里和差化积得出$f(x)=\frac{1}{2}cos(\frac{\pi}{3}x)$

  8. 【BZOJ1293】[SCOI2009]生日礼物(单调队列)

    [BZOJ1293][SCOI2009]生日礼物(单调队列) 题面 BZOJ 洛谷 题解 离散之后随便拿单调队列维护一下就好了. #include<iostream> #include&l ...

  9. [JSOI2007]文本生成器(AC自动机,DP)

    题目链接: 洛谷 LOJ BZOJ 题目大意:给定 $n$ 个只含大写字母的串(称为可读串),问有多少种只含大写字母的长为 $m$ 的串,包含至少一个可读串. $1\le n\le 60,1\le \ ...

  10. psutil库

    psutil是一个非常强大的第三方库,用法简单,这里主要是做一下梳理. 先看看官方说明: psutil (python system and process utilities) is a cross ...