
Uart体系结构
UART设备驱动可以使用tty驱动的框架来实现,但是因为串口之间有共性,所以Linux在tty接口上封装了一层(serial core)。后面我们再拿一篇文章来解释tty驱动,tty其实就是各种终端设备,串口其实也是终端设备。
驱动工程师没必要关心上层的流程,只需注册一个uart_driver,并按硬件规范将对应接口函数完成就可以了。

框图来自:< Linux设备驱动开发详解>
上图我们只需要实现xxx_uart.c , 而我们实现所需要的结构体和函数接口就是由serial_core.c提供。接下来我们来看一下对应的结构体和接口函数。
重要结构体
内核版本:4.20.12
uart_driver
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
//描述一个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
//物理硬件的所有操作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 *); //停止txvoid (*start_tx)(struct uart_port *);//启动txvoid (*throttle)(struct uart_port *);//通知串口驱动,线路规程输入缓冲区接近满了void (*unthrottle)(struct uart_port *);//通知串口驱动可以将字符发送到线路规程输入缓冲区void (*send_xchar)(struct uart_port *, char ch); //传输高优先级字符,即使端口已停止。void (*stop_rx)(struct uart_port *); //停止Rxvoid (*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,复位DMAvoid (*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_POLLint (*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函数
//注册/注销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, 串口对于芯片而言,也是一个控制器。
分析一大堆代码是不是看着很累,所以千万别全部看,挑重点看,理清思路即可。
往期推荐

长按识别图中二维码关注




