linux uart驱动
注:该文档对应的linux版本为linux 4.9,以arm AMBA-PL011的uart驱动为例
参考文章:https://blog.csdn.net/lizuobin2/article/details/51773305?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169267158416800192272058%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=169267158416800192272058&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-51773305-null-null.268^v1^koosearch&utm_term=tty&spm=1018.2226.3001.4450
一、uart驱动数据结构及软件分层
1.1 uart软件分层
uart驱动总体可分为三层,用户层提供/dev/ttyS*接口用于读写uart数据。内核层分为tty、uart两部分,uart又分为uart 核心和uart控制器驱动,uart核心体用用于注册uart驱动的接口,uart驱动则负责操作uart控制器。硬件层就是soc上的uart控制器。
1.2 uart数据结构
struct uart_port: 代表一个串口
struct uart_ops: 串口的操作函数
struct uart_state: 里面包含着uart_port和tty_port
struct uart_driver: 用于注册uart的结构体,所以控制器共用一个uart_driver
struct uart_driver { struct module *owner; /* 拥有该uart_driver的模块,一般为THIS_MODULE */ const char *driver_name; /* 串口驱动名,串口设备文件名以驱动名为基础 */ const char *dev_name; /* 串口设备名 */ int major; /* 主设备号 */ int minor; /* 次设备号 */ int nr; /* soc的串口控制器数量 */ struct console *cons; /* 其对应的console.若该uart_driver支持serial console,否则为NULL */ /* 下面这俩,它们应该被初始化为NULL */ struct uart_state *state; /* 这里会一次申请nr个state */ struct tty_driver *tty_driver; /* tty相关 */ };
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; };
struct uart_port { spinlock_t lock; /* port lock */ unsigned long iobase; /* io端口基地址(物理) */ unsigned char __iomem *membase; /* 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; /* 中断号 */ unsigned long irqflags; /* 中断标志 */ unsigned int uartclk; /* 串口时钟*/ unsigned int fifosize; /* 串口缓冲区大小 */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* 寄存器位移 */ unsigned char iotype; /* 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; /* 关心 Rx error status */ unsigned int ignore_status_mask; /* 忽略 Rx error status */ struct uart_state *state; /* pointer to parent state */ struct uart_icount icount; /* 串口信息计数器 */ 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; /*
...
这里省略一些宏定义
...
*/ int hw_stopped; /* sw-assisted CTS flow state */ unsigned int mctrl; /* 当前的Moden 设置 */ 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; /* o内存物理基地址 */ 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 */ };
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 (*send_xchar)(struct uart_port *, char ch); /* 发送xChar */ 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 *);/* 设置线路规程 */ void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); /* 串口电源管理 */ int (*set_wake)(struct uart_port *, unsigned int state); /* * 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 void (*poll_put_char)(struct uart_port *, unsigned char); int (*poll_get_char)(struct uart_port *); #endif };
1.3 uart core层提供的接口函数
在include/linux/serial_core.h中主要提供了以下几个注册uart的接口函数
int uart_register_driver(struct uart_driver *uart); //注册一个串口驱动
void uart_unregister_driver(struct uart_driver *uart); //注销一个串口驱动
int uart_add_one_port(struct uart_driver *reg, struct uart_port *port); //给驱动添加一个uart_port
int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port); //移除uart_port
二、uart驱动加载
amba-pl011对应的uart驱动文件为 drivers/tty/serial/amba-pl011.c,然而,该文件中的compatible与设备树中的并不匹配。因为这里不使用compatible匹配,而是使用amba_id匹配,所以串口的初始化入口函数为pl011_probe
这里有一个描述amba-pl011的结构体
struct uart_amba_port { struct uart_port port; const u16 *reg_offset; struct clk *clk; const struct vendor_data *vendor; unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ unsigned int old_status; unsigned int fifosize; /* vendor-specific */ unsigned int old_cr; /* state during shutdown */ bool autorts; unsigned int fixed_baud; /* vendor-set fixed baud rate */ char type[12]; #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ bool using_tx_dma; bool using_rx_dma; struct pl011_dmarx_data dmarx; struct pl011_dmatx_data dmatx; bool dma_probed; #endif };
这个pl011_probe函数比较简单,就是填充一个这个结构体,最后调用uart_add_one_port函数注册这个uart驱动。其中最重要的就是填充struct uart_port 这个成员,提供uart操作函数。