Uart体系结构
UART设备驱动可以使用tty驱动的框架来实现,但是因为串口之间有共性,所以Linux在tty接口上封装了一层(serial core)。后面我们再拿一篇文章来解释tty驱动,tty其实就是各种终端设备,串口其实也是终端设备。
驱动工程师没必要关心上层的流程,只需注册一个uart_driver,并按硬件规范将对应接口函数完成就可以了。
上图我们只需要实现xxx_uart.c , 而我们实现所需要的结构体和函数接口就是由serial_core.c提供。接下来我们来看一下对应的结构体和接口函数。
重要结构体
内核版本:4.20.12
- uart_driver
1 2 3 4 5 6 7 8 9 10 11 12 13 | struct uart_driver { struct module *owner; const char *driver_name; const char *dev_name; //设备名,即dev下的节点名 int major; int minor; int nr; struct console *cons;//console配置,串口作为console时才需要 //私有的,底层驱动把它初始化为NULL即可 struct uart_state *state; struct tty_driver *tty_driver; }; |
串口设备也是字符设备,所以看到很多字符设备相关的,console就是控制台,我们平常所使用的debug口就是console。
- uart_port
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | //描述一个UART端口 struct uart_port { spinlock_t lock; /* port lock */ unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ 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); void (*set_ldisc)(struct uart_port *, struct ktermios *); unsigned int (*get_mctrl)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int); unsigned int (*get_divisor)(struct uart_port *, unsigned int baud, unsigned int *frac); void (*set_divisor)(struct uart_port *, unsigned int baud, unsigned int quot, unsigned int quot_frac); 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 *); //485配置 int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485); int (*iso7816_config)(struct uart_port *, struct serial_iso7816 *iso7816); unsigned int irq; /* 中断号 */ unsigned long irqflags; /* 中断标志 */ unsigned int uartclk; /* 串口时钟 */ 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 */ unsigned char quirks; /* internal quirks */ //省略宏定义.... 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; //省略宏定义.... upstat_t status; //省略宏定义.... int hw_stopped; /* sw-assisted CTS flow state */ unsigned int mctrl; /* current modem ctrl settings */ 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 */ resource_size_t mapsize; struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; unsigned char unused[2]; const char *name; /* port name */ struct attribute_group *attr_group; /* port specific attributes */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ struct serial_rs485 rs485; struct serial_iso7816 iso7816; void *private_data; /* generic platform data pointer */ }; |
uart_port用于描述一个UART端口的I/O端口或I/O内存地址、FIFO大小、端口类型等信息。这个结构体参数很多,还有很多对串口进行配置的函数。
- uart_ops
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | //物理硬件的所有操作 struct uart_ops { //一些操作函数 unsigned int (*tx_empty)(struct uart_port *);//判断发送FIFO是否为空 void (*set_mctrl)(struct uart_port *, unsigned int mctrl); //设置控制信息 unsigned int (*get_mctrl)(struct uart_port *); //获取当前控制信息 void (*stop_tx)(struct uart_port *); //停止tx void (*start_tx)(struct uart_port *);//启动tx void (*throttle)(struct uart_port *);//通知串口驱动,线路规程输入缓冲区接近满了 void (*unthrottle)(struct uart_port *);//通知串口驱动可以将字符发送到线路规程输入缓冲区 void (*send_xchar)(struct uart_port *, char ch); //传输高优先级字符,即使端口已停止。 void (*stop_rx)(struct uart_port *); //停止Rx void (*enable_ms)(struct uart_port *); //使能modem状态中断 void (*break_ctl)(struct uart_port *, int ctl); //控制中断信号的传输 int (*startup)(struct uart_port *); //启动串口 void (*shutdown)(struct uart_port *); //关闭串口 void (*flush_buffer)(struct uart_port *); //刷新写buffer,复位DMA 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); //电源管理 //返回一个描述串口类型的字符串 const char *(*type)(struct uart_port *); //释放IO和内存资源 void (*release_port)(struct uart_port *); //申请IO和内存资源 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 }; |
- uart_driver是对tty_driver的封装,uart_driver和platform_driver还是有区别的,因为它并没有probe回调函数。它主要是一些字符设备的信息。
- uart_port用来描述具体的串口,主要是一些串口参数。
- uart_ops就是一些串口的操作函数,和字符设备中的file_operations差不多。
API函数
1 2 3 4 5 6 7 8 9 | //注册/注销uart_driver到串口核心层 int uart_register_driver(struct uart_driver *drv) void uart_unregister_driver(struct uart_driver *drv) //关联具体串口和驱动 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) |
我们使用到的接口函数很少,所以其实蛮简单的,Linux封装完之后就是填充结构体,然后调用接口注册一下。
总结
首先我们要清楚,在底层,Uart驱动是为每个port都分配了缓存空间的。所以应用层读取的都是缓存空间中的。然后uart_driver不能和platform_driver混淆。后面我们分析实例时会发现Uart的驱动是由platform_driver来回调probe的。之前说过,控制器都是使用platform_driver, 串口对于芯片而言,也是一个控制器。
分析一大堆代码是不是看着很累,所以千万别全部看,挑重点看,理清思路即可。