linux配置串口驱动程序,[Linux 驱动] -- Linux 驱动之串口(UART)
一、UART 驅動程序概述
在嵌入式 Linux 系統中,串口被看成終端設備,終端設備(tty)的驅動程序分為三個部分:
tty_core
tty_disicipline
tty_driver
包括3個結構體:
uart_driver
uart_port
uart_ops( include/serial_core.h)
因此實現一個平臺的 UART 驅動程序只需要實現這3個結構體即可。
二、uart_drvier 與 tty_driver 之間的關系
uart_driver 結構體:
uart_driver 結構體包含了串口設備名、串口驅動名、主次設備號、串口控制臺(可選)等信息,還封裝了 tty_driver(底層串口驅動,無需關心 tty_driver)。
struct uart_driver
{
struct module *owner; //擁有該uart_driver的模塊,一般為THIS_MODULE
const char *driver_name; //串口驅動名,串口設備文件名以驅動名為基礎
const char *dev_name; //串口設備名
int major; //主設備號
int minor; //次設備號
int nr; //該 uart_driver 支持的最大串口個數
struct console *cons; //其對應的console。若該uart_driver支持serial console,否則為NULL
...........................
struct uart_state *state;
struct tty_driver *tty_driver; //uart_driver 封裝了 tty_driver,使底層uart驅動不用關心tty_driver。
};
一個 tty 驅動程序必須注冊/注銷 tty_driver;
一個 uart 驅動則變為注冊/注銷 uart_driver;
使用如下接口:
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_drvier *drv);
int tty_register_driver(struct tty_driver *drv);
void tty_unregister_driver(struct tty_driver *drv);
實際上,uart_register_driver() 和 uart_unregister_drvier() 中分別包含了 tty_register_driver() 和 tty_unregister () 的操作,詳情如下:
uart_port結構體:
uart_port 用于描述一個 UART 端口(直接對應于一個串口)的 I/O 端口或 I/O內存地址、FIFO大小、端口類型等信息。
struct uart_port
{
spinlock_t lock; //串口端口鎖
unsigned int iobase; //IO 端口基地址
unsigned char __iomem *membase; //IO 內存基地址,經映射(如ioremap)后的IO內存虛擬基地址
unsigned int irq; //中斷號
unsigend int uartlock; //串口時鐘
unsigend int fifosize; //串口FIFO緩沖大小
unsigned char x_char; //xon/xoff 字符
unsigned char regshift; //寄存器位移
unsigned char iotype; //IO訪問方式
unsigned char unused1;
#define UPIO_PORT (0) //IO端口
#deifne UPIO_HUB6 (1)
#define UPIO_MEM (2) //IO內存
#define UPIO_MEM32(3)
#define UPIO_AU (4) //Aulx00 type IO
#define UPIO_TSI (5) //Tsi108/109 type IO
#define UPIO_DWAPB (6) //DesignWare APB UART
#define UPIO_RM9000 (7) //RM9000 type IO
unsigned int read_status_mask; //關心的Rx error status
unsigned int ignore_status_mask; //忽略的Rx error status
struct uart_info *info; //重要,見下面
struct uart_icount icount; //計數器 uart_icount 為串口信息計數器,包含了發送字符計數、接收字符計數等。在串口的發送中斷處理函數和接收處理函數中,我們需要管理這些計數。
struct console *cons; //console 結構體
#ifdef CONFIG_SERIAL_CORE_CONSOLE
unsigned long 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;
unsigend char unused[2];
void *private_data; //端口私有數據,一般為platform數據指針
};
串口核心層提供如下函數來添加一個端口:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
對上述函數的調用應該發生在 uart_register_driver() 之后, uart_add_one_port() 的一個最重要的作用是封裝了 tty_register_device()。
uart_add_one_port() 的“反函數”是 uart_remove_one_port(),其中會調用 tty_unregister_device(), 原型為:
int uart_remove_one_port(struct driver *drv, struct uart_port *port);
uart_info結構體:
uart_info 有兩個成員在底層串口驅動會用到: xmit 和 tty。用戶空間程序通過串口發送數據時,上層驅動將用戶數據保存在xmit;而串口發送中斷處理函數就是通過xmit獲取到用戶數據并將它們發送出去。串口接收中斷處理函數需要通過tty將接收到的數據傳遞給行規則層。
在使用串口核心層這個通用串口tty驅動層的接口后,一個串口驅動要完成的主要工作將包括:
定義uart_drvier、uart_ops、uart_port等結構體的實例,并在適當的地方根據具體硬件和驅動的情況初始化它們。(當然具體設備xxx的驅動可以將這些結構套在新定義的 xxx_uart_driver、xxx_uart_ops、xxx_uart_port之內)。
在模塊初始化時調用uart_register_driver() 和 uart_add_one_port()以注冊UART驅動并添加端口,在模塊卸載時調用uart_unregister_driver 和 uart_remove_one_port() 以注銷UART驅動并移除端口。
根據具體硬件的datasheet實現uart_ops中的成員函數,這些函數的實現成為UART驅動的主體工作。
串口驅動之tty
概念介紹:
在Linux中,終端是一類字符設備,他包括多種類型,通常使用tty來簡稱各種中斷設備串口終端(/dev/ttyS*):串口終端是使用串口連接的終端設備,Linux中將每一個串口設備都看作一個字符設備,這些串行端口對應的設備名稱是/dev/ttySAC0 和 /dev/ttySAC1。
控制臺終端(/dev/console):
在Linux中,計算中的輸出設備通常被稱為控制臺終端(console)。這里特指printk()信息輸出的。注意:/dev/console 是一個虛擬的設備,它需要映射到真正的tty上。比如通過內核啟動參數“console=ttySAC0”就是把console映射到串口0,經常被內核所使用。
注意:這里的終端是一個虛擬設備,虛擬設備必須和實際的設備聯系起來,console=ttySAC0系統啟動的時候就關聯起來了。
虛擬終端(/dev/tty*):
當用戶登錄的使用使用的是虛擬終端,使用快捷鍵組合:ctrl+alt+[F1-F6]組合鍵就可以切換到tty1,tty2,tty3等上面去。tty1-tty6等成為虛擬終端,而tty0是當前使用的終端的一個別名。主要是提供給應用程序使用。
tty架構
tty核心:
tty核心是對整個tty設備的抽象,并提供單一的接口。
tty線路規劃:
tty線路規程是對數據的傳輸的格式化,比如需要實現某種協議,就需要將協議的實現代碼放在該位置。
tty驅動:
是面向tty設備的硬件驅動
注意:Linux中的獲取回溯信息使用函數dump_stack()用來顯示各種函數的調用信息。
串口驅動程序的結構
分析:串口驅動程序需要提供給用戶讀數據的功能,寫數據,打開串口和關閉串口的功能。打開之前需要對肯定需要對串口進行初始化的工作。
重要數據結構:
struct uart_driver :一個串口對應一個串口驅動,用于描述串口結構
struct uart_port:??? 有幾個串口就對應幾個port
struct uart_ops:? UART相關操作函數結構體,對應相關串口所支持的操作函數集
struct uart_state:UART狀態結構
struct uart_info: UART信息結構
串口初始化:
定義并描述串口:struct uart_driver;
注冊串口驅動程序:uart_register_driver;
取出相應的串口,并初始化該取出的串口。
串口驅動之打開驅動:
系統調用過程:用戶使用open()函數打開設備文件
注意:
打開設備文件肯定有對應的設備驅動文件打開函數:file_operations;
在使用uart_register_driver()注冊串口驅動的時候,該函數里面會調用函數tty_register_driver(),該函數會調用 cdev_init()函數和cdev_add()。
從這里可以看出tty設備是屬于字符設備。
總結
以上是生活随笔為你收集整理的linux配置串口驱动程序,[Linux 驱动] -- Linux 驱动之串口(UART)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jrtplib linux编译,linu
- 下一篇: 2022年系统分析师论文模拟题--押题