轉自:http://blog.csdn.net/sharecode/article/details/9196591
Linux 中將串口驅動進行了分層,如圖:
本節講解與底層硬件密切相關的層,以S3C2440爲例剖析:
實現文件有:/drivers/serial/samsung.c /drivers/serial/samsung.h /drivers/serial/s3c2440.c (kernel 2.6.28),Serial Core層在/drivers/serial/serial_core.c主要文件中。
硬件驅動層與Serial-Core溝通數據結構如下:
1. uart_driver包含了串口設備名、串口驅動名、主次設備號、串口控制檯(可選)等信息,還封裝了tty_driver(底層串口驅動無需關心tty_driver)。
struct uart_driver {
struct module *owner;/* 擁有該uart_driver的模塊,一般爲THIS_MODULE
*/
constchar*driver_name;/* 串口驅動名,串口設備文件名以驅動名爲基礎 */
constchar*dev_name;/* 串口設備名 */
int major;/* 主設備號 */
int minor;/* 次設備號 */
int nr;/* 該uart_driver支持的串口個數(最大)
*/
struct console *cons;/* 其對應的console.若該uart_driver支持serial
console,否則爲NULL */
/*
* these are private; the low level driver should not
* touch these; they should be initialised to NULL
*/
struct uart_state *state;
struct tty_driver *tty_driver;
};
2. uart_port用於描述串口端口的I/O端口或I/O內存地址、FIFO大小、端口類型、串口時鐘等信息。實際上,一個uart_port實例對應一個串口設備。
struct uart_port {
spinlock_t lock;/* 串口端口鎖 */
unsignedint iobase;/* IO端口基地址 */
unsignedchar __iomem *membase;/*
IO內存基地址,經映射(如ioremap)後的IO內存虛擬基地址 */
unsignedint irq;/* 中斷號 */
unsignedint uartclk;/* 串口時鐘 */
unsignedint fifosize;/* 串口FIFO緩衝大小
*/
unsignedchar x_char;/* xon/xoff字符 */
unsignedchar regshift;/* 寄存器位移 */
unsignedchar iotype;/* IO訪問方式 */
unsignedchar unused1;
#define UPIO_PORT (0)/*
IO端口 */
#define UPIO_HUB6 (1)
#define UPIO_MEM (2)/*
IO內存 */
#define UPIO_MEM32 (3)
#define UPIO_AU (4)/*
Au1x00 type IO */
#define UPIO_TSI (5)/*
Tsi108/109 type IO */
#define UPIO_DWAPB (6)/*
DesignWare APB UART */
#define UPIO_RM9000 (7)/*
RM9000 type IO */
unsignedint read_status_mask;/* 關心的Rx
error status */
unsignedint ignore_status_mask;/* 忽略的Rx
error status */
struct uart_info *info; //重要,見下面
struct uart_icount icount; /*
計數器 uart_icount爲串口信息計數器,包含了發送字符計數、接收字符計數等。在串口的發送中斷處理函數和接收中斷處理函數中,我們需要管理這些計數。*/
struct console *cons;/* console結構體
*/
#ifdefCONFIG_SERIAL_CORE_CONSOLE
unsignedlong sysrq;/* sysrq timeout */
#endif
upf_t flags;
#define UPF_FOURPORT ((__forceupf_t)(1 << 1))
#define UPF_SAK ((__forceupf_t)(1 << 2))
#define UPF_SPD_MASK ((__forceupf_t)(0x1030))
#define UPF_SPD_HI ((__forceupf_t)(0x0010))
#define UPF_SPD_VHI ((__forceupf_t)(0x0020))
#define UPF_SPD_CUST ((__forceupf_t)(0x0030))
#define UPF_SPD_SHI ((__forceupf_t)(0x1000))
#define UPF_SPD_WARP ((__forceupf_t)(0x1010))
#define UPF_SKIP_TEST ((__forceupf_t)(1 << 6))
#define UPF_AUTO_IRQ ((__forceupf_t)(1 << 7))
#define UPF_HARDPPS_CD ((__forceupf_t)(1 << 11))
#define UPF_LOW_LATENCY ((__forceupf_t)(1 << 13))
#define UPF_BUGGY_UART ((__forceupf_t)(1 << 14))
#define UPF_MAGIC_MULTIPLIER((__force upf_t)(1 << 16))
#define UPF_CONS_FLOW ((__forceupf_t)(1 << 23))
#define UPF_SHARE_IRQ ((__forceupf_t)(1 << 24))
#define UPF_BOOT_AUTOCONF ((__forceupf_t)(1 << 28))
#define UPF_FIXED_PORT ((__forceupf_t)(1 << 29))
#define UPF_DEAD ((__forceupf_t)(1 << 30))
#define UPF_IOREMAP ((__forceupf_t)(1 << 31))
#define UPF_CHANGE_MASK ((__forceupf_t)(0x17fff))
#define UPF_USR_MASK ((__forceupf_t)(UPF_SPD_MASK|UPF_LOW_LATENCY))
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;/* 端口索引 */
resource_size_t mapbase;/* IO內存物理基地址,可用於ioremap
*/
struct device *dev;/* 父設備
*/
unsigned char hub6;/* this should be in the 8250 driver */
unsigned char suspended;
unsigned char unused[2];
void*private_data;/* 端口私有數據,一般爲platform數據指針
*/
};
(1) Uart_struct uart_icount {
__u32 cts;
__u32 dsr;
__u32 rng;
__u32 dcd;
__u32 rx;/* 發送字符計數 */
__u32 tx;/* 接受字符計數 */
__u32 frame;/* 幀錯誤計數 */
__u32 overrun;/* Rx FIFO溢出計數 */
__u32 parity; /* 幀校驗錯誤計數 */
__u32 brk; /* break計數 */
__u32 buf_overrun;
};
uart_info有兩個成員在底層串口驅動會用到:xmit和tty。用戶空間程序通過串口發送數據時,上層驅動將用戶數據保存在xmit;而串口發送中斷處理函數就是通過xmit獲取到用戶數據並將它們發送出去。串口接收中斷處理函數需要通過tty將接收到的數據傳遞給行規則層。
struct uart_info {
struct tty_struct *tty;
//接受
struct circ_buf xmit; //發送
uif_t flags;
/*
* Definitions for info->flags. These are _private_ to serial_core,and
* are specific to this structure. They may be queried by low leveldrivers.
*/
#define UIF_CHECK_CD ((__force uif_t)(1 << 25))
#define UIF_CTS_FLOW ((__force uif_t)(1 << 26))
#define UIF_NORMAL_ACTIVE ((__force uif_t)(1 << 29))
#define UIF_INITIALIZED ((__force uif_t)(1 << 31))
#define UIF_SUSPENDED ((__force uif_t)(1 << 30))
int blocked_open;
struct tasklet_struct tlet; //上層驅動任務等待隊列的
wait_queue_head_t open_wait;
wait_queue_head_t delta_msr_wait;
};
3. Uart_port中有一個重要的uart_ops,底層硬件需要實現:
struct uart_ops {
unsignedint(*tx_empty)(struct uart_port *); /* 串口的Tx
FIFO緩存是否爲空 */
void(*set_mctrl)(struct uart_port *,unsignedint mctrl);/* 設置串口modem控制 */
unsignedint(*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(*set_termios)(struct uart_port *,struct ktermios *new,struct ktermios *old);/* 設置串口參數
*/
void(*pm)(struct uart_port *,unsignedint state,
unsignedint oldstate);/* 串口電源管理 */
int(*set_wake)(struct uart_port *,unsignedint state);/*
*/
constchar*(*type)(struct uart_port *);/* 返回一描述串口類型的字符串
*/
void(*release_port)(struct uart_port *);/* 釋放串口已申請的IO端口/IO內存資源,必要時還需iounmap */
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 *,unsignedint,unsignedlong);/*
IO控制 */
};
4. uart_driver通過Serial Core層的
int uart_register_driver(struct uart_driver *drv)
向Core註冊,通過int uart_add_one_port(struct uart_driver *drv,struct uart_port *port)向該驅動添加uart_port。
Serial Core層還導出如下函數共上層或下層調用:
EXPORT_SYMBOL(uart_match_port); //判斷兩個uart_port是否相等
EXPORT_SYMBOL(uart_write_wakeup); //該函數常在中斷處理函數中調用用來喚醒上層因向串口端口寫數據而阻塞的進程
EXPORT_SYMBOL(uart_register_driver); //已解釋
EXPORT_SYMBOL(uart_unregister_driver);
EXPORT_SYMBOL(uart_suspend_port); //用於掛起特定的串口端口
EXPORT_SYMBOL(uart_resume_port);
EXPORT_SYMBOL(uart_add_one_port); //已解釋
EXPORT_SYMBOL(uart_remove_one_port);
具體實現請參照/drivers/serial/samsung.c /drivers/serial/s3c2440.c
下一節重點介紹Serial Core層