加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
    • 關(guān)鍵API
  • 推薦器件
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

【驅(qū)動(dòng)】串口驅(qū)動(dòng)分析(三)-serial driver

05/12 10:52
2414
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

前兩節(jié)我們介紹串口驅(qū)動(dòng)的框架和tty core部分。這節(jié)我們介紹和硬件緊密相關(guān)的串口驅(qū)動(dòng)部分。

UART驅(qū)動(dòng)部分依賴于硬件平臺,而TTY驅(qū)動(dòng)和具體的平臺無關(guān)。雖然UART部分依賴于平臺,但是不管是哪個(gè)硬件平臺,驅(qū)動(dòng)的思路都是一致的,下面分模塊來分別介紹。

關(guān)鍵數(shù)據(jù)結(jié)構(gòu)

struct uart_driver

struct uart_driver結(jié)構(gòu)體本身并不包含底層UART硬件的操作方法,其是所有串口設(shè)備驅(qū)動(dòng)的抽象和封裝。起到了連接硬件設(shè)備驅(qū)動(dòng)和TTY驅(qū)動(dòng)的作用。注冊了struct uart_driver后還不能使用UART設(shè)備,還需要關(guān)聯(lián)具體的UART設(shè)備。

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;
};

owner:指向該驅(qū)動(dòng)程序的擁有者模塊的指針,即加載該驅(qū)動(dòng)程序的內(nèi)核模塊。

driver_name:字符串,表示驅(qū)動(dòng)程序的名稱。

dev_name:字符串,表示設(shè)備名稱,即驅(qū)動(dòng)程序控制的設(shè)備文件的名稱,比如ttyS。

major:表示設(shè)備文件的主設(shè)備號。

minor:表示設(shè)備文件的次設(shè)備號。

nr:整數(shù),表示該驅(qū)動(dòng)程序控制的設(shè)備數(shù)量。

cons:指向struct console類型的指針,表示該串口設(shè)備所綁定的控制臺。

此外,結(jié)構(gòu)體中還包含了兩個(gè)私有的指針字段:

state:指向struct uart_state類型的指針,表示該驅(qū)動(dòng)程序內(nèi)部的狀態(tài)信息。

tty_driver:指向struct tty_driver類型的指針,表示該驅(qū)動(dòng)程序所對應(yīng)的 tty 驅(qū)動(dòng)程序。

struct uart_port

一個(gè)串口芯片上往往有多個(gè)串行端口(serial ports,對應(yīng)于一個(gè)物理上的串口),這些串行端口具備相同的操作機(jī)制。Linux內(nèi)核將這些串行端口用struct uart_port結(jié)構(gòu)體描述。struct uart_port用于描述一個(gè)UART端口的中斷、I/O內(nèi)存地址、FIFO大小、端口類型等信息。

在 Linux 內(nèi)核中,每個(gè)串口設(shè)備都會(huì)對應(yīng)一個(gè) struct uart_port 數(shù)據(jù)結(jié)構(gòu),并且這個(gè)數(shù)據(jù)結(jié)構(gòu)會(huì)作為串口設(shè)備的一個(gè)屬性被保存在相應(yīng)的設(shè)備節(jié)點(diǎn)中。

當(dāng)應(yīng)用程序通過打開設(shè)備節(jié)點(diǎn)來訪問串口設(shè)備時(shí),內(nèi)核會(huì)通過設(shè)備節(jié)點(diǎn)獲取對應(yīng)的 struct uart_port 數(shù)據(jù)結(jié)構(gòu),然后通過這個(gè)數(shù)據(jù)結(jié)構(gòu)來進(jìn)行串口的讀寫等操作。

struct?uart_port?{
?spinlock_t??lock;???/*?port?lock?*/
?unsigned?long??iobase;???/*?in/out[bwl]?*/
?unsigned?char?__iomem?*membase;??/*?read/write[bwl]?*/
?unsigned?int??(*serial_in)(struct?uart_port?*,?int);
?void???(*serial_out)(struct?uart_port?*,?int,?int);
?void???(*set_termios)(struct?uart_port?*,
???????????????????struct?ktermios?*new,
???????????????????struct?ktermios?*old);
?void???(*set_mctrl)(struct?uart_port?*,?unsigned?int);
?int???(*startup)(struct?uart_port?*port);
?void???(*shutdown)(struct?uart_port?*port);
?void???(*throttle)(struct?uart_port?*port);
?void???(*unthrottle)(struct?uart_port?*port);
?int???(*handle_irq)(struct?uart_port?*);
?void???(*pm)(struct?uart_port?*,?unsigned?int?state,
??????????unsigned?int?old);
?void???(*handle_break)(struct?uart_port?*);
?int???(*rs485_config)(struct?uart_port?*,
??????struct?serial_rs485?*rs485);
?unsigned?int??irq;???/*?irq?number?*/
?unsigned?long??irqflags;??/*?irq?flags??*/
?unsigned?int??uartclk;??/*?base?uart?clock?*/
?unsigned?int??fifosize;??/*?tx?fifo?size?*/
?unsigned?char??x_char;???/*?xon/xoff?char?*/
?unsigned?char??regshift;??/*?reg?offset?shift?*/
?unsigned?char??iotype;???/*?io?access?style?*/
?unsigned?char??unused1;

?unsigned?int??read_status_mask;?/*?driver?specific?*/
?unsigned?int??ignore_status_mask;?/*?driver?specific?*/
?struct?uart_state?*state;???/*?pointer?to?parent?state?*/
?struct?uart_icount?icount;???/*?statistics?*/
?struct?console??*cons;???/*?struct?console,?if?any?*/
?/*?flags?must?be?updated?while?holding?port?mutex?*/
?upf_t???flags;
?/*
??*?Must?hold?termios_rwsem,?port?mutex?and?port?lock?to?change;
??*?can?hold?any?one?lock?to?read.
??*/
?upstat_t??status;

?int???hw_stopped;??/*?sw-assisted?CTS?flow?state?*/
?unsigned?int??mctrl;???/*?current?modem?ctrl?settings?*/
?unsigned?int??timeout;??/*?character-based?timeout?*/
?unsigned?int??type;???/*?port?type?*/
?const?struct?uart_ops?*ops;
?unsigned?int??custom_divisor;
?unsigned?int??line;???/*?port?index?*/
?unsigned?int??minor;
?resource_size_t??mapbase;??/*?for?ioremap?*/
?resource_size_t??mapsize;
?struct?device??*dev;???/*?parent?device?*/
?unsigned?char??hub6;???/*?this?should?be?in?the?8250?driver?*/
?unsigned?char??suspended;
?unsigned?char??irq_wake;
?unsigned?char??unused[2];
?struct?attribute_group?*attr_group;??/*?port?specific?attributes?*/
?const?struct?attribute_group?**tty_groups;?/*?all?attributes?(serial?core?use?only)?*/
?struct?serial_rs485?????rs485;
?void???*private_data;??/*?generic?platform?data?pointer?*/
};

unsigned long iobase: 指定了該串口設(shè)備在I/O空間中的基地址。

unsigned char __iomem *membase: 指向該串口設(shè)備在內(nèi)存中映射的地址。

unsigned int (*serial_in)(struct uart_port *, int): 函數(shù)指針,用于從串口設(shè)備中讀取數(shù)據(jù)。

void (*serial_out)(struct uart_port *, int, int): 函數(shù)指針,用于向串口設(shè)備中寫入數(shù)據(jù)。

void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old): 函數(shù)指針,用于設(shè)置串口設(shè)備的終端參數(shù)。

