在接触嵌入式Linux以来,从上电那一刻开始,我们接触的就是从串口命令行打印的信息;作为U-Boot和Kernel调试打印和交互的接口,串口的重要性不言而喻。从硬件上来说,串口并不复杂,最简单的通讯只需要RX,TX,GND三根线即可;复杂的也就是增加RTS,CTS实现流控,或者增加DIR方向引脚实现RS485通讯。但是从软件的角度来看,串口的管理框架是非常复杂的,涉及到了串口驱动、tty设备注册,console终端管理,理解起来就相当复杂了;这也是如此靠后去讲解tty serial框架的原因。
本文中将从UART常用硬件结构、UART驱动实现、串口命令调试和应用层访问以及Console终端管理四部分讲解tty serial框架相关说明;具体目录如下所示。
UART是支持全双工和半双工、异步收发的通讯模块。在嵌入式系统中,UART可以说是最常用的通讯模块之一,例如用于打印调试信息和命令控制的调试口,用于和蓝牙,wifi模块连接的通讯口,板级芯片通讯的连接口,都是基于UART功能实现。另外UART也支持外扩芯片支持不同的物理接口,如RS232,RS485和RS422等,可以满足多场景多设备的需求。
UART对于芯片I/O输出的是TTL电平,一般用于短距离通讯。为了增加可靠性和远距离通讯,可以通过外部扩展支持RS232,RS485和RS422等多种通讯接口,这些接口连接时注意不能混用,否则有损坏器件的风险。
我们的调试串口一般使用UART-TTL或者UART-RS232来实时打印;在工业中,通常使用RS232或者RS485来实现稳定可靠的远程通讯。RS422因为成本问题,则常用于对通讯速率要求高,要求全双工,且传输距离远的场景。
UART的常用引脚如下所示。
在产品开发中,一般使用UART-TXD和UART-RXD进行通信。对于部分低功耗或者高低频搭配的产品,通过流控线来限制高速产品的数据发送,避免处理来不及导致数据包丢失。方向控制则用于RS485连接通讯,控制收发方向。
串口模块
tty的操作主要涉及文件:
//获取tty相关的gpio,分别对应cts-gpios, dsr-gpios, dcd-gpios, rng-gpios, rts-gpios, dtr-gpios
drivers/tty/serial/serial_mctrl_gpio.c
drivers/tty/serial/serial_core.c
drivers/tty/serial/imx.c
drivers/tty/serial/imx_earlycon.c
//include/linux/circ_buf.h
struct circ_buf {
char *buf;
int head;
int tail;
};
//include/linux/serial_core.h
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 obase; /* 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,
const 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 *);
int (*rs485_config)(struct uart_port *,
struct ktermios *termios,
struct serial_rs485 *rs485);
int (*iso7816_config)(struct uart_port *,
struct serial_iso7816 *iso7816);
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 */
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 */
/* flags must be updated while holding port mutex */
upf_t flags;
/*
* Must hold termios_rwsem, port mutex and port lock to change;
* can hold any one lock to read.
*/
upstat_t status;
int hw_stopped; /* sw-assisted CTS flow state */
unsigned int mctrl; /* current modem ctrl settings */
unsigned int frame_time; /* frame timing in ns */
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 long sysrq; /* sysrq timeout */
unsigned int sysrq_ch; /* char for sysrq */
unsigned char has_sysrq;
unsigned char sysrq_seq; /* index in sysrq_toggle_seq */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char suspended;
unsigned char console_reinit;
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_rs485 rs485_supported; /* Supported mask for serial_rs485 */
struct gpio_desc *rs485_term_gpio; /* enable RS485 bus termination */
struct serial_iso7816 iso7816;
void *private_data; /* generic platform data pointer */
};
直接开始下一节说明: nvmem子系统管理框架