Linux驱动-内核uart串口驱动分析
前言
寫(xiě)文章的目的是想通過(guò)記錄自己的學(xué)習(xí)過(guò)程,以便以后使用到相關(guān)的知識(shí)點(diǎn)可以回顧和參考。
一、簡(jiǎn)介
串口是很常用的一個(gè)外設(shè),在 Linux 下通常通過(guò)串口和其他設(shè)備或傳感器進(jìn)行通信,根據(jù)電平的不同,串口分為 TTL , RS232和RS485。不管是什么樣的接口電平,其驅(qū)動(dòng)程序都是一樣的,通過(guò)外接 RS485 這樣的芯片就可以將串口轉(zhuǎn)換為 RS485 信號(hào)。
同 I2C、SPI 一樣,Linux 也提供了串口驅(qū)動(dòng)框架,我們只需要按照相應(yīng)的串口框架編寫(xiě)驅(qū)動(dòng)程序即可。串口驅(qū)動(dòng)沒(méi)有什么主機(jī)端和設(shè)備端之分,就只有一個(gè)串口驅(qū)動(dòng),而且這個(gè)驅(qū)動(dòng)也已經(jīng)由 soc廠家 編寫(xiě)好在內(nèi)核中了,我們真正要做的就是在arch/arm/mach-s5p6818/dev-uart.c中通過(guò)platform_device結(jié)構(gòu)體描述設(shè)備的信息并注冊(cè)進(jìn)內(nèi)核,如果是使用設(shè)備樹(shù)來(lái)描述設(shè)備信息的,就到對(duì)應(yīng)的.dts文件中添加所要使用的串口節(jié)點(diǎn)信息,當(dāng)系統(tǒng)啟動(dòng)以后串口驅(qū)動(dòng)和設(shè)備匹配成功,相應(yīng)的串口就會(huì)被驅(qū)動(dòng)起來(lái),生成/dev/ttySACX(X=0….n)文件。
二、UART 驅(qū)動(dòng)的幾個(gè)重要結(jié)構(gòu)體
在編寫(xiě) UART t驅(qū)動(dòng)程序中,一共有三個(gè)結(jié)構(gòu)體比較重要,分別為:uart_driver, uart_port, uart_ops。
1、uart_driver 結(jié)構(gòu)體
uart_driver 結(jié)構(gòu)體表示 UART 驅(qū)動(dòng), 它定義在include/linux/serial_core.h文件中,內(nèi)容如下:
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; };每個(gè)串口驅(qū)動(dòng)都需要定義一個(gè) uart_driver,加載驅(qū)動(dòng)的時(shí)候通過(guò)uart_register_driver 函數(shù)向系統(tǒng)注冊(cè)這個(gè) uart_driver,此函數(shù)原型如下:
int uart_register_driver(struct uart_driver *drv)
函數(shù)參數(shù)和返回值含義如下:
drv:要注冊(cè)的 uart_driver。
返回值:0,成功;負(fù)值,失敗。
注銷(xiāo)驅(qū)動(dòng)的時(shí)候也需要注銷(xiāo)掉前面注冊(cè)的 uart_driver,需要用到 uart_unregister_driver 函數(shù),函數(shù)原型如下:
void uart_unregister_driver(struct uart_driver *drv)
函數(shù)參數(shù)和返回值含義如下:
drv:要注銷(xiāo)的 uart_driver。
返回值:無(wú)。
2、uart_port 結(jié)構(gòu)體
uart_port 表示一個(gè)具體的 port,uart_port 定義在 include/linux/serial_core.h 文件,內(nèi)容如下(有省略):
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 成員,它是一個(gè) uart_ops 結(jié)構(gòu)體類(lèi)型的變量,ops 包含了串口的具體驅(qū)動(dòng)函數(shù),每個(gè) UART 都有一個(gè) uart_port,那么 uart_port 是怎么和 uart_driver 結(jié)合起來(lái)的呢?這里要用到 uart_add_one_port 函數(shù),函數(shù)原型如下:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
函數(shù)參數(shù)和返回值含義如下:
drv:此 port 對(duì)應(yīng)的 uart_driver。
uport:要添加到 uart_driver 中的 port。
返回值:0,成功;負(fù)值,失敗。
卸載 UART 驅(qū)動(dòng)的時(shí)候也需要將 uart_port 從相應(yīng)的 uart_driver 中移除,需要用到uart_remove_one_port 函數(shù),函數(shù)原型如下:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)
函數(shù)參數(shù)和返回值含義如下:
drv:要卸載的 port 所對(duì)應(yīng)的 uart_driver。
uport:要卸載的 uart_port。
返回值:0,成功;負(fù)值,失敗。
3、uart_ops 結(jié)構(gòu)體
在上面講解 uart_port 的時(shí)候說(shuō)過(guò),uart_port 中的 ops 成員變量很重要,因?yàn)?ops 包含了針對(duì) UART 具體的驅(qū)動(dòng)函數(shù),Linux 系統(tǒng)收發(fā)數(shù)據(jù)最終調(diào)用的都是 ops 中的函數(shù)。ops 是 uart_ops類(lèi)型的結(jié)構(gòu)體指針變量,uart_ops 定義在 include/linux/serial_core.h 文件中,內(nèi)容如下:
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 驅(qū)動(dòng)編寫(xiě)人員需要實(shí)現(xiàn) uart_ops,因?yàn)?uart_ops 是最底層的 UART 驅(qū)動(dòng)接口,是實(shí)實(shí)在在的和 UART 寄存器打交道的。
.
.
三、 Linux 下 UART 驅(qū)動(dòng)框架
因?yàn)槲业陌遄觭oc是三星公司的,所以?xún)?nèi)核中的 UART 驅(qū)動(dòng)在 drivers/tty/serial/nxp-s3c.c 文件中,不同的soc,其UART驅(qū)動(dòng)都不同。
在 nxp-s3c.c 中,大致了解到驅(qū)動(dòng)的框架,框架如下:
可以看出內(nèi)核中的 的 UART 驅(qū)動(dòng)本質(zhì)上是一個(gè) platform 驅(qū)動(dòng)
四、 Linux 下 UART 設(shè)備信息描述
UART驅(qū)動(dòng)有了,要想實(shí)現(xiàn)串口收發(fā)功能,還差串口設(shè)備,有設(shè)備,有驅(qū)動(dòng),設(shè)備跟驅(qū)動(dòng)配對(duì)成功,才是真正實(shí)現(xiàn)串口收發(fā)功能。那么內(nèi)核中的串口設(shè)備信息描述是放在哪里呢?在上面分析驅(qū)動(dòng)框架時(shí),里面 platform_driver 結(jié)構(gòu)體的 name 字段就起到這個(gè)匹配作用的,
.name = "nxp-uart",
它還能幫助我們找到設(shè)備信息描述存放的位置,在 ubuntu 終端中進(jìn)入kernel的根目錄,使用指令 grep -nR “nxp-uart” ,就能找到 uart 設(shè)備信息描述在哪個(gè)位置了,它在 arch/arm/mach-s5p6818/dev-uart.c 中,內(nèi)面有如下內(nèi)容:
platform_device 的注冊(cè)可以看出,它就是對(duì)應(yīng)前面驅(qū)動(dòng)程序中的 platform_driver,所以 uart 設(shè)備和驅(qū)動(dòng)也就是一個(gè) flatform總線上的設(shè)備和驅(qū)動(dòng)。
總結(jié)
以上是生活随笔為你收集整理的Linux驱动-内核uart串口驱动分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 视频教程-汇编语言程序设计VII-其他
- 下一篇: linux 其他常用命令