void (*set_mctrl)(struct uart_port *, unsigned int): 函數(shù)指針,用于設(shè)置串口設(shè)備的 modem 控制信號。

int (*startup)(struct uart_port *port): 函數(shù)指針,用于初始化串口設(shè)備并啟動(dòng)傳輸。

void (*shutdown)(struct uart_port *port): 函數(shù)指針,用于關(guān)閉串口設(shè)備。

void (*throttle)(struct uart_port *port): 函數(shù)指針,用于將串口設(shè)備的傳輸流控制為停止?fàn)顟B(tài)。

void (*unthrottle)(struct uart_port *port): 函數(shù)指針,用于取消串口設(shè)備的傳輸流控制停止?fàn)顟B(tài)。

int (*handle_irq)(struct uart_port *): 函數(shù)指針,用于處理串口設(shè)備的中斷。

void (*pm)(struct uart_port *, unsigned int state, unsigned int old): 函數(shù)指針,用于處理串口設(shè)備的電源管理。

void (*handle_break)(struct uart_port *): 函數(shù)指針,用于處理串口設(shè)備的中斷信號中斷符。

int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485): 函數(shù)指針,用于配置 RS485 串行通信參數(shù)。

unsigned int irq: 該串口設(shè)備所使用的中斷號。

unsigned long irqflags: 該串口設(shè)備的中斷標(biāo)志。

unsigned int uartclk: 該串口設(shè)備的時(shí)鐘頻率。

unsigned int fifosize: 該串口設(shè)備的 FIFO 大小。

unsigned char x_char: XON/XOFF 字符。

unsigned char regshift: 寄存器偏移量。

unsigned char iotype: I/O 訪問類型。

unsigned char unused1: 未使用的成員變量。

unsigned int read_status_mask: 用于指定讀取狀態(tài)的屏蔽位。

unsigned int ignore_status_mask: 用于指定忽略狀態(tài)的屏蔽位。

struct uart_state *state: 指向該串口設(shè)備所在狀態(tài)結(jié)構(gòu)體的指針。

struct uart_icount icount: 用于存儲(chǔ)串口設(shè)備的統(tǒng)計(jì)信息。

struct console *cons: 指向該串口設(shè)備所屬控制臺設(shè)備的指針。

unsigned int mctrl:當(dāng)前調(diào)制解調(diào)器控制(Modem Control)的設(shè)置。這個(gè)值包含了當(dāng)前控制信號(如DTR、RTS、DSR、CTS等)的狀態(tài)。通常由硬件控制。

unsigned int timeout:基于字符的超時(shí)時(shí)間。當(dāng)字符被傳輸?shù)経ART端口時(shí),如果在規(guī)定的時(shí)間內(nèi)沒有收到下一個(gè)字符,則會(huì)超時(shí)并發(fā)送通知。通常由驅(qū)動(dòng)程序設(shè)置。

unsigned int type:端口類型。這個(gè)值通常用于標(biāo)識UART硬件的特殊性質(zhì)(如芯片類型、波特率范圍等)。

const struct uart_ops *ops:一個(gè)指向struct uart_ops結(jié)構(gòu)體的指針。這個(gè)結(jié)構(gòu)體包含了與UART驅(qū)動(dòng)程序相關(guān)的函數(shù)指針,如UART讀、寫、啟動(dòng)、停止等等。

unsigned int custom_divisor:自定義除數(shù),用于實(shí)現(xiàn)非標(biāo)準(zhǔn)波特率。這個(gè)值通常由驅(qū)動(dòng)程序設(shè)置。

unsigned int line:端口索引,用于標(biāo)識該UART端口的編號。

unsigned int minor:端口的次設(shè)備號,用于標(biāo)識該UART端口在系統(tǒng)中的位置。

resource_size_t mapbase、resource_size_t mapsize:映射區(qū)域的起始地址和大小。這些值通常由驅(qū)動(dòng)程序設(shè)置,用于將UART端口的物理地址映射到虛擬地址。

struct device *dev:指向父設(shè)備的指針。通常是該UART設(shè)備所連接的總線控制器設(shè)備。

unsigned char hub6:用于指示Hub6電路板的狀態(tài)。這個(gè)變量應(yīng)該是在8250驅(qū)動(dòng)程序中定義的。

unsigned char suspended:用于指示該端口是否被掛起。

unsigned char irq_wake:用于指示該端口是否支持喚醒中斷。

unsigned char unused[2]:未使用的字節(jié)。

struct attribute_group *attr_group:指向?qū)傩越M的指針。屬性組包含了UART設(shè)備的屬性和操作,如設(shè)備狀態(tài)、波特率設(shè)置等等。

const struct attribute_group **tty_groups:指向指針數(shù)組的指針,該數(shù)組包含了所有屬性組的指針,供串行核心使用。

struct serial_rs485 rs485:RS485配置結(jié)構(gòu)體,用于RS485通信。

void *private_data:指向私有數(shù)據(jù)的指針。這個(gè)指針通常由驅(qū)動(dòng)程序使用,用于保存驅(qū)動(dòng)程序特定的數(shù)據(jù)。

struct uart_ops

Linux 系統(tǒng)收發(fā)數(shù)據(jù)最終調(diào)用的都是 ops 中的函數(shù)。 ops 是 uart_ops類型的結(jié)構(gòu)體指針變量。uart硬件操作函數(shù)集合,底層硬件驅(qū)動(dòng)必須實(shí)現(xiàn)這個(gè)結(jié)構(gòu)體。

uart_ops結(jié)構(gòu)體 用于定義一個(gè)串口驅(qū)動(dòng)程序的接口,讓上層調(diào)用這些接口實(shí)現(xiàn)串口的讀寫等操作。它包含了很多函數(shù)指針,每個(gè)函數(shù)指針對應(yīng)了一個(gè)特定的串口操作。

在Linux內(nèi)核中,串口的驅(qū)動(dòng)程序是分為兩層實(shí)現(xiàn)的:串口芯片驅(qū)動(dòng)程序和 serial core 層。其中,serial core 層提供了大量的函數(shù)接口,供上層的串口芯片驅(qū)動(dòng)程序使用,這些函數(shù)接口的定義就包含在了 struct uart_ops 結(jié)構(gòu)體中。

當(dāng)編寫串口芯片驅(qū)動(dòng)程序時(shí),需要實(shí)現(xiàn) struct uart_ops 結(jié)構(gòu)體中定義的各個(gè)函數(shù)接口,以便 serial core 層調(diào)用。

例如,在芯片驅(qū)動(dòng)程序中實(shí)現(xiàn)的 uart_start() 函數(shù)就對應(yīng)了 struct uart_ops 結(jié)構(gòu)體中的 startup 函數(shù)指針。

因此,struct uart_ops 結(jié)構(gòu)體是串口驅(qū)動(dòng)程序?qū)崿F(xiàn)的關(guān)鍵,其定義了驅(qū)動(dòng)程序需要實(shí)現(xiàn)的所有函數(shù)接口,并與 serial core 層進(jìn)行了對接。

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??(*throttle)(struct?uart_port?*);
?void??(*unthrottle)(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?*,?struct?ktermios?*);
?void??(*pm)(struct?uart_port?*,?unsigned?int?state,
?????????unsigned?int?oldstate);
?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_POLL
?int??(*poll_init)(struct?uart_port?*);
?void??(*poll_put_char)(struct?uart_port?*,?unsigned?char);
?int??(*poll_get_char)(struct?uart_port?*);
#endif
};

tx_empty():檢查串口的發(fā)送緩沖區(qū)是否為空,用于判斷是否可以發(fā)送數(shù)據(jù)。

set_mctrl():設(shè)置串口的 modem 控制信號,如 RTS、DTR 等。

get_mctrl():獲取串口的 modem 控制信號。

stop_tx():停止當(dāng)前正在發(fā)送的數(shù)據(jù)。

start_tx():開始發(fā)送數(shù)據(jù)。

throttle():限制發(fā)送速率,減少發(fā)送的數(shù)據(jù)量。

unthrottle():取消限制發(fā)送速率。

send_xchar():發(fā)送一個(gè) XON 或 XOFF 字符,用于流控。

stop_rx():停止接收數(shù)據(jù)。

enable_ms():啟用串口的 modem 狀態(tài)檢測功能。

break_ctl():發(fā)送一個(gè) break 信號。

startup():初始化串口硬件。

shutdown():關(guān)閉串口硬件。

flush_buffer():清空串口的緩沖區(qū)。

set_termios():設(shè)置串口的終端參數(shù)。

set_ldisc():設(shè)置串口的行規(guī)則。

pm():實(shí)現(xiàn)串口的 power management。

wake_peer():用于喚醒其他休眠狀態(tài)的串口。

另外,還包含了一些函數(shù)指針用于處理串口的 IO 資源:

type():返回描述串口類型的字符串。

release_port():釋放串口的 IO 和內(nèi)存資源,包括解除 IO 映射等。

request_port():請求串口的 IO 和內(nèi)存資源,包括 IO 映射等。

config_port():配置串口的參數(shù)。

verify_port():驗(yàn)證串口的參數(shù)是否正確。

ioctl():實(shí)現(xiàn)串口設(shè)備的 ioctl 接口。

struct uart_state

uart_state 表示 UART 狀態(tài),并與 struct uart_port 結(jié)構(gòu)體配合使用來管理 UART 端口。

struct uart_port 結(jié)構(gòu)體表示 UART 端口的硬件信息和操作,而 struct uart_state 結(jié)構(gòu)體則表示與該端口相關(guān)的軟件狀態(tài)。

由于 UART 狀態(tài)可以包含多個(gè),因此可以在同一時(shí)刻使用多個(gè) UART 狀態(tài)來管理多個(gè) UART 端口的操作。

struct?uart_state?{
?struct?tty_port??port;

?enum?uart_pm_state?pm_state;
?struct?circ_buf??xmit;

?struct?uart_port?*uart_port;
};

struct tty_port port:表示 tty 端口的狀態(tài)信息,包括接受和發(fā)送緩沖區(qū),控制信息和流控信息等等。

enum uart_pm_state pm_state:表示串口設(shè)備的電源管理狀態(tài),可以是UART_PM_STATE_ON、UART_PM_STATE_OFFUART_PM_STATE_UNDEFINED

struct circ_buf xmit:表示串口設(shè)備的發(fā)送緩沖區(qū),用于存儲(chǔ)待發(fā)送的數(shù)據(jù)。

struct uart_port *uart_port:表示該串口設(shè)備對應(yīng)的

struct uart_port結(jié)構(gòu)體。

當(dāng)應(yīng)用程序向串口設(shè)備寫入數(shù)據(jù)時(shí),數(shù)據(jù)將被存儲(chǔ)到 xmit 緩沖區(qū)中,并且將觸發(fā)串口驅(qū)動(dòng)程序的數(shù)據(jù)發(fā)送處理函數(shù)。這個(gè)函數(shù)會(huì)從 xmit 緩沖區(qū)中取出數(shù)據(jù),并通過 uart_port 中的函數(shù)指針將數(shù)據(jù)發(fā)送到物理串口。在發(fā)送數(shù)據(jù)時(shí),驅(qū)動(dòng)程序還會(huì)根據(jù)串口的流控狀態(tài)進(jìn)行數(shù)據(jù)流控制。

當(dāng)收到數(shù)據(jù)時(shí),數(shù)據(jù)將被存儲(chǔ)到 port 的接受緩沖區(qū)中,并且將觸發(fā)串口驅(qū)動(dòng)程序的數(shù)據(jù)接收處理函數(shù)。處理函數(shù)將從接受緩沖區(qū)中取出數(shù)據(jù)并將其傳遞給應(yīng)用程序。

數(shù)據(jù)結(jié)構(gòu)抽象完畢后,serial core向下層的driver提供了方便的編程API,主要包括以下函數(shù)。

關(guān)鍵API

uart_register_driver

uart_register_driver將定義并填充好的uart driver注冊到kernel中,一般在驅(qū)動(dòng)模塊的init接口中被調(diào)用。

int?uart_register_driver(struct?uart_driver?*drv)
{
?struct?tty_driver?*normal;
?int?i,?retval;

?BUG_ON(drv->state);
??
?drv->state?=?kzalloc(sizeof(struct?uart_state)?*?drv->nr,?GFP_KERNEL);
?if?(!drv->state)
??goto?out;

?normal?=?alloc_tty_driver(drv->nr);
?if?(!normal)
??goto?out_kfree;

?drv->tty_driver?=?normal;

?normal->driver_name?=?drv->driver_name;
?normal->name??=?drv->dev_name;
?normal->major??=?drv->major;
?normal->minor_start?=?drv->minor;
?normal->type??=?TTY_DRIVER_TYPE_SERIAL;
?normal->subtype??=?SERIAL_TYPE_NORMAL;
?normal->init_termios?=?tty_std_termios;
?normal->init_termios.c_cflag?=?B9600?|?CS8?|?CREAD?|?HUPCL?|?CLOCAL;
?normal->init_termios.c_ispeed?=?normal->init_termios.c_ospeed?=?9600;
?normal->flags??=?TTY_DRIVER_REAL_RAW?|?TTY_DRIVER_DYNAMIC_DEV;
?normal->driver_state????=?drv;
?tty_set_operations(normal,?&uart_ops);

?/*
??*?Initialise?the?UART?state(s).
??*/
?for?(i?=?0;?i?<?drv->nr;?i++)?{
??struct?uart_state?*state?=?drv->state?+?i;
??struct?tty_port?*port?=?&state->port;

??tty_port_init(port);
??port->ops?=?&uart_port_ops;
?}

?retval?=?tty_register_driver(normal);
?if?(retval?>=?0)
??return?retval;

?for?(i?=?0;?i?<?drv->nr;?i++)
??tty_port_destroy(&drv->state[i].port);
?put_tty_driver(normal);
out_kfree:
?kfree(drv->state);
out:
?return?-ENOMEM;
}

uart_register_driver()注冊所做工作如下:

根據(jù)driver支持的最大設(shè)備數(shù),申請n個(gè) uart_state 空間,每一個(gè) uart_state 都有一個(gè) uart_port 。

接著它會(huì)分配一個(gè) tty_driver 對象,并初始化它的各個(gè)屬性,如 driver_namename,majorminor_start 等等。這些屬性是用于在 TTY 子系統(tǒng)中創(chuàng)建 tty 設(shè)備的,它們的值來自于 uart_driver 對象中指定的值。

接下來,它會(huì)在 tty_driver 中設(shè)置 tty 操作,其中 tty_ops 是一個(gè)結(jié)構(gòu)體,定義了 UART 串行接口所需要的函數(shù)。這些函數(shù)在串口設(shè)備注冊后,當(dāng)有數(shù)據(jù)進(jìn)出串口時(shí),TTY 子系統(tǒng)會(huì)調(diào)用這些函數(shù)。tty_set_operations() 函數(shù)用于在 tty_driver 中設(shè)置 tty 操作。

在初始化完 tty_driver 后,函數(shù)會(huì)遍歷所有的 UART 設(shè)備狀態(tài)對象,并初始化它們。這些狀態(tài)對象被存儲(chǔ)在 uart_driver 對象的 state 字段中。每個(gè) UART 設(shè)備狀態(tài)對象包含一個(gè) tty_port 對象,其中存儲(chǔ)了關(guān)于該串口設(shè)備的信息,例如流控、字長、奇偶校驗(yàn)等等。在此處, tty_port 的操作被設(shè)置為 uart_port_ops,它包含了具體實(shí)現(xiàn) UART 串行接口所需的函數(shù)。

最后會(huì)調(diào)用 tty_register_driver() 函數(shù)來向內(nèi)核注冊 tty 驅(qū)動(dòng)程序,并將驅(qū)動(dòng)程序的 tty_driver 結(jié)構(gòu)體與 uart_driver 結(jié)構(gòu)體相關(guān)聯(lián)。

如果注冊失敗,該函數(shù)將釋放之前分配的內(nèi)存。如果注冊成功,該函數(shù)將返回 0,否則將返回一個(gè)負(fù)的錯(cuò)誤碼。

總結(jié)一句話:tty serial core底層驅(qū)動(dòng)層和tty層之間的聯(lián)系需要從uart_register_driver()中連接,tty_driver是在uart_driver注冊過程中構(gòu)建的。

uart_unregister_driver

uart_unregister_driver是一個(gè)Linux內(nèi)核中的串口驅(qū)動(dòng)反注冊函數(shù),用于將之前注冊的驅(qū)動(dòng)程序與系統(tǒng)中的串口設(shè)備取消關(guān)聯(lián)。

/**
?*?uart_unregister_driver?-?remove?a?driver?from?the?uart?core?layer
?*?@drv:?low?level?driver?structure
?*
?*?Remove?all?references?to?a?driver?from?the?core?driver.??The?low
?*?level?driver?must?have?removed?all?its?ports?via?the
?*?uart_remove_one_port()?if?it?registered?them?with?uart_add_one_port().
?*?(ie,?drv->port?==?NULL)
?*/
void?uart_unregister_driver(struct?uart_driver?*drv)
{
?struct?tty_driver?*p?=?drv->tty_driver;
?unsigned?int?i;
?/*獲取與該驅(qū)動(dòng)程序關(guān)聯(lián)的?tty_driver?實(shí)例*/
?tty_unregister_driver(p);
????
????/*取消注冊驅(qū)動(dòng)程序,將它與系統(tǒng)中的?tty?設(shè)備斷開關(guān)聯(lián)*/
?put_tty_driver(p);
????
????/*釋放該?tty_driver?實(shí)例,如果此時(shí)該實(shí)例的使用計(jì)數(shù)為零,即沒有其他模塊在使用該實(shí)例,那么它將會(huì)被完全卸載并釋放所有內(nèi)存資源*/
?for?(i?=?0;?i?<?drv->nr;?i++)
??tty_port_destroy(&drv->state[i].port);
?kfree(drv->state);
?drv->state?=?NULL;
?drv->tty_driver?=?NULL;
}

uart_add_one_port

uart_add_one_port用于將一個(gè)UART端口添加到UART驅(qū)動(dòng)程序的狀態(tài)表中,并注冊TTY端口設(shè)備,讓用戶空間能夠通過該設(shè)備與UART通信。

/**
?*?uart_add_one_port?-?attach?a?driver-defined?port?structure
?*?@drv:?pointer?to?the?uart?low?level?driver?structure?for?this?port
?*?@uport:?uart?port?structure?to?use?for?this?port.
?*
?*?This?allows?the?driver?to?register?its?own?uart_port?structure
?*?with?the?core?driver.??The?main?purpose?is?to?allow?the?low
?*?level?uart?drivers?to?expand?uart_port,?rather?than?having?yet
?*?more?levels?of?structures.
?*/
int?uart_add_one_port(struct?uart_driver?*drv,?struct?uart_port?*uport)
{
?struct?uart_state?*state;
?struct?tty_port?*port;
?int?ret?=?0;
?struct?device?*tty_dev;
?int?num_groups;
?
????/*檢查是否在中斷上下文中,如果是則直接返回錯(cuò)誤*/
?BUG_ON(in_interrupt());
????
?/*檢查所添加的端口是否超出驅(qū)動(dòng)程序支持的范圍,如果是則返回EINVAL*/
?if?(uport->line?>=?drv->nr)
??return?-EINVAL;
?
????/*獲取該端口所對應(yīng)的狀態(tài)信息(uart_state)以及端口(tty_port)*/
?state?=?drv->state?+?uport->line;
?port?=?&state->port;

?mutex_lock(&port_mutex);
?mutex_lock(&port->mutex);
????
????/*檢查端口是否已經(jīng)被其他設(shè)備占用,如果是則返回EINVAL*/
?if?(state->uart_port)?{
??ret?=?-EINVAL;
??goto?out;
?}

?/*?鏈接端口和驅(qū)動(dòng)程序狀態(tài)表,并進(jìn)行相應(yīng)的初始化工作,包括PM狀態(tài)、控制臺、spinlock等?*/
?state->uart_port?=?uport;
?uport->state?=?state;

?state->pm_state?=?UART_PM_STATE_UNDEFINED;
?uport->cons?=?drv->cons;
?uport->minor?=?drv->tty_driver->minor_start?+?uport->line;

?/*
??*?If?this?port?is?a?console,?then?the?spinlock?is?already
??*?initialised.
??*/
?if?(!(uart_console(uport)?&&?(uport->cons->flags?&?CON_ENABLED)))?{
??spin_lock_init(&uport->lock);
??lockdep_set_class(&uport->lock,?&port_lock_key);
?}
?if?(uport->cons?&&?uport->dev)
??of_console_check(uport->dev->of_node,?uport->cons->name,?uport->line);
?
????/*配置端口的屬性,例如波特率、數(shù)據(jù)位、停止位等*/
?uart_configure_port(drv,?state,?uport);

?num_groups?=?2;
?if?(uport->attr_group)
??num_groups++;
????
?/*分配并設(shè)置TTY設(shè)備屬性組,這些屬性組包括TTY設(shè)備通用屬性組和用戶自定義屬性組*/
?uport->tty_groups?=?kcalloc(num_groups,?sizeof(*uport->tty_groups),
????????GFP_KERNEL);
?if?(!uport->tty_groups)?{
??ret?=?-ENOMEM;
??goto?out;
?}
?uport->tty_groups[0]?=?&tty_dev_attr_group;
?if?(uport->attr_group)
??uport->tty_groups[1]?=?uport->attr_group;

?/*注冊TTY端口設(shè)備,并將其與tty_driver和tty_port關(guān)聯(lián)起來*/
?tty_dev?=?tty_port_register_device_attr(port,?drv->tty_driver,
???uport->line,?uport->dev,?port,?uport->tty_groups);
????/*如果注冊成功,將該設(shè)備標(biāo)記為可喚醒*/
?if?(likely(!IS_ERR(tty_dev)))?{
??device_set_wakeup_capable(tty_dev,?1);
?}?else?{
??dev_err(uport->dev,?"Cannot?register?tty?device?on?line?%dn",
?????????uport->line);
?}

?/*
??*?Ensure?UPF_DEAD?is?not?set.
??*/
?uport->flags?&=?~UPF_DEAD;
?
?out:
?mutex_unlock(&port->mutex);
?mutex_unlock(&port_mutex);

?return?ret;
}

uart_remove_one_port

uart_remove_one_port用于從核心驅(qū)動(dòng)程序中分離(斷開)一個(gè)指定的端口結(jié)構(gòu)。

/**
?*?uart_remove_one_port?-?detach?a?driver?defined?port?structure
?*?@drv:?pointer?to?the?uart?low?level?driver?structure?for?this?port
?*?@uport:?uart?port?structure?for?this?port
?*
?*?This?unhooks?(and?hangs?up)?the?specified?port?structure?from?the
?*?core?driver.??No?further?calls?will?be?made?to?the?low-level?code
?*?for?this?port.
?*/
int?uart_remove_one_port(struct?uart_driver?*drv,?struct?uart_port?*uport)
{
?struct?uart_state?*state?=?drv->state?+?uport->line;
?struct?tty_port?*port?=?&state->port;
?struct?tty_struct?*tty;
?int?ret?=?0;
????
?/*檢查當(dāng)前是否處于中斷上下文中*/
?BUG_ON(in_interrupt());
?
????/*檢查uart狀態(tài)結(jié)構(gòu)中的uart端口指針是否等于傳遞給該函數(shù)的uart端口指針,如果不是則打印一條錯(cuò)誤消息*/
?if?(state->uart_port?!=?uport)
??dev_alert(uport->dev,?"Removing?wrong?port:?%p?!=?%pn",
???state->uart_port,?uport);
????
?/*獲取tty端口結(jié)構(gòu)的互斥鎖,該鎖用于防止并發(fā)修改端口狀態(tài)*/
?mutex_lock(&port_mutex);

?/*獲取tty端口結(jié)構(gòu)的互斥鎖,然后檢查uart端口指針是否為空。如果為空,則表示當(dāng)前端口已被刪除。在這種情況下,將返回-EINVAL并解鎖互斥鎖?*/
?mutex_lock(&port->mutex);
?if?(!state->uart_port)?{
??mutex_unlock(&port->mutex);
??ret?=?-EINVAL;
??goto?out;
?}
????/*鎖定?port->mutex?互斥鎖,并將?uport->flags?設(shè)置為?UPF_DEAD,表示該端口已經(jīng)被關(guān)閉。之后解鎖?port->mutex。*/
?uport->flags?|=?UPF_DEAD;
?mutex_unlock(&port->mutex);

?/*從tty層中刪除設(shè)備*/
?tty_unregister_device(drv->tty_driver,?uport->line);
????
?/*獲取?tty?設(shè)備對應(yīng)的?tty?結(jié)構(gòu)體,并使用?tty_vhangup()?函數(shù)關(guān)閉該?tty?設(shè)備的控制終端。最后,使用?tty_kref_put()?函數(shù)釋放?tty?結(jié)構(gòu)體的引用計(jì)數(shù)。*/
?tty?=?tty_port_tty_get(port);
?if?(tty)?{
??tty_vhangup(port->tty);
??tty_kref_put(tty);
?}

?/*如果該端口用作控制臺,則使用?unregister_console()?函數(shù)取消該端口的控制臺注冊*/
?if?(uart_console(uport))
??unregister_console(uport->cons);

?/*根據(jù)?uport->type?的值來釋放端口的?IO?和內(nèi)存資源,如果?uport->type?的值為?PORT_UNKNOWN,則表示沒有對應(yīng)的資源需要釋放*/
?if?(uport->type?!=?PORT_UNKNOWN)
??uport->ops->release_port(uport);
?kfree(uport->tty_groups);

?/*將?uport->type?的值設(shè)置為?PORT_UNKNOWN,表示該端口不再存在。同時(shí)將?state->uart_port?設(shè)置為?NULL,表示?state?對應(yīng)的端口不再與?uport?相關(guān)聯(lián)。*/
?uport->type?=?PORT_UNKNOWN;

?state->uart_port?=?NULL;
out:
?mutex_unlock(&port_mutex);

?return?ret;
}

uart_write_wakeup

uart_write_wakeupuart_write_wakeup喚醒上層因向串口端口寫數(shù)據(jù)而阻塞的進(jìn)程,通常在串口發(fā)送中斷處理函數(shù)中調(diào)用該函數(shù)。

/*
?*?This?routine?is?used?by?the?interrupt?handler?to?schedule?processing?in
?*?the?software?interrupt?portion?of?the?driver.
?*/
void?uart_write_wakeup(struct?uart_port?*port)
{
?struct?uart_state?*state?=?port->state;
?/*
??*?This?means?you?called?this?function?_after_?the?port?was
??*?closed.??No?cookie?for?you.
??*/
?BUG_ON(!state);
????/*函數(shù)喚醒與state->port相關(guān)聯(lián)的終端。*/
?tty_wakeup(state->port.tty);
}

uart_suspend_port

uart_suspend_port函數(shù)用于將端口掛起以進(jìn)行電源管理。它執(zhí)行一系列操作,包括檢查子設(shè)備是否可以喚醒系統(tǒng),停止發(fā)送和接收數(shù)據(jù),等待發(fā)送緩沖區(qū)為空,關(guān)閉端口,停止控制臺,并更改端口的電源管理狀態(tài)。

int?uart_suspend_port(struct?uart_driver?*drv,?struct?uart_port?*uport)
{
?struct?uart_state?*state?=?drv->state?+?uport->line;
?struct?tty_port?*port?=?&state->port;
?struct?device?*tty_dev;
?struct?uart_match?match?=?{uport,?drv};
????
?/*給port加鎖,以確保在執(zhí)行其他操作時(shí)不會(huì)發(fā)生競爭條件*/
?mutex_lock(&port->mutex);
?
????/*查找與uport->dev相關(guān)聯(lián)的子設(shè)備。它使用match結(jié)構(gòu)體和serial_match_port函數(shù)來匹配子設(shè)備*/
?tty_dev?=?device_find_child(uport->dev,?&match,?serial_match_port);
????/*如果找到了子設(shè)備并且該設(shè)備可以喚醒系統(tǒng),則將uport->irq設(shè)置為喚醒中斷,并將uport->irq_wake設(shè)置為1。然后,釋放tty_dev并解鎖port的互斥鎖,并返回0*/
?if?(device_may_wakeup(tty_dev))?{
??if?(!enable_irq_wake(uport->irq))
???uport->irq_wake?=?1;
??put_device(tty_dev);
??mutex_unlock(&port->mutex);
??return?0;
?}
????/*如果找到了子設(shè)備但該設(shè)備不能喚醒系統(tǒng),則釋放tty_dev*/
?put_device(tty_dev);

?/*?Nothing?to?do?if?the?console?is?not?suspending?*/
????/*如果控制臺未啟用掛起并且uport是控制臺,則跳轉(zhuǎn)到unlock解鎖*/
?if?(!console_suspend_enabled?&&?uart_console(uport))
??goto?unlock;
?
????/*將uport->suspended設(shè)置為1,表示端口已掛起。*/
?uport->suspended?=?1;
?
????/*如果端口已初始化,則執(zhí)行一些操作以停止傳輸并關(guān)閉端口。這些操作包括設(shè)置ASYNCB_SUSPENDED和清除ASYNCB_INITIALIZED標(biāo)志,停止發(fā)送和接收數(shù)據(jù),等待發(fā)送緩沖區(qū)為空,關(guān)閉端口*/
?if?(port->flags?&?ASYNC_INITIALIZED)?{
??const?struct?uart_ops?*ops?=?uport->ops;
??int?tries;

??set_bit(ASYNCB_SUSPENDED,?&port->flags);
??clear_bit(ASYNCB_INITIALIZED,?&port->flags);

??spin_lock_irq(&uport->lock);
??ops->stop_tx(uport);
??ops->set_mctrl(uport,?0);
??ops->stop_rx(uport);
??spin_unlock_irq(&uport->lock);

??/*
???*?Wait?for?the?transmitter?to?empty.
???*/
??for?(tries?=?3;?!ops->tx_empty(uport)?&&?tries;?tries--)
???msleep(10);
??if?(!tries)
???dev_err(uport->dev,?"%s%d:?Unable?to?drain?transmittern",
????drv->dev_name,
????drv->tty_driver->name_base?+?uport->line);

??ops->shutdown(uport);
?}

?/*
??*?Disable?the?console?device?before?suspending.
??*/
????/*?*/
?????/*如果uport是控制臺,則停止控制臺*/
?if?(uart_console(uport))
??console_stop(uport->cons);
?/*調(diào)用uart_change_pm函數(shù)以更改端口的電源管理狀態(tài)為UART_PM_STATE_OFF*/
?uart_change_pm(state,?UART_PM_STATE_OFF);
unlock:
?mutex_unlock(&port->mutex);

?return?0;
}

uart_resume_port

uart_resume_port作用是恢復(fù)一個(gè)已經(jīng)掛起的UART端口。

int?uart_resume_port(struct?uart_driver?*drv,?struct?uart_port?*uport)
{
?struct?uart_state?*state?=?drv->state?+?uport->line;
?struct?tty_port?*port?=?&state->port;
?struct?device?*tty_dev;
?struct?uart_match?match?=?{uport,?drv};
?struct?ktermios?termios;

?mutex_lock(&port->mutex);
?
????/*使用device_find_child搜索與名為match的struct?uart_match匹配的uport->dev的子設(shè)備*/
?tty_dev?=?device_find_child(uport->dev,?&match,?serial_match_port);
????
????/*如果找到設(shè)備并且端口未掛起并且設(shè)備可以喚醒,則函數(shù)禁用IRQ喚醒并返回0*/
?if?(!uport->suspended?&&?device_may_wakeup(tty_dev))?{
??if?(uport->irq_wake)?{
???disable_irq_wake(uport->irq);
???uport->irq_wake?=?0;
??}
??put_device(tty_dev);
??mutex_unlock(&port->mutex);
??return?0;
?}
????
????/*函數(shù)將uport->suspended設(shè)置為0*/
?put_device(tty_dev);
?uport->suspended?=?0;

?/*
??*?Re-enable?the?console?device?after?suspending.
??*/
????/*如果端口是控制臺端口,則函數(shù)將termios結(jié)構(gòu)設(shè)置為控制臺cflag設(shè)置*/
?if?(uart_console(uport))?{
??/*
???*?First?try?to?use?the?console?cflag?setting.
???*/
??memset(&termios,?0,?sizeof(struct?ktermios));
??termios.c_cflag?=?uport->cons->cflag;

??/*
???*?If?that's?unset,?use?the?tty?termios?setting.
???*/
??if?(port->tty?&&?termios.c_cflag?==?0)
???termios?=?port->tty->termios;
??/*如果啟用了控制臺掛起,則函數(shù)使用uart_change_pm將電源管理狀態(tài)更改為打開狀態(tài),使用uport->ops->set_termios設(shè)置termios,并使用console_start啟動(dòng)控制臺*/
??if?(console_suspend_enabled)
???uart_change_pm(state,?UART_PM_STATE_ON);
??uport->ops->set_termios(uport,?&termios,?NULL);
??if?(console_suspend_enabled)
???console_start(uport->cons);
?}

?if?(port->flags?&?ASYNC_SUSPENDED)?{
??const?struct?uart_ops?*ops?=?uport->ops;
??int?ret;
?/*如果端口已掛起,則函數(shù)使用uart_change_pm將電源管理狀態(tài)更改為打開狀態(tài)*/
??uart_change_pm(state,?UART_PM_STATE_ON);
??spin_lock_irq(&uport->lock);
????????/*使用ops->set_mctrl將調(diào)制解調(diào)器控制線設(shè)置為0*/
??ops->set_mctrl(uport,?0);
??spin_unlock_irq(&uport->lock);
??if?(console_suspend_enabled?||?!uart_console(uport))?{
???/*?Protected?by?port?mutex?for?now?*/
???struct?tty_struct?*tty?=?port->tty;
????????????/*使用ops->startup啟動(dòng)端口*/
???ret?=?ops->startup(uport);
???if?(ret?==?0)?{
????????????/*如果端口成功啟動(dòng),則使用uart_change_speed更改端口速度,使用ops->start_tx啟動(dòng)傳輸,并在port->flags中設(shè)置ASYNCB_INITIALIZED位*/
????if?(tty)
?????uart_change_speed(tty,?state,?NULL);
????spin_lock_irq(&uport->lock);
????ops->set_mctrl(uport,?uport->mctrl);
????????????????
????ops->start_tx(uport);
????spin_unlock_irq(&uport->lock);
????set_bit(ASYNCB_INITIALIZED,?&port->flags);
???}?else?{
????/*
?????*?Failed?to?resume?-?maybe?hardware?went?away?
?????*?Clear?the?"initialized"?flag?so?we?won't?try
?????*?to?call?the?low?level?drivers?shutdown?method.
?????*/
????????????????/*如果端口無法恢復(fù),則函數(shù)清除ASYNCB_INITIALIZED位并調(diào)用uart_shutdown*/
????uart_shutdown(tty,?state);
???}
??}

??clear_bit(ASYNCB_SUSPENDED,?&port->flags);
?}

?mutex_unlock(&port->mutex);

?return?0;
}

uart_get_baud_rate

uart_get_baud_rate,該函數(shù)的作用是根據(jù)給定的終端設(shè)置和范圍,獲取一個(gè)可用的波特率。如果無法獲取滿足要求的波特率,則會(huì)盡可能地使用最接近的波特率。

/**
?*?uart_get_baud_rate?-?return?baud?rate?for?a?particular?port
?*?@port:?uart_port?structure?describing?the?port?in?question.
?*?@termios:?desired?termios?settings.
?*?@old:?old?termios?(or?NULL)
?*?@min:?minimum?acceptable?baud?rate
?*?@max:?maximum?acceptable?baud?rate
?*
?*?Decode?the?termios?structure?into?a?numeric?baud?rate,
?*?taking?account?of?the?magic?38400?baud?rate?(with?spd_*
?*?flags),?and?mapping?the?%B0?rate?to?9600?baud.
?*
?*?If?the?new?baud?rate?is?invalid,?try?the?old?termios?setting.
?*?If?it's?still?invalid,?we?try?9600?baud.
?*
?*?Update?the?@termios?structure?to?reflect?the?baud?rate
?*?we're?actually?going?to?be?using.?Don't?do?this?for?the?case
?*?where?B0?is?requested?("hang?up").
?*/
unsigned?int
uart_get_baud_rate(struct?uart_port?*port,?struct?ktermios?*termios,
?????struct?ktermios?*old,?unsigned?int?min,?unsigned?int?max)
{
?unsigned?int?try;
?unsigned?int?baud;
?unsigned?int?altbaud;
?int?hung_up?=?0;
?upf_t?flags?=?port->flags?&?UPF_SPD_MASK;
?
?switch?(flags)?{
?case?UPF_SPD_HI:
??altbaud?=?57600;
??break;
?case?UPF_SPD_VHI:
??altbaud?=?115200;
??break;
?case?UPF_SPD_SHI:
??altbaud?=?230400;
??break;
?case?UPF_SPD_WARP:
??altbaud?=?460800;
??break;
?default:
??altbaud?=?38400;
??break;
?}

?for?(try?=?0;?try?<?2;?try++)?{
??baud?=?tty_termios_baud_rate(termios);

??/*
???*?The?spd_hi,?spd_vhi,?spd_shi,?spd_warp?kludge...
???*?Die!?Die!?Die!
???*/
??if?(try?==?0?&&?baud?==?38400)
???baud?=?altbaud;

??/*
???*?Special?case:?B0?rate.
???*/
??if?(baud?==?0)?{
???hung_up?=?1;
???baud?=?9600;
??}

??if?(baud?>=?min?&&?baud?<=?max)
???return?baud;

??/*
???*?Oops,?the?quotient?was?zero.??Try?again?with
???*?the?old?baud?rate?if?possible.
???*/
??termios->c_cflag?&=?~CBAUD;
??if?(old)?{
???baud?=?tty_termios_baud_rate(old);
???if?(!hung_up)
????tty_termios_encode_baud_rate(termios,
????????baud,?baud);
???old?=?NULL;
???continue;
??}

??/*
???*?As?a?last?resort,?if?the?range?cannot?be?met?then?clip?to
???*?the?nearest?chip?supported?rate.
???*/
??if?(!hung_up)?{
???if?(baud?<=?min)
????tty_termios_encode_baud_rate(termios,
???????min?+?1,?min?+?1);
???else
????tty_termios_encode_baud_rate(termios,
???????max?-?1,?max?-?1);
??}
?}
?/*?Should?never?happen?*/
?WARN_ON(1);
?return?0;
}

該函數(shù)所作工作如下:

根據(jù)UPF_SPD_MASK標(biāo)志位解析出一個(gè)備用波特率altbaud。函數(shù)會(huì)嘗試兩次獲取波特率。第一次,函數(shù)會(huì)從termios中解析出當(dāng)前波特率,如果它等于38400,則將波特率設(shè)置為備用波特率altbaud。如果波特率等于0(即請求“掛起”),則設(shè)置波特率為9600。如果波特率在min和max范圍內(nèi),則返回該波特率。如果第一次獲取的波特率為0,則函數(shù)會(huì)嘗試使用舊的終端設(shè)置。如果仍然無法滿足要求,函數(shù)會(huì)將波特率剪裁到最接近的支持的波特率。剪裁的方式是,如果波特率小于等于最小值min,則設(shè)置波特率為min + 1,否則設(shè)置波特率為max - 1。

uart_get_divisor

uart_get_divisor,用于計(jì)算給定端口的 UART 時(shí)鐘分頻器值,以實(shí)現(xiàn)指定的波特率 。主要是通過一些基本的數(shù)學(xué)運(yùn)算來計(jì)算出 UART 時(shí)鐘分頻器值。這個(gè)值是用來配置 UART 硬件的,以實(shí)現(xiàn)指定的波特率。

串口通信中,時(shí)鐘分頻器值對應(yīng)著波特率,即時(shí)鐘分頻器值越小,波特率越高,傳輸速度越快。

/**
?*?uart_get_divisor?-?return?uart?clock?divisor
?*?@port:?uart_port?structure?describing?the?port.
?*?@baud:?desired?baud?rate
?*
?*?Calculate?the?uart?clock?divisor?for?the?port.
?*/
unsigned?int
uart_get_divisor(struct?uart_port?*port,?unsigned?int?baud)
{
?unsigned?int?quot;

?/*
??*?Old?custom?speed?handling.
??*/
?if?(baud?==?38400?&&?(port->flags?&?UPF_SPD_MASK)?==?UPF_SPD_CUST)
??quot?=?port->custom_divisor;
?else
??quot?=?DIV_ROUND_CLOSEST(port->uartclk,?16?*?baud);

?return?quot;
}

該函數(shù)所作工作如下

首先根據(jù)給定的波特率計(jì)算出 UART 時(shí)鐘周期的長度 period,這個(gè)周期的長度是通過 16 * baud 計(jì)算得到的。然后,將端口的 UART 時(shí)鐘頻率除以 period,得到的商值 quot 就是需要的 UART 時(shí)鐘分頻器值。這里使用了 DIV_ROUND_CLOSEST 宏,它的作用是將浮點(diǎn)數(shù)四舍五入為最接近的整數(shù)值。

在計(jì)算時(shí)鐘分頻器值時(shí),還有一個(gè)特殊的情況需要處理。如果給定的波特率為 38400,并且端口的標(biāo)志位值為 UPF_SPD_CUST,則需要使用端口的自定義分頻器值,而不是根據(jù)公式計(jì)算出來的值。這是因?yàn)樵谝恍├系拇隍?qū)動(dòng)中,可能會(huì)使用自定義分頻器值來支持一些特殊的波特率。

uart_update_timeout

uart_update_timeout用于設(shè)置串口的 FIFO 超時(shí)時(shí)間。FIFO(First-In-First-Out)是串口硬件中用于緩存數(shù)據(jù)的一種常見結(jié)構(gòu),它可以提高串口傳輸?shù)男?。而超時(shí)時(shí)間則是指在 FIFO 中沒有數(shù)據(jù)傳輸時(shí),等待多長時(shí)間后自動(dòng)清空 FIFO。超時(shí)時(shí)間的設(shè)置可以影響串口傳輸?shù)姆€(wěn)定性和效率。

/**
?*?uart_update_timeout?-?update?per-port?FIFO?timeout.
?*?@port:??uart_port?structure?describing?the?port
?*?@cflag:?termios?cflag?value
?*?@baud:??speed?of?the?port
?*
?*?Set?the?port?FIFO?timeout?value.??The?@cflag?value?should
?*?reflect?the?actual?hardware?settings.
?*/
void
uart_update_timeout(struct?uart_port?*port,?unsigned?int?cflag,
??????unsigned?int?baud)
{
?unsigned?int?bits;

?/*?byte?size?and?parity?*/
?switch?(cflag?&?CSIZE)?{
?case?CS5:
??bits?=?7;
??break;
?case?CS6:
??bits?=?8;
??break;
?case?CS7:
??bits?=?9;
??break;
?default:
??bits?=?10;
??break;?/*?CS8?*/
?}

?if?(cflag?&?CSTOPB)
??bits++;
?if?(cflag?&?PARENB)
??bits++;

?/*
??*?The?total?number?of?bits?to?be?transmitted?in?the?fifo.
??*/
?bits?=?bits?*?port->fifosize;

?/*
??*?Figure?the?timeout?to?send?the?above?number?of?bits.
??*?Add?.02?seconds?of?slop
??*/
?port->timeout?=?(HZ?*?bits)?/?baud?+?HZ/50;
}

根據(jù)終端設(shè)置中的 cflag 值,計(jì)算出每個(gè)字節(jié)需要傳輸?shù)奈粩?shù)bits。根據(jù) cflag 中的 CSIZE 標(biāo)志位,確定每個(gè)字節(jié)的位數(shù)(5、6、7 或 8 位),并根據(jù) CSTOPB 和 PARENB 標(biāo)志位,增加停止位和奇偶校驗(yàn)位的位數(shù)。將每個(gè)字節(jié)需要傳輸?shù)奈粩?shù)bits乘以 FIFO 的大小,得到總共需要傳輸?shù)奈粩?shù)。根據(jù)波特率和總共需要傳輸?shù)奈粩?shù),計(jì)算出超時(shí)時(shí)間。將總共需要傳輸?shù)奈粩?shù)除以波特率,得到傳輸這些數(shù)據(jù)所需要的時(shí)間,再加上一些額外的時(shí)間(0.02 秒)作為緩沖,得到超時(shí)時(shí)間。最后,將計(jì)算出來的超時(shí)時(shí)間賦值給端口結(jié)構(gòu)體中的 timeout 成員變量,從而完成 FIFO 超時(shí)時(shí)間的設(shè)置。

uart_match_port

uart_match_port根據(jù)兩個(gè)端口的屬性比較兩個(gè)串口端口是否相等。

/*
?*?Are?the?two?ports?equivalent?
?*/
int?uart_match_port(struct?uart_port?*port1,?struct?uart_port?*port2)
{
?if?(port1->iotype?!=?port2->iotype)
??return?0;

?switch?(port1->iotype)?{
?case?UPIO_PORT:
??return?(port1->iobase?==?port2->iobase);
?case?UPIO_HUB6:
??return?(port1->iobase?==?port2->iobase)?&&
?????????(port1->hub6???==?port2->hub6);
?case?UPIO_MEM:
?case?UPIO_MEM32:
?case?UPIO_MEM32BE:
?case?UPIO_AU:
?case?UPIO_TSI:
??return?(port1->mapbase?==?port2->mapbase);
?}
?return?0;
}
    根據(jù)兩個(gè)串口端口的 iotype 屬性進(jìn)行比較,如果不相等,則兩個(gè)端口不相等,函數(shù)返回 0。根據(jù) iotype 屬性的不同,比較兩個(gè)端口的其他屬性。對于 UPIO_PORT 和 UPIO_HUB6 類型的端口,比較它們的 iobase 和 hub6 屬性是否相等;對于其他類型的端口,比較它們的 mapbase 屬性是否相等。如果所有屬性都相等,則兩個(gè)端口相等,函數(shù)返回 1,否則返回 0。

uart_console_write

uart_console_write用于將控制臺消息寫入串口。

嵌入式系統(tǒng)中,通常需要將控制臺輸出重定向到串口,以便進(jìn)行調(diào)試和日志記錄。該函數(shù)實(shí)現(xiàn)了將一個(gè)字符串寫入串口的操作,其中需要將字符串中的換行符轉(zhuǎn)換為回車換行符。

/**
?*?uart_console_write?-?write?a?console?message?to?a?serial?port
?*?@port:?the?port?to?write?the?message
?*?@s:?array?of?characters
?*?@count:?number?of?characters?in?string?to?write
?*?@putchar:?function?to?write?character?to?port
?*/
void?uart_console_write(struct?uart_port?*port,?const?char?*s,
???unsigned?int?count,
???void?(*putchar)(struct?uart_port?*,?int))
{
?unsigned?int?i;

?for?(i?=?0;?i?<?count;?i++,?s++)?{
??if?(*s?==?'n')
???putchar(port,?'r');
??putchar(port,?*s);
?}
}

該函數(shù)的實(shí)現(xiàn)主要是遍歷字符串中的所有字符,并將每個(gè)字符寫入串口。在寫入字符之前,需要判斷該字符是否為換行符。如果是換行符,則需要先將其轉(zhuǎn)換為回車換行符,再寫入串口。

總結(jié)

對接底層的部分,Kernel 主要是提供了兩個(gè)接口:

1、uart_register_driver (一次調(diào)用)

2、uart_add_one_port (多次調(diào)用)

通過這兩個(gè)接口,實(shí)現(xiàn)了芯片將自己的 UART 對接到 Linux Kernel UART Driver 中。

芯片廠商需要自行設(shè)計(jì)并實(shí)現(xiàn)的部分有:

1、uart_drvier 結(jié)構(gòu)(一個(gè))

2、uart_port 結(jié)構(gòu)(多個(gè))

3、uart_ops 對串口的操作集(可能一個(gè),可能多個(gè))

所以從結(jié)構(gòu)上來看,整個(gè)對接過程為:

這里有一點(diǎn)需要特別注意,在對接底層的部分中,Kernel 定義了一個(gè)結(jié)構(gòu)體叫:struct uart_ops

在 tty 層,對 tty_driver 初始化的時(shí)候(serial_core.c),調(diào)用到:

tty_set_operations(normal,?&uart_ops);

而他的實(shí)現(xiàn)是:

void?tty_set_operations(struct?tty_driver?*driver,const?struct?tty_operations?*op)

{
?driver->ops?=?op;
};
EXPORT_SYMBOL(tty_set_operations);

看到了么,傳進(jìn)去的是 ****tty_operations *op****,所以,在 tty_driver 掛接的 uart_ops 并非那個(gè) struct uart_ops,而是這個(gè) serial_core.c 文件內(nèi)定義的:

static?const?struct?tty_operations?uart_ops?=?{
?.open??=?uart_open,
?.close??=?uart_close,
?.write??=?uart_write,
?.put_char?=?uart_put_char,
?.flush_chars?=?uart_flush_chars,
?.write_room?=?uart_write_room,
?.chars_in_buffer=?uart_chars_in_buffer,
?.flush_buffer?=?uart_flush_buffer,
?.ioctl??=?uart_ioctl,
?.throttle?=?uart_throttle,
?.unthrottle?=?uart_unthrottle,
?.send_xchar?=?uart_send_xchar,
?.set_termios?=?uart_set_termios,
?.set_ldisc?=?uart_set_ldisc,
?.stop??=?uart_stop,
?.start??=?uart_start,
?.hangup??=?uart_hangup,
?.break_ctl?=?uart_break_ctl,
?.wait_until_sent=?uart_wait_until_sent,
#ifdef?CONFIG_PROC_FS
?.proc_show?=?uart_proc_show,
#endif
?.tiocmget?=?uart_tiocmget,
?.tiocmset?=?uart_tiocmset,
?.set_serial?=?uart_set_info_user,
?.get_serial?=?uart_get_info_user,
?.get_icount?=?uart_get_icount,
#ifdef?CONFIG_CONSOLE_POLL
?.poll_init?=?uart_poll_init,
?.poll_get_char?=?uart_poll_get_char,
?.poll_put_char?=?uart_poll_put_char,
#endif
};

名字一樣,但是不是同一個(gè)結(jié)構(gòu),容易讓人眼花~~

本文參考

https://blog.csdn.net/zhoutaopower/article/details/99289550

https://www.cnblogs.com/timemachine213/p/14317462.html

https://blog.csdn.net/u011728480/article/details/105676239

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險(xiǎn)等級 參考價(jià)格 更多信息
TJA1051T/3/2Z 1 NXP Semiconductors Interface Circuit
$9.33 查看
AFBR-5803ATZ 1 Broadcom Limited Transceiver, 1270nm Min, 1380nm Max, 125Mbps(Tx), 125Mbps(Rx), ST Connector, Through Hole Mount, ROHS COMPLIANT, SIP-9

ECAD模型

下載ECAD模型
$34.2 查看
ABM11AIG-40.000MHZ-4Z-T3 1 Abracon Corporation CRYSTAL 40MHZ 10PF SMD
$1.44 查看

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

作者就職于某500強(qiáng)公司,擔(dān)任BSP工程師。具有豐富的嵌入式開發(fā)經(jīng)驗(yàn)。專欄主要分享計(jì)算機(jī)基礎(chǔ),操作系統(tǒng),Linux驅(qū)動(dòng)開發(fā),Arm體系與架構(gòu),C/C++,數(shù)據(jù)結(jié)構(gòu)與算法等相關(guān)文章。歡迎關(guān)注我的公眾號【嵌入式與Linux那些事】,一起學(xué)習(xí)交流。