Linux驱动-内核uart串口驱动分析
前言
寫文章的目的是想通過記錄自己的學習過程,以便以后使用到相關的知識點可以回顧和參考。
一、簡介
串口是很常用的一個外設,在 Linux 下通常通過串口和其他設備或傳感器進行通信,根據電平的不同,串口分為 TTL , RS232和RS485。不管是什么樣的接口電平,其驅動程序都是一樣的,通過外接 RS485 這樣的芯片就可以將串口轉換為 RS485 信號。
同 I2C、SPI 一樣,Linux 也提供了串口驅動框架,我們只需要按照相應的串口框架編寫驅動程序即可。串口驅動沒有什么主機端和設備端之分,就只有一個串口驅動,而且這個驅動也已經由 soc廠家 編寫好在內核中了,我們真正要做的就是在arch/arm/mach-s5p6818/dev-uart.c中通過platform_device結構體描述設備的信息并注冊進內核,如果是使用設備樹來描述設備信息的,就到對應的.dts文件中添加所要使用的串口節點信息,當系統啟動以后串口驅動和設備匹配成功,相應的串口就會被驅動起來,生成/dev/ttySACX(X=0….n)文件。
二、UART 驅動的幾個重要結構體
在編寫 UART t驅動程序中,一共有三個結構體比較重要,分別為:uart_driver, uart_port, uart_ops。
1、uart_driver 結構體
uart_driver 結構體表示 UART 驅動, 它定義在include/linux/serial_core.h文件中,內容如下:
struct uart_driver {struct module *owner;const char *driver_name;const char *dev_name;int major;int minor;int nr;struct console *cons;/** 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; };每個串口驅動都需要定義一個 uart_driver,加載驅動的時候通過uart_register_driver 函數向系統注冊這個 uart_driver,此函數原型如下:
int uart_register_driver(struct uart_driver *drv)
函數參數和返回值含義如下:
drv:要注冊的 uart_driver。
返回值:0,成功;負值,失敗。
注銷驅動的時候也需要注銷掉前面注冊的 uart_driver,需要用到 uart_unregister_driver 函數,函數原型如下:
void uart_unregister_driver(struct uart_driver *drv)
函數參數和返回值含義如下:
drv:要注銷的 uart_driver。
返回值:無。
2、uart_port 結構體
uart_port 表示一個具體的 port,uart_port 定義在 include/linux/serial_core.h 文件,內容如下(有省略):
struct uart_port {spinlock_t lock; /* port lock */unsigned long iobase; /* in/out[bwl] */unsigned char __iomem *membase; /* read/write[bwl] */..........const struct uart_ops *ops;unsigned int custom_divisor;unsigned int line; /* port index */resource_size_t mapbase; /* for ioremap */struct device *dev; /* parent device */unsigned char hub6; /* this should be in the 8250 driver */......... };uart_port 中最主要的就是 ops 成員,它是一個 uart_ops 結構體類型的變量,ops 包含了串口的具體驅動函數,每個 UART 都有一個 uart_port,那么 uart_port 是怎么和 uart_driver 結合起來的呢?這里要用到 uart_add_one_port 函數,函數原型如下:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
函數參數和返回值含義如下:
drv:此 port 對應的 uart_driver。
uport:要添加到 uart_driver 中的 port。
返回值:0,成功;負值,失敗。
卸載 UART 驅動的時候也需要將 uart_port 從相應的 uart_driver 中移除,需要用到uart_remove_one_port 函數,函數原型如下:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
函數參數和返回值含義如下:
drv:要卸載的 port 所對應的 uart_driver。
uport:要卸載的 uart_port。
返回值:0,成功;負值,失敗。
3、uart_ops 結構體
在上面講解 uart_port 的時候說過,uart_port 中的 ops 成員變量很重要,因為 ops 包含了針對 UART 具體的驅動函數,Linux 系統收發數據最終調用的都是 ops 中的函數。ops 是 uart_ops類型的結構體指針變量,uart_ops 定義在 include/linux/serial_core.h 文件中,內容如下:
struct uart_ops {unsigned int (*tx_empty)(struct uart_port *);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 (*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);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 *, int new);void (*pm)(struct uart_port *, unsigned int state,unsigned int oldstate);int (*set_wake)(struct uart_port *, unsigned int state);void (*wake_peer)(struct uart_port *);/** Return a string describing the type of the port*/const char *(*type)(struct uart_port *);/** Release IO and memory resources used by the port.* This includes iounmap if necessary.*/void (*release_port)(struct uart_port *);/** Request IO and memory resources used by the port.* This includes iomapping the port if necessary.*/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_POLLvoid (*poll_put_char)(struct uart_port *, unsigned char);int (*poll_get_char)(struct uart_port *); #endif };UART 驅動編寫人員需要實現 uart_ops,因為 uart_ops 是最底層的 UART 驅動接口,是實實在在的和 UART 寄存器打交道的。
.
.
三、 Linux 下 UART 驅動框架
因為我的板子soc是三星公司的,所以內核中的 UART 驅動在 drivers/tty/serial/nxp-s3c.c 文件中,不同的soc,其UART驅動都不同。
在 nxp-s3c.c 中,大致了解到驅動的框架,框架如下:
可以看出內核中的 的 UART 驅動本質上是一個 platform 驅動
四、 Linux 下 UART 設備信息描述
UART驅動有了,要想實現串口收發功能,還差串口設備,有設備,有驅動,設備跟驅動配對成功,才是真正實現串口收發功能。那么內核中的串口設備信息描述是放在哪里呢?在上面分析驅動框架時,里面 platform_driver 結構體的 name 字段就起到這個匹配作用的,
.name = "nxp-uart",
它還能幫助我們找到設備信息描述存放的位置,在 ubuntu 終端中進入kernel的根目錄,使用指令 grep -nR “nxp-uart” ,就能找到 uart 設備信息描述在哪個位置了,它在 arch/arm/mach-s5p6818/dev-uart.c 中,內面有如下內容:
platform_device 的注冊可以看出,它就是對應前面驅動程序中的 platform_driver,所以 uart 設備和驅動也就是一個 flatform總線上的設備和驅動。
總結
以上是生活随笔為你收集整理的Linux驱动-内核uart串口驱动分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 视频教程-汇编语言程序设计VII-其他
- 下一篇: 郭天祥的10天学会51单片机_第十二节