设备驱动中的tty(kernel-4.7)

来源:互联网 发布:ocr软件手机版 编辑:程序博客网 时间:2024/04/29 08:54

TTY概念解析

在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备。

• 串口终端(/dev/ttyS*)
串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看作是一个字符设备。这些串行端口所对应的设备名称是 /dev/ttySAC0;/dev/ttySAC1……

• 控制台终端(/dev/console)
在Linux系统中,计算机的输出设备通常被称为控制台终端(Console),这里特指printk信息输出到的设备。/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数” console=ttySAC0”就把console映射到了串口0,是一种虚拟的设备,可以和屏幕或者串口关联

• 虚拟终端(/dev/tty*)
当用户登录时,使用的是虚拟终端。使用Ctcl+Alt+[F1—F6]组合键时,我们就可以切换到tty1、tty2、tty3等上面去。tty1–tty6等称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名.

TTY架构分析

这里写图片描述
Linux tty子系统包含:tty核心,tty线路规程和tty驱动。tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱动。
下图详细的描述了tty子系统之间的互相调用关系,即用户应用层 –> 线路规划层 –> TTY层 –> 底层驱动层 –> 物理硬件层。
这里写图片描述

串口驱动调用关系分析

串口驱动重要结构和对应的驱动调用层次:
这里写图片描述

整个 uart 框架大概的样子如上图所示,简单来分的话可以说成两层,一层是下层我们的串口驱动层,它直接与硬件相接触,我们需要填充一个 struct uart_ops的结构体,另一层是上层 tty 层,包括 tty 核心以及线路规程,它们各自都有一个 Ops 结构,用户空通过间是 tty 注册的字符设备节点来访问,这么说来如上图所示涉及到了4个 ops 结构了,层层跳转。下面,就来分析分析它们的层次结构。

在 s3c2440 平台,它是这样来注册串口驱动的,分配一个struct uart_driver简单填充,并调用uart_register_driver注册到内核中去。

static struct uart_driver s3c24xx_uart_drv = {    .owner      = THIS_MODULE,    .driver_name    = "s3c2410_serial",    .nr     = CONFIG_SERIAL_SAMSUNG_UARTS,    .cons       = S3C24XX_SERIAL_CONSOLE,    .dev_name   = S3C24XX_SERIAL_NAME,    .major      = S3C24XX_SERIAL_MAJOR,    .minor      = S3C24XX_SERIAL_MINOR,};

uart_driver中,只是填充了一些名字、设备号等信息,这些都是不涉及底层硬件访问的,来看一下完整的 uart_driver 结构,在include/linux/serial_core.h中:

struct uart_driver {    struct module       *owner;/* 拥有该uart_driver的模块,一般为THIS_MODULE */      const char      *driver_name;/* 串口驱动名,串口设备文件名以驱动名为基础 */    const char      *dev_name;  /* 串口设备名 */    int          major; /* 主设备号 */     int          minor; /* 次设备号 */    int          nr;  /* 该uart_driver支持的串口个数(最大) */      struct console      *cons; /* 其对应的console.若该uart_driver支持serial console,否则为NULL */      /*     * these are private; the low level driver should not     * touch these; they should be initialised to NULL     */      /* 下面这俩,它们应该被初始化为NULL */     struct uart_state   *state; /* 下层,串口驱动层 */    struct tty_driver   *tty_driver;  /* tty相关 */  };

上边填充的结构体中,有两个成员未被赋值,对于tty_driver代表的是上层,它会在 register_uart_driver 中的过程中赋值,而uart_state则代表下层,uart_state也会在register_uart_driver的过程中分配空间,但是它里面真正设置硬件相关的东西是 uart_state->uart_port,这个uart_port是需要我们从其它地方调用 uart_add_one_port 来添加的。

在串口驱动层首先,需要认识这几个结构体:

/* * This is the state information which is persistent across opens. */struct uart_state {    struct tty_port     port;    enum uart_pm_state  pm_state;    struct circ_buf     xmit;    atomic_t        refcount;    wait_queue_head_t   remove_wait;    struct uart_port    *uart_port; //对应于一个串口设备 };

在注册 driver 时,会根据 uart_driver->nr来申请 nr 个 uart_state空间,用来存放驱动所支持的串口(端口)的物理信息

struct uart_port {    spinlock_t      lock;           /* port lock */    unsigned long       iobase; /* in/out[bwl] *//* io端口基地址(物理) */     unsigned char __iomem   *membase;/* read/write[bwl] *//* io内存基地址(虚拟) */     unsigned int        (*serial_in)(struct uart_port *, int);    void            (*serial_out)(struct uart_port *, int, int);    void            (*set_termios)(struct uart_port *,                               struct ktermios *new,                               struct ktermios *old);    unsigned int        (*get_mctrl)(struct uart_port *);    void            (*set_mctrl)(struct uart_port *, unsigned int);    int         (*startup)(struct uart_port *port);    void            (*shutdown)(struct uart_port *port);    void            (*throttle)(struct uart_port *port);    void            (*unthrottle)(struct uart_port *port);    int         (*handle_irq)(struct uart_port *);    void            (*pm)(struct uart_port *, unsigned int state,                      unsigned int old);    void            (*handle_break)(struct uart_port *);    int         (*rs485_config)(struct uart_port *,                        struct serial_rs485 *rs485);    unsigned int        irq;            /* irq number */* 中断号 */      unsigned long       irqflags;       /* irq flags  *//* 中断标志  */    unsigned int        uartclk;        /* base uart clock *//* 串口时钟 */      unsigned int        fifosize;       /* tx fifo size *//* 串口缓冲区大小 */      unsigned char       x_char;         /* xon/xoff char */    unsigned char       regshift;       /* reg offset shift */ /* 寄存器位移 */      unsigned char       iotype;         /* io access style *//* IO访问方式 */      unsigned char       unused1;#define UPIO_PORT       (SERIAL_IO_PORT)    /* 8b I/O port access */#define UPIO_HUB6       (SERIAL_IO_HUB6)    /* Hub6 ISA card */#define UPIO_MEM        (SERIAL_IO_MEM)     /* driver-specific */#define UPIO_MEM32      (SERIAL_IO_MEM32)   /* 32b little endian */#define UPIO_AU         (SERIAL_IO_AU)      /* Au1x00 and RT288x type IO */#define UPIO_TSI        (SERIAL_IO_TSI)     /* Tsi108/109 type IO */#define UPIO_MEM32BE        (SERIAL_IO_MEM32BE) /* 32b big endian */#define UPIO_MEM16      (SERIAL_IO_MEM16)   /* 16b little endian */    unsigned int        read_status_mask;   /* driver specific */    unsigned int        ignore_status_mask; /* driver specific */    struct uart_state   *state;         /* pointer to parent state */    struct uart_icount  icount;         /* statistics */ /* 串口信息计数器 */    struct console      *cons;          /* struct console, if any */#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)    unsigned long       sysrq;          /* sysrq timeout */#endif    /* flags must be updated while holding port mutex */    upf_t           flags;    /*     * These flags must be equivalent to the flags defined in     * include/uapi/linux/tty_flags.h which are the userspace definitions     * assigned from the serial_struct flags in uart_set_info()     * [for bit definitions in the UPF_CHANGE_MASK]     *     * Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable     * except bit 15 (UPF_NO_TXEN_TEST) which is masked off.     * The remaining bits are serial-core specific and not modifiable by     * userspace.     */#define UPF_FOURPORT        ((__force upf_t) ASYNC_FOURPORT       /* 1  */ )#define UPF_SAK         ((__force upf_t) ASYNC_SAK            /* 2  */ )#define UPF_SPD_HI      ((__force upf_t) ASYNC_SPD_HI         /* 4  */ )#define UPF_SPD_VHI     ((__force upf_t) ASYNC_SPD_VHI        /* 5  */ )#define UPF_SPD_CUST        ((__force upf_t) ASYNC_SPD_CUST   /* 0x0030 */ )#define UPF_SPD_WARP        ((__force upf_t) ASYNC_SPD_WARP   /* 0x1010 */ )#define UPF_SPD_MASK        ((__force upf_t) ASYNC_SPD_MASK   /* 0x1030 */ )#define UPF_SKIP_TEST       ((__force upf_t) ASYNC_SKIP_TEST      /* 6  */ )#define UPF_AUTO_IRQ        ((__force upf_t) ASYNC_AUTO_IRQ       /* 7  */ )#define UPF_HARDPPS_CD      ((__force upf_t) ASYNC_HARDPPS_CD     /* 11 */ )#define UPF_SPD_SHI     ((__force upf_t) ASYNC_SPD_SHI        /* 12 */ )#define UPF_LOW_LATENCY     ((__force upf_t) ASYNC_LOW_LATENCY    /* 13 */ )#define UPF_BUGGY_UART      ((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )#define UPF_NO_TXEN_TEST    ((__force upf_t) (1 << 15))#define UPF_MAGIC_MULTIPLIER    ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )/* Port has hardware-assisted h/w flow control */#define UPF_AUTO_CTS        ((__force upf_t) (1 << 20))#define UPF_AUTO_RTS        ((__force upf_t) (1 << 21))#define UPF_HARD_FLOW       ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))/* Port has hardware-assisted s/w flow control */#define UPF_SOFT_FLOW       ((__force upf_t) (1 << 22))#define UPF_CONS_FLOW       ((__force upf_t) (1 << 23))#define UPF_SHARE_IRQ       ((__force upf_t) (1 << 24))#define UPF_EXAR_EFR        ((__force upf_t) (1 << 25))#define UPF_BUG_THRE        ((__force upf_t) (1 << 26))/* The exact UART type is known and should not be probed.  */#define UPF_FIXED_TYPE      ((__force upf_t) (1 << 27))#define UPF_BOOT_AUTOCONF   ((__force upf_t) (1 << 28))#define UPF_FIXED_PORT      ((__force upf_t) (1 << 29))#define UPF_DEAD        ((__force upf_t) (1 << 30))#define UPF_IOREMAP     ((__force upf_t) (1 << 31))#define __UPF_CHANGE_MASK   0x17fff#define UPF_CHANGE_MASK     ((__force upf_t) __UPF_CHANGE_MASK)#define UPF_USR_MASK        ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))#if __UPF_CHANGE_MASK > ASYNC_FLAGS#error Change mask not equivalent to userspace-visible bit defines#endif    /*     * Must hold termios_rwsem, port mutex and port lock to change;     * can hold any one lock to read.     */    upstat_t        status;#define UPSTAT_CTS_ENABLE   ((__force upstat_t) (1 << 0))#define UPSTAT_DCD_ENABLE   ((__force upstat_t) (1 << 1))#define UPSTAT_AUTORTS      ((__force upstat_t) (1 << 2))#define UPSTAT_AUTOCTS      ((__force upstat_t) (1 << 3))#define UPSTAT_AUTOXOFF     ((__force upstat_t) (1 << 4))    int         hw_stopped;     /* sw-assisted CTS flow state */    unsigned int        mctrl;  /* current modem ctrl settings *//* 当前的Moden 设置 */      unsigned int        timeout;/* character-based timeout */    unsigned int        type;   /* port type */  /* 端口类型 */      const struct uart_ops   *ops;  /* 串口端口操作函数 */      unsigned int        custom_divisor;    unsigned int        line;           /* port index *//* 端口索引 */      unsigned int        minor;    resource_size_t     mapbase;        /* for ioremap */  /* io内存物理基地址 */     resource_size_t     mapsize;    struct device       *dev;           /* parent device */    unsigned char       hub6;           /* this should be in the 8250 driver */    unsigned char       suspended;    unsigned char       irq_wake;    unsigned char       unused[2];    struct attribute_group  *attr_group;        /* port specific attributes */    const struct attribute_group **tty_groups;  /* all attributes (serial core use only) */    struct serial_rs485     rs485;    void            *private_data;      /* generic platform data pointer */};

这个结构体,是需要我们自己来填充的,例如 s3c2440 有3个串口,那么就需要填充3个 uart_port,并且通过 uart_add_one_port 添加到 uart_driver->uart_state->uart_port中去。当然uart_driver 有多个 uart_state,每个 uart_state有一个uart_port。在uart_port里还有一个非常重要的成员struct uart_ops *ops ,这个也是需要我们自己来实现的,一般芯片厂家都写好了或者只需要稍作修改。

struct uart_port;struct serial_struct;struct device;/* * This structure describes all the operations that can be done on the * physical hardware.  See Documentation/serial/driver for details. */struct uart_ops {    unsigned int    (*tx_empty)(struct uart_port *); /* 串口的Tx FIFO缓存是否为空 */      void        (*set_mctrl)(struct uart_port *, unsigned int mctrl); /* 设置串口modem控制 */      unsigned int    (*get_mctrl)(struct uart_port *);  /* 获取串口modem控制 */    void        (*stop_tx)(struct uart_port *); /* 禁止串口发送数据 */     void        (*start_tx)(struct uart_port *);/* 使能串口发送数据 */    void        (*throttle)(struct uart_port *);    void        (*unthrottle)(struct uart_port *);    void        (*send_xchar)(struct uart_port *, char ch);    void        (*stop_rx)(struct uart_port *);  /* 禁止串口接收数据 */     void        (*enable_ms)(struct uart_port *);/* 使能modem的状态信号 */     void        (*break_ctl)(struct uart_port *, int ctl);/* 设置break信号 */      int     (*startup)(struct uart_port *); /* 启动串口,应用程序打开串口设备文件时,该函数会被调用 */    void        (*shutdown)(struct uart_port *);/* 关闭串口,应用程序关闭串口设备文件时,该函数会被调用 */      void        (*flush_buffer)(struct uart_port *);    void        (*set_termios)(struct uart_port *, struct ktermios *new,                       struct ktermios *old); /* 设置串口参数 */     void        (*set_ldisc)(struct uart_port *, struct ktermios *);/* 设置线路规程 */      void        (*pm)(struct uart_port *, unsigned int state,                  unsigned int oldstate);/* 串口电源管理 */      /*     * Return a string describing the type of the port     */    const char  *(*type)(struct uart_port *);    /*     * Release IO and memory resources used by the port.     * This includes iounmap if necessary.     */    void        (*release_port)(struct uart_port *);    /*     * Request IO and memory resources used by the port.     * This includes iomapping the port if necessary.     */    int     (*request_port)(struct uart_port *);/* 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口 */    void        (*config_port)(struct uart_port *, int);/* 执行串口所需的自动配置 */      int     (*verify_port)(struct uart_port *, struct serial_struct *);/* 核实新串口的信息 */     int     (*ioctl)(struct uart_port *, unsigned int, unsigned long);#ifdef CONFIG_CONSOLE_POLL    int     (*poll_init)(struct uart_port *);    void        (*poll_put_char)(struct uart_port *, unsigned char);    int     (*poll_get_char)(struct uart_port *);#endif};

在tty 层要从uart_register_driver来看起了,因为tty_driver是在注册过程中构建的,也就顺便了解了注册过程,该函数在driver/tty/serial/serail_core.c中:

/** *  uart_register_driver - register a driver with the uart core layer *  @drv: low level driver structure * *  Register a uart driver with the core driver.  We in turn register *  with the tty layer, and initialise the core driver per-port state. * *  We have a proc file in /proc/tty/driver which is named after the *  normal driver. * *  drv->port should be NULL, and the per-port structures should be *  registered using uart_add_one_port after this call has succeeded. */int uart_register_driver(struct uart_driver *drv){    struct tty_driver *normal;    int i, retval;    BUG_ON(drv->state);    /*     * Maybe we should be using a slab cache for this, especially if     * we have a large number of ports to handle.     *//* 根据driver支持的最大设备数,申请n个 uart_state 空间,每一个 uart_state 都有一个uart_port */      drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);    if (!drv->state)        goto out;     /* tty层:分配一个 tty_driver ,并将drv->tty_driver 指向它 */     normal = alloc_tty_driver(drv->nr);    if (!normal)        goto out_kfree;    drv->tty_driver = normal;     /* 对 tty_driver 进行设置 */      normal->driver_name = drv->driver_name;    normal->name        = drv->dev_name;    normal->major       = drv->major;    normal->minor_start = drv->minor;    normal->type        = TTY_DRIVER_TYPE_SERIAL;    normal->subtype     = SERIAL_TYPE_NORMAL;    normal->init_termios    = tty_std_termios;    normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;    normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;    normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;    normal->driver_state    = drv;    tty_set_operations(normal, &uart_ops);    /*     * Initialise the UART state(s).     */    for (i = 0; i < drv->nr; i++) {        struct uart_state *state = drv->state + i;        struct tty_port *port = &state->port;        tty_port_init(port);        port->ops = &uart_port_ops;    }     /* tty层:注册 driver->tty_driver */      retval = tty_register_driver(normal);    if (retval >= 0)        return retval;    for (i = 0; i < drv->nr; i++)        tty_port_destroy(&drv->state[i].port);    put_tty_driver(normal);out_kfree:    kfree(drv->state);out:    return -ENOMEM;}

注册过程:
1、根据driver支持的最大设备数,申请n个uart_state空间,每一个uart_state 都有一个 uart_port
2、分配一个tty_driver,并将drv->tty_driver 指向它。
3、对tty_driver 进行设置,其中包括默认波特率、校验方式等,还有一个重要的 opsuart_ops ,它是tty核心与串口驱动通信的接口。
4、注册 tty_driver
注册 uart_driver实际上是注册 tty_driver,因此与用户空间打交道的工作完全交给了tty_driver,而且这一部分都是内核实现好的,我们不需要修改,了解一下工作原理即可。

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,//当termios设置被改变时又tty核心调用    .set_ldisc  = uart_set_ldisc,// 设置线路规程函数      .stop       = uart_stop,    .start      = uart_start,    .hangup     = uart_hangup,// 挂起函数,当驱动挂起tty设备时调用      .break_ctl  = uart_break_ctl, // 线路中断控制函数      .wait_until_sent= uart_wait_until_sent,#ifdef CONFIG_PROC_FS    .proc_fops  = &uart_proc_fops,#endif    .tiocmget   = uart_tiocmget,// 获得当前tty的线路规程的设置      .tiocmset   = uart_tiocmset,// 设置当前tty线路规程的设置     .get_icount = uart_get_icount,#ifdef CONFIG_CONSOLE_POLL    .poll_init  = uart_poll_init,    .poll_get_char  = uart_poll_get_char,    .poll_put_char  = uart_poll_put_char,#endif};

以上是 tty 核心的 Ops ,下面来看tty_driver的注册。

/* * Called by a tty driver to register itself. */int tty_register_driver(struct tty_driver *driver){    int error;    int i;    dev_t dev;    struct device *d;     /* 如果没有主设备号则申请 */    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)        goto err;    if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {        error = tty_cdev_add(driver, dev, 0, driver->num);        if (error)            goto err_unreg_char;    }    mutex_lock(&tty_mutex);       /* 将该 driver->tty_drivers 添加到全局链表 tty_drivers */      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++) {            d = tty_register_device(driver, i, NULL);            if (IS_ERR(d)) {                error = PTR_ERR(d);                goto err_unreg_devs;            }        }    }      /* proc 文件系统注册driver */      proc_tty_register_driver(driver);    driver->flags |= TTY_DRIVER_INSTALLED;    return 0;err_unreg_devs:    for (i--; i >= 0; i--)        tty_unregister_device(driver, i);    mutex_lock(&tty_mutex);    list_del(&driver->tty_drivers);    mutex_unlock(&tty_mutex);err_unreg_char:    unregister_chrdev_region(dev, driver->num);err:    return error;}

调用层次:
这里写图片描述
串口数据读写函数调用流程图:
这里写图片描述
通过对串口write系统调用,进行分析,以此来判断上图是否正确, 系统之间的互相调用关系以s3c2440为例,调用图示如下:
这里写图片描述

tty驱动接口分析

tty driver的所有操作都包含在tty_driver中。内核即供了一个名叫alloc_tty_driver()来分配这个tty_driver。当然 我们也可以在自己的驱动中将它定义成一个静态的结构。对tty_driver进行一些必要的初始化之后,调用tty_register_driver()将其注册.

未完待续。。。。

1 0
原创粉丝点击