zoukankan      html  css  js  c++  java
  • Linux UART介绍

    1. UART介绍

    UART是一类tty设备, 是一种串行端口终端, 具体可参考<UART接口介绍>
    在Linux中UART属于tty驱动的一部分, 具体实现包括驱动抽象层和硬件实现层

    本文主要介绍了UART驱动抽象层, 代码主要是drivers/tty/serial/serial_core.c

    2. UART接口

    UART抽象层提供了一系列API供硬件实现层使用, 主要包括

    /* 注册/释放uart驱动 */
    int uart_register_driver(struct uart_driver *drv);
    void uart_unregister_driver(struct uart_driver *drv);
    /* 添加uart端口/设备 */
    int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport);
    int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport);
    /* 端口挂起和恢复 */
    int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport);
    int uart_resume_port(struct uart_driver *drv, struct uart_port *uport);
    /* 读写相关 */
    void uart_write_wakeup(struct uart_port *port);
    void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag);

    uart_register_driver完成了如下事宜
    1. 为uart_driver分配uart_driver::nr个uart_state数据结构
    2. 调用alloc_tty_driver分配tty_driver数据结构并赋值给uart_driver::tty_driver
    3. 从uart_driver对应变量赋值tty_driver的driver_name、name、major、minor_start成员
    4. 设置tty_driver::type为TTY_DRIVER_TYPE_SERIAL, tty_driver::subtype为SERIAL_TYPE_NORMAL
    5. 设置tty_driver::init_termios为tty_std_termios, 并定制init_termios::c_cflag和init_termios::c_ispeed
    6. 设置tty_driver::flags为TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV
    7. 设置tty_driver::driver_state指向uart私有数据uart_driver
    8. 调用tty_set_operations将uart_ops赋值给tty_driver::ops
    9. 调用tty_port_init初始化uart_driver::uart_state::tty_port
    9.1 调用tty_buffer_init初始化tty_port::buf, 以及buf关联的工作队列tty_bufhead::work, 工作函数为flush_to_ldisc
    9.2 初始化tty_port::open_wait和tty_port::delta_msr_wait等待队列
    9.3 初始化tty_port::mutex和tty_port::buf_mutex互斥锁
    9.4 初始化tty_port::close_delay为50、tty_port::closing_wait为3000, 为什么?
    10. 将uart_port_ops赋值给uart_driver::uart_state::tty_port::tty_port_operations
    11. 调用tty_register_driver注册tty驱动

    uart_add_one_port完成了如下事宜
    1. 将参数uport赋值给uart_driver::uart_state::uart_port, 将uart_driver::uart_state赋值给uport::uart_state
    2. 将uart_driver::console赋值给uport::console
    3. 获取uart端口的次设备号并赋值给uport::minor
    4. 调用uart_configure_port进行uart端口配置
    4.1 首先判断如果该端口没有配置物理信息则直接返回
    4.2 如果uport::flags包含了UPF_BOOT_AUTOCONF, 则调用uport::uart_ops::config_port进行端口自动配置
    4.3 通过uart_report_port来向内核打印uart端口相关信息
    4.4 调用uart_change_pm给端口上电, 实际是调用uport::uart_ops::pm
    4.5 调用uport::uart_ops::set_mctrl禁用串口控制
    4.6 如果该端口关联控制台, 则调用register_console来注册控制台
    4.7 如果该端口不是控制台端口, 则调用uart_change_pm给端口断电
    5. 为uport::tty_groups分配2/3个数据结构并赋值, [0]为tty_dev_attr_group; [1]为uport::attr_group
    6. 调用tty_port_register_device_attr注册tty设备

    3. UART数据结构

    UART抽象层包含如下几个重要数据结构

    uart_driver是uart的私有驱动结构, 包括一些和tty_driver相同的变量和uart相关的变量
    一个uart_driver通常对应多个uart_state, 一个uart_state对应一个uart_port, 一个uart_port对应一个串口设备

    struct uart_driver {
        struct module     *owner;
        const char        *driver_name;        /* 驱动名称, 如serial */
        const char        *dev_name;           /* 设备名称, 如ttyS, 体现到/dev/文件系统下 */ 
        int               major;               /* 主设备号, 如TTY_MAJOR*/
    int minor; /* 次设备号 */ int nr; /* 串口设备数 */ struct console *cons; /* 控制台设备 */ struct uart_state *state; /* 串口状态 */ struct tty_driver *tty_driver; /* tty驱动 */ };

    uart_port用于描述串口端口的物理信息, 如I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等

    struct uart_port {
        spinlock_t               lock;              /* port lock */
        unsigned long            iobase;            /* io端口基地址 */
        unsigned char __iomem    *membase;          /* 内存端口基地址 */
        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;                    /* 中断号 */
        unsigned long      irqflags;               /* 中断标志 */
        unsigned int       uartclk;                /* 串口时钟 */
        unsigned int       fifosize;               /* fifo大小 */
        unsigned char      x_char;                 /* xon/xoff char */
        unsigned char      regshift;               /* 寄存器偏移值 */
        unsigned char      iotype;                 /* io访问类型 */
        unsigned char      unused1;
    
        unsigned int         read_status_mask;    /* driver specific */
        unsigned int         ignore_status_mask;  /* driver specific */
        struct uart_state    *state;              /* 指向对用uart_state */
        struct uart_icount   icount;              /* 串口使用计数 */
    
        struct console       *cons;               /* 控制台设备 */
    #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
        unsigned long        sysrq;               /* sysrq timeout */
    #endif
        upf_t                flags;
        upstat_t             status;
        int                  hw_stopped;          /* sw-assisted CTS flow state */
        unsigned int         mctrl;               /* 当前的串口控制设置 */
        unsigned int         timeout;             /* character-based timeout */
        unsigned int         type;                /* 端口类型 */
        const struct uart_ops    *ops;            /* 串口操作函数集 */
        unsigned int         custom_divisor;
        unsigned int         line;                /* 端口号 */
        unsigned int         minor;
        resource_size_t      mapbase;             /* 串口寄存器基地址 */
        resource_size_t      mapsize;
        struct device        *dev;                /* 父设备 */
        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;           /* 端口私有数据, 一般为platform数据指针 */
    };

    uart_state代表uart设备的状态

    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;
    };

    uart_ops是uart端口操作集, 与硬件相关

    struct uart_ops {
        unsigned int (*tx_empty)(struct uart_port *);              /* 发送TX FIFO缓冲区是否为空 */
        void         (*set_mctrl)(struct uart_port *, unsigned int mctrl);  /* 设置串口控制模式 */
        unsigned int (*get_mctrl)(struct uart_port *);             /* 获取串口控制状态 */
        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);   /* XON/XOFF发送函数 */
        void         (*stop_rx)(struct uart_port *);               /* 停止接收 */
        void         (*enable_ms)(struct uart_port *);             /* 串口状态使能使能 */
        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);     /* 电源管理 */ 
        void         (*wake_peer)(struct uart_port *);             /**/
    
        const char   *(*type)(struct uart_port *);                 /* 端口描述符 */
        void         (*release_port)(struct uart_port *);          /* 释放端口物理资源 */
        int          (*request_port)(struct uart_port *);          /* 申请端口物理资源 */
        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
    };

    4. UART驱动编写

    uart驱动的编写主要步骤如下:

    1. 定义uart_driver静态变量, 实现driver_name、dev_name、nr、cons、major、minor等
    2. 定义并实现uart_ops和console数据结构
    3. 根据具体的硬件进行实现uart_port数据结构, 或者从DTS获取对应的数据
    3. 调用uart_register_driver注册uart驱动
    4. 调用uart_add_one_port添加uart端口

    参考:
    <tty驱动分析>
    <Linux串口(serial、uart)驱动程序设计>

  • 相关阅读:
    java工程师要求
    系统架构设计师知识模块
    Mybatis使用训练
    项目—视频直播系统
    [数算]概率
    查看镜像文件
    Hadoop启动命令
    Hadoop启动命令
    HDFS设置配额管理
    HDFS设置配额管理
  • 原文地址:https://www.cnblogs.com/hzl6255/p/9535271.html
Copyright © 2011-2022 走看看