日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

Linux驱动-内核uart串口驱动分析

發(fā)布時(shí)間:2024/8/1 linux 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux驱动-内核uart串口驱动分析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

寫(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)的框架,框架如下:

/* 操作函數(shù)集合 */ static struct uart_ops s3c24xx_serial_ops = {.pm = s3c24xx_serial_pm,.tx_empty = s3c24xx_serial_tx_empty,.get_mctrl = s3c24xx_serial_get_mctrl,.set_mctrl = s3c24xx_serial_set_mctrl,.stop_tx = s3c24xx_serial_stop_tx,.start_tx = s3c24xx_serial_start_tx,.stop_rx = s3c24xx_serial_stop_rx,.enable_ms = s3c24xx_serial_enable_ms,.break_ctl = s3c24xx_serial_break_ctl,.startup = s3c24xx_serial_startup,.shutdown = s3c24xx_serial_shutdown,.set_termios = s3c24xx_serial_set_termios,.type = s3c24xx_serial_type,.release_port = s3c24xx_serial_release_port,.request_port = s3c24xx_serial_request_port,.config_port = s3c24xx_serial_config_port,.verify_port = s3c24xx_serial_verify_port,.wake_peer = s3c24xx_serial_wake_peer,.flush_buffer = pl011_dma_flush_buffer, };/*定義 uart_driver 結(jié)構(gòu)體 */ static struct uart_driver s3c24xx_uart_drv = {.owner = THIS_MODULE,.driver_name = S3C24XX_SERIAL_NAME,.dev_name = S3C24XX_SERIAL_NAME,.nr = UART_NR,.cons = S3C24XX_SERIAL_CONSOLE,.major = S3C24XX_SERIAL_MAJOR,.minor = S3C24XX_SERIAL_MINOR, };/* probe函數(shù) */ static int s3c24xx_serial_probe(struct platform_device *pdev) {/* 定義一個(gè)s3c24xx_uart_port 結(jié)構(gòu)體,里面的port成員就是uart_port */struct s3c24xx_uart_port *uport;.........../* 收發(fā)數(shù)據(jù)最終調(diào)用的操作函數(shù)集合 */uport->port.ops = &s3c24xx_serial_ops; ......./* 把uart_port添加入uart_driver中*/uart_add_one_port(&s3c24xx_uart_drv, &uport->port);return 0;........ }/* remove函數(shù) */ static int __devexit s3c24xx_serial_remove(struct platform_device *dev) {struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);......uart_remove_one_port(&s3c24xx_uart_drv, port);.......return 0; }/* 使用設(shè)備樹(shù)配對(duì)的匹配表 */ static const struct of_device_id s3c24xx_uart_dt_match[] = {{ .compatible = "Nexell,s3c-uart",.data = (void *)EXYNOS4210_SERIAL_DRV_DATA },{}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match);static struct platform_driver samsung_serial_driver = {.probe = s3c24xx_serial_probe, /* 熟悉的probe函數(shù),配對(duì)成功就會(huì)調(diào)用 */.remove = __devexit_p(s3c24xx_serial_remove),.driver = {.name = "nxp-uart", /* 配對(duì)用的名字 */.owner = THIS_MODULE,.pm = SERIAL_SAMSUNG_PM_OPS,.of_match_table = s3c24xx_uart_dt_match,/* 使用設(shè)備樹(shù)配對(duì)用的匹配表 */}, };static int __init s3c24xx_serial_modinit(void) {/* 向內(nèi)核注冊(cè)u(píng)art_driver */uart_register_driver(&s3c24xx_uart_drv); /* 注冊(cè)平臺(tái)設(shè)備 */return platform_driver_register(&samsung_serial_driver); }static void __exit s3c24xx_serial_modexit(void) {/* 注銷(xiāo)uart_driver */uart_unregister_driver(&s3c24xx_uart_drv); }module_init(s3c24xx_serial_modinit); module_exit(s3c24xx_serial_modexit);

可以看出內(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)容:

#if defined(CONFIG_SERIAL_NXP_UART0) void uport0_weak_alias_init(int hwport) __attribute__((weak, alias("uart_device_init"))); void uport0_weak_alias_exit(int hwport) __attribute__((weak, alias("uart_device_exit"))); void uport0_weak_alias_wake_peer(struct uart_port *uport)__attribute__((weak, alias("uart_device_wake_peer")));/* 存放在 platform_data 中的數(shù)據(jù) */ static struct s3c24xx_uart_platdata uart0_data = {.hwport = 0,.init = uport0_weak_alias_init,.exit = uport0_weak_alias_exit,.wake_peer = uport0_weak_alias_wake_peer,.ucon = S5PV210_UCON_DEFAULT,.ufcon = S5PV210_UFCON_DEFAULT,.has_fracval = 1,#if defined(CONFIG_SERIAL_NXP_UART0_DMA).enable_dma = 1,.dma_filter = pl08x_filter_id,.dma_rx_param = (void *) DMA_PERIPHERAL_NAME_UART0_RX,.dma_tx_param = (void *) DMA_PERIPHERAL_NAME_UART0_TX,#else.enable_dma = 0,#endif }; /* UART_RESOURCE是一個(gè)宏,用來(lái)設(shè)置resource資源 */ static UART_RESOURCE(uart0, PHY_BASEADDR_UART0, IRQ_PHY_UART0); /* UART_RESOURCE是一個(gè)宏,向內(nèi)核注冊(cè) platform_device 結(jié)構(gòu)體 */ static UART_PLDEVICE(uart0, "nxp-uart", 0, uart0_resource, &uart0_data); #endif

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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。