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, structuart_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的state(n)分配空間
2. 調用alloc_tty_driver()分配tty_driver、ttys(n)、termios(n)、ports(n)、cdevs(n), 並初始化tty_driver的magic、num、owner、flags
3. 從uart_driver對應變量賦值tty_driver的driver_name、name、major、minor_start
4. 設置tty_driver的type為TTY_DRIVER_TYPE_SERIAL, subtype為SERIAL_TYPE_NORMAL
5. 設置tty_driver的init_termios為tty_std_termios, 並定制init_termios的c_cflag和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初始化state的port, 即tty_port結構體, 並將uart_port_ops賦值給tty_port的ops
10. 調用tty_register_driver註冊tty驅動
uart_unregister_driver完成了如下事宜
1. 調用tty_unregister_driver註銷tty驅動
2. 調用put_tty_driver釋放驅動
3. 調用tty_port_destroy銷毀state的tty_port
3. UART數據結構
UART抽象層包含如下幾個重要數據結構
uart_driver是uart的私有驅動結構, 包括一些和tty_driver相同的變量和uart相關的變量
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大小、端口類型、串口時鐘等
其中, 一個uart_port實例對應一個串口設備
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_ops是uart端口操作集, 與硬件相關
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 *); /* 停止發送 */ void (*start_tx)(struct uart_port *); /* 開始發送 */ void (*throttle)(struct uart_port *); /* */ void (*unthrottle)(struct uart_port *); /* */ void (*send_xchar)(struct uart_port *, char ch); /* */ 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結構體
3. 調用uart_register_driver註冊uart驅動
4. 調用uart_add_one_port添加uart端口
Linux UART介紹