簡(jiǎn)介
前兩節(jié)我們介紹串口驅(qū)動(dòng)的框架和tty core部分。這節(jié)我們介紹和硬件緊密相關(guān)的串口驅(qū)動(dòng)部分。
UART驅(qū)動(dòng)部分依賴于硬件平臺(tái),而TTY驅(qū)動(dòng)和具體的平臺(tái)無(wú)關(guān)。雖然UART部分依賴于平臺(tái),但是不管是哪個(gè)硬件平臺(tái),驅(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)的作用。注冊(cè)了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)容如下:
structuart_driver{ structmodule*owner; constchar*driver_name; constchar*dev_name; intmajor; intminor; intnr; structconsole*cons; /* *theseareprivate;thelowleveldrivershouldnot *touchthese;theyshouldbeinitialisedtoNULL */ structuart_state*state; structtty_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è)備號(hào)。
minor:表示設(shè)備文件的次設(shè)備號(hào)。
nr:整數(shù),表示該驅(qū)動(dòng)程序控制的設(shè)備數(shù)量。
cons:指向 struct console 類型的指針,表示該串口設(shè)備所綁定的控制臺(tái)。
此外,結(jié)構(gòu)體中還包含了兩個(gè)私有的指針字段:
state:指向 struct uart_state 類型的指針,表示該驅(qū)動(dòng)程序內(nèi)部的狀態(tài)信息。
tty_driver:指向 struct tty_driver 類型的指針,表示該驅(qū)動(dòng)程序所對(duì)應(yīng)的 tty 驅(qū)動(dòng)程序。
struct uart_port
一個(gè)串口芯片上往往有多個(gè)串行端口(serial ports,對(duì)應(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ì)對(duì)應(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)獲取對(duì)應(yīng)的 struct uart_port 數(shù)據(jù)結(jié)構(gòu),然后通過這個(gè)數(shù)據(jù)結(jié)構(gòu)來進(jìn)行串口的讀寫等操作。
structuart_port{ spinlock_tlock;/*portlock*/ unsignedlongiobase;/*in/out[bwl]*/ unsignedchar__iomem*membase;/*read/write[bwl]*/ unsignedint(*serial_in)(structuart_port*,int); void(*serial_out)(structuart_port*,int,int); void(*set_termios)(structuart_port*, structktermios*new, structktermios*old); void(*set_mctrl)(structuart_port*,unsignedint); int(*startup)(structuart_port*port); void(*shutdown)(structuart_port*port); void(*throttle)(structuart_port*port); void(*unthrottle)(structuart_port*port); int(*handle_irq)(structuart_port*); void(*pm)(structuart_port*,unsignedintstate, unsignedintold); void(*handle_break)(structuart_port*); int(*rs485_config)(structuart_port*, structserial_rs485*rs485); unsignedintirq;/*irqnumber*/ unsignedlongirqflags;/*irqflags*/ unsignedintuartclk;/*baseuartclock*/ unsignedintfifosize;/*txfifosize*/ unsignedcharx_char;/*xon/xoffchar*/ unsignedcharregshift;/*regoffsetshift*/ unsignedchariotype;/*ioaccessstyle*/ unsignedcharunused1; unsignedintread_status_mask;/*driverspecific*/ unsignedintignore_status_mask;/*driverspecific*/ structuart_state*state;/*pointertoparentstate*/ structuart_icounticount;/*statistics*/ structconsole*cons;/*structconsole,ifany*/ /*flagsmustbeupdatedwhileholdingportmutex*/ upf_tflags; /* *Mustholdtermios_rwsem,portmutexandportlocktochange; *canholdanyonelocktoread. */ upstat_tstatus; inthw_stopped;/*sw-assistedCTSflowstate*/ unsignedintmctrl;/*currentmodemctrlsettings*/ unsignedinttimeout;/*character-basedtimeout*/ unsignedinttype;/*porttype*/ conststructuart_ops*ops; unsignedintcustom_divisor; unsignedintline;/*portindex*/ unsignedintminor; resource_size_tmapbase;/*forioremap*/ resource_size_tmapsize; structdevice*dev;/*parentdevice*/ unsignedcharhub6;/*thisshouldbeinthe8250driver*/ unsignedcharsuspended; unsignedcharirq_wake; unsignedcharunused[2]; structattribute_group*attr_group;/*portspecificattributes*/ conststructattribute_group**tty_groups;/*allattributes(serialcoreuseonly)*/ structserial_rs485rs485; void*private_data;/*genericplatformdatapointer*/ };
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 控制信號(hào)。
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è)備的中斷信號(hào)中斷符。
int (*rs485_config)(struct uart_port *, struct serial_rs485 *rs485): 函數(shù)指針,用于配置 RS485 串行通信參數(shù)。
unsigned int irq: 該串口設(shè)備所使用的中斷號(hào)。
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è)備所屬控制臺(tái)設(shè)備的指針。
unsigned int mctrl:當(dāng)前調(diào)制解調(diào)器控制(Modem Control)的設(shè)置。這個(gè)值包含了當(dāng)前控制信號(hào)(如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)識(shí)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)識(shí)該UART端口的編號(hào)。
unsigned int minor:端口的次設(shè)備號(hào),用于標(biāo)識(shí)該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ù)指針對(duì)應(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ù)就對(duì)應(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)行了對(duì)接。
structuart_ops{ unsignedint(*tx_empty)(structuart_port*); void(*set_mctrl)(structuart_port*,unsignedintmctrl); unsignedint(*get_mctrl)(structuart_port*); void(*stop_tx)(structuart_port*); void(*start_tx)(structuart_port*); void(*throttle)(structuart_port*); void(*unthrottle)(structuart_port*); void(*send_xchar)(structuart_port*,charch); void(*stop_rx)(structuart_port*); void(*enable_ms)(structuart_port*); void(*break_ctl)(structuart_port*,intctl); int(*startup)(structuart_port*); void(*shutdown)(structuart_port*); void(*flush_buffer)(structuart_port*); void(*set_termios)(structuart_port*,structktermios*new, structktermios*old); void(*set_ldisc)(structuart_port*,structktermios*); void(*pm)(structuart_port*,unsignedintstate, unsignedintoldstate); void(*wake_peer)(structuart_port*); /* *Returnastringdescribingthetypeoftheport */ constchar*(*type)(structuart_port*); /* *ReleaseIOandmemoryresourcesusedbytheport. *Thisincludesiounmapifnecessary. */ void(*release_port)(structuart_port*); /* *RequestIOandmemoryresourcesusedbytheport. *Thisincludesiomappingtheportifnecessary. */ int(*request_port)(structuart_port*); void(*config_port)(structuart_port*,int); int(*verify_port)(structuart_port*,structserial_struct*); int(*ioctl)(structuart_port*,unsignedint,unsignedlong); #ifdefCONFIG_CONSOLE_POLL int(*poll_init)(structuart_port*); void(*poll_put_char)(structuart_port*,unsignedchar); int(*poll_get_char)(structuart_port*); #endif };
tx_empty():檢查串口的發(fā)送緩沖區(qū)是否為空,用于判斷是否可以發(fā)送數(shù)據(jù)。
set_mctrl():設(shè)置串口的 modem 控制信號(hào),如 RTS、DTR 等。
get_mctrl():獲取串口的 modem 控制信號(hào)。
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():?jiǎn)⒂么诘?modem 狀態(tài)檢測(cè)功能。
break_ctl():發(fā)送一個(gè) break 信號(hào)。
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():請(qǐng)求串口的 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 端口的操作。
structuart_state{ structtty_portport; enumuart_pm_statepm_state; structcirc_bufxmit; structuart_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_OFF 或 UART_PM_STATE_UNDEFINED。
struct circ_buf xmit:表示串口設(shè)備的發(fā)送緩沖區(qū),用于存儲(chǔ)待發(fā)送的數(shù)據(jù)。
struct uart_port *uart_port:表示該串口設(shè)備對(duì)應(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注冊(cè)到kernel中,一般在驅(qū)動(dòng)模塊的init接口中被調(diào)用。
intuart_register_driver(structuart_driver*drv) { structtty_driver*normal; inti,retval; BUG_ON(drv->state); drv->state=kzalloc(sizeof(structuart_state)*drv->nr,GFP_KERNEL); if(!drv->state) gotoout; normal=alloc_tty_driver(drv->nr); if(!normal) gotoout_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); /* *InitialisetheUARTstate(s). */ for(i=0;inr;i++){ structuart_state*state=drv->state+i; structtty_port*port=&state->port; tty_port_init(port); port->ops=&uart_port_ops; } retval=tty_register_driver(normal); if(retval>=0) returnretval; for(i=0;inr;i++) tty_port_destroy(&drv->state[i].port); put_tty_driver(normal); out_kfree: kfree(drv->state); out: return-ENOMEM; }
uart_register_driver()注冊(cè)所做工作如下:
根據(jù)driver支持的最大設(shè)備數(shù),申請(qǐng)n個(gè) uart_state 空間,每一個(gè) uart_state 都有一個(gè) uart_port 。
接著它會(huì)分配一個(gè) tty_driver 對(duì)象,并初始化它的各個(gè)屬性,如 driver_name,name,major,minor_start 等等。這些屬性是用于在 TTY 子系統(tǒng)中創(chuàng)建 tty 設(shè)備的,它們的值來自于 uart_driver 對(duì)象中指定的值。
接下來,它會(huì)在 tty_driver 中設(shè)置 tty 操作,其中 tty_ops 是一個(gè)結(jié)構(gòu)體,定義了 UART 串行接口所需要的函數(shù)。這些函數(shù)在串口設(shè)備注冊(cè)后,當(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)對(duì)象,并初始化它們。這些狀態(tài)對(duì)象被存儲(chǔ)在 uart_driver 對(duì)象的 state 字段中。每個(gè) UART 設(shè)備狀態(tài)對(duì)象包含一個(gè) tty_port 對(duì)象,其中存儲(chǔ)了關(guān)于該串口設(shè)備的信息,例如流控、字長(zhǎng)、奇偶校驗(yàn)等等。在此處, tty_port 的操作被設(shè)置為 uart_port_ops,它包含了具體實(shí)現(xiàn) UART 串行接口所需的函數(shù)。
最后會(huì)調(diào)用 tty_register_driver() 函數(shù)來向內(nèi)核注冊(cè) tty 驅(qū)動(dòng)程序,并將驅(qū)動(dòng)程序的 tty_driver 結(jié)構(gòu)體與 uart_driver 結(jié)構(gòu)體相關(guān)聯(lián)。
如果注冊(cè)失敗,該函數(shù)將釋放之前分配的內(nèi)存。如果注冊(cè)成功,該函數(shù)將返回 0,否則將返回一個(gè)負(fù)的錯(cuò)誤碼。
總結(jié)一句話:tty serial core底層驅(qū)動(dòng)層和tty層之間的聯(lián)系需要從uart_register_driver()中連接,tty_driver是在uart_driver注冊(cè)過程中構(gòu)建的。
uart_unregister_driver
uart_unregister_driver是一個(gè)Linux內(nèi)核中的串口驅(qū)動(dòng)反注冊(cè)函數(shù),用于將之前注冊(cè)的驅(qū)動(dòng)程序與系統(tǒng)中的串口設(shè)備取消關(guān)聯(lián)。
/** *uart_unregister_driver-removeadriverfromtheuartcorelayer *@drv:lowleveldriverstructure * *Removeallreferencestoadriverfromthecoredriver.Thelow *leveldrivermusthaveremovedallitsportsviathe *uart_remove_one_port()ifitregisteredthemwithuart_add_one_port(). *(ie,drv->port==NULL) */ voiduart_unregister_driver(structuart_driver*drv) { structtty_driver*p=drv->tty_driver; unsignedinti; /*獲取與該驅(qū)動(dòng)程序關(guān)聯(lián)的tty_driver實(shí)例*/ tty_unregister_driver(p); /*取消注冊(cè)驅(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;inr;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)表中,并注冊(cè)TTY端口設(shè)備,讓用戶空間能夠通過該設(shè)備與UART通信。
/** *uart_add_one_port-attachadriver-definedportstructure *@drv:pointertotheuartlowleveldriverstructureforthisport *@uport:uartportstructuretouseforthisport. * *Thisallowsthedrivertoregisteritsownuart_portstructure *withthecoredriver.Themainpurposeistoallowthelow *leveluartdriverstoexpanduart_port,ratherthanhavingyet *morelevelsofstructures. */ intuart_add_one_port(structuart_driver*drv,structuart_port*uport) { structuart_state*state; structtty_port*port; intret=0; structdevice*tty_dev; intnum_groups; /*檢查是否在中斷上下文中,如果是則直接返回錯(cuò)誤*/ BUG_ON(in_interrupt()); /*檢查所添加的端口是否超出驅(qū)動(dòng)程序支持的范圍,如果是則返回EINVAL*/ if(uport->line>=drv->nr) return-EINVAL; /*獲取該端口所對(duì)應(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; gotoout; } /*鏈接端口和驅(qū)動(dòng)程序狀態(tài)表,并進(jìn)行相應(yīng)的初始化工作,包括PM狀態(tài)、控制臺(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; /* *Ifthisportisaconsole,thenthespinlockisalready *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; gotoout; } uport->tty_groups[0]=&tty_dev_attr_group; if(uport->attr_group) uport->tty_groups[1]=uport->attr_group; /*注冊(cè)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); /*如果注冊(cè)成功,將該設(shè)備標(biāo)記為可喚醒*/ if(likely(!IS_ERR(tty_dev))){ device_set_wakeup_capable(tty_dev,1); }else{ dev_err(uport->dev,"Cannotregisterttydeviceonline%d ", uport->line); } /* *EnsureUPF_DEADisnotset. */ uport->flags&=~UPF_DEAD; out: mutex_unlock(&port->mutex); mutex_unlock(&port_mutex); returnret; }
uart_remove_one_port
uart_remove_one_port用于從核心驅(qū)動(dòng)程序中分離(斷開)一個(gè)指定的端口結(jié)構(gòu)。
/** *uart_remove_one_port-detachadriverdefinedportstructure *@drv:pointertotheuartlowleveldriverstructureforthisport *@uport:uartportstructureforthisport * *Thisunhooks(andhangsup)thespecifiedportstructurefromthe *coredriver.Nofurthercallswillbemadetothelow-levelcode *forthisport. */ intuart_remove_one_port(structuart_driver*drv,structuart_port*uport) { structuart_state*state=drv->state+uport->line; structtty_port*port=&state->port; structtty_struct*tty; intret=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,"Removingwrongport:%p!=%p ", 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; gotoout; } /*鎖定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è)備對(duì)應(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); } /*如果該端口用作控制臺(tái),則使用unregister_console()函數(shù)取消該端口的控制臺(tái)注冊(cè)*/ if(uart_console(uport)) unregister_console(uport->cons); /*根據(jù)uport->type的值來釋放端口的IO和內(nèi)存資源,如果uport->type的值為PORT_UNKNOWN,則表示沒有對(duì)應(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對(duì)應(yīng)的端口不再與uport相關(guān)聯(lián)。*/ uport->type=PORT_UNKNOWN; state->uart_port=NULL; out: mutex_unlock(&port_mutex); returnret; }
uart_write_wakeup
uart_write_wakeupuart_write_wakeup喚醒上層因向串口端口寫數(shù)據(jù)而阻塞的進(jìn)程,通常在串口發(fā)送中斷處理函數(shù)中調(diào)用該函數(shù)。
/* *Thisroutineisusedbytheinterrupthandlertoscheduleprocessingin *thesoftwareinterruptportionofthedriver. */ voiduart_write_wakeup(structuart_port*port) { structuart_state*state=port->state; /* *Thismeansyoucalledthisfunction_after_theportwas *closed.Nocookieforyou. */ 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),并更改端口的電源管理狀態(tài)。
intuart_suspend_port(structuart_driver*drv,structuart_port*uport) { structuart_state*state=drv->state+uport->line; structtty_port*port=&state->port; structdevice*tty_dev; structuart_matchmatch={uport,drv}; /*給port加鎖,以確保在執(zhí)行其他操作時(shí)不會(huì)發(fā)生競(jìng)爭(zhēng)條件*/ 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); return0; } /*如果找到了子設(shè)備但該設(shè)備不能喚醒系統(tǒng),則釋放tty_dev*/ put_device(tty_dev); /*Nothingtodoiftheconsoleisnotsuspending*/ /*如果控制臺(tái)未啟用掛起并且uport是控制臺(tái),則跳轉(zhuǎn)到unlock解鎖*/ if(!console_suspend_enabled&&uart_console(uport)) gotounlock; /*將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){ conststructuart_ops*ops=uport->ops; inttries; 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); /* *Waitforthetransmittertoempty. */ for(tries=3;!ops->tx_empty(uport)&&tries;tries--) msleep(10); if(!tries) dev_err(uport->dev,"%s%d:Unabletodraintransmitter ", drv->dev_name, drv->tty_driver->name_base+uport->line); ops->shutdown(uport); } /* *Disabletheconsoledevicebeforesuspending. */ /**/ /*如果uport是控制臺(tái),則停止控制臺(tái)*/ 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); return0; }
uart_resume_port
uart_resume_port作用是恢復(fù)一個(gè)已經(jīng)掛起的UART端口。
intuart_resume_port(structuart_driver*drv,structuart_port*uport) { structuart_state*state=drv->state+uport->line; structtty_port*port=&state->port; structdevice*tty_dev; structuart_matchmatch={uport,drv}; structktermiostermios; mutex_lock(&port->mutex); /*使用device_find_child搜索與名為match的structuart_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); return0; } /*函數(shù)將uport->suspended設(shè)置為0*/ put_device(tty_dev); uport->suspended=0; /* *Re-enabletheconsoledeviceaftersuspending. */ /*如果端口是控制臺(tái)端口,則函數(shù)將termios結(jié)構(gòu)設(shè)置為控制臺(tái)cflag設(shè)置*/ if(uart_console(uport)){ /* *Firsttrytousetheconsolecflagsetting. */ memset(&termios,0,sizeof(structktermios)); termios.c_cflag=uport->cons->cflag; /* *Ifthat'sunset,usethettytermiossetting. */ if(port->tty&&termios.c_cflag==0) termios=port->tty->termios; /*如果啟用了控制臺(tái)掛起,則函數(shù)使用uart_change_pm將電源管理狀態(tài)更改為打開狀態(tài),使用uport->ops->set_termios設(shè)置termios,并使用console_start啟動(dòng)控制臺(tái)*/ 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){ conststructuart_ops*ops=uport->ops; intret; /*如果端口已掛起,則函數(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)){ /*Protectedbyportmutexfornow*/ structtty_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{ /* *Failedtoresume-maybehardwarewentaway? *Clearthe"initialized"flagsowewon'ttry *tocallthelowleveldriversshutdownmethod. */ /*如果端口無(wú)法恢復(fù),則函數(shù)清除ASYNCB_INITIALIZED位并調(diào)用uart_shutdown*/ uart_shutdown(tty,state); } } clear_bit(ASYNCB_SUSPENDED,&port->flags); } mutex_unlock(&port->mutex); return0; }
uart_get_baud_rate
uart_get_baud_rate,該函數(shù)的作用是根據(jù)給定的終端設(shè)置和范圍,獲取一個(gè)可用的波特率。如果無(wú)法獲取滿足要求的波特率,則會(huì)盡可能地使用最接近的波特率。
/** *uart_get_baud_rate-returnbaudrateforaparticularport *@port:uart_portstructuredescribingtheportinquestion. *@termios:desiredtermiossettings. *@old:oldtermios(orNULL) *@min:minimumacceptablebaudrate *@max:maximumacceptablebaudrate * *Decodethetermiosstructureintoanumericbaudrate, *takingaccountofthemagic38400baudrate(withspd_* *flags),andmappingthe%B0rateto9600baud. * *Ifthenewbaudrateisinvalid,trytheoldtermiossetting. *Ifit'sstillinvalid,wetry9600baud. * *Updatethe@termiosstructuretoreflectthebaudrate *we'reactuallygoingtobeusing.Don'tdothisforthecase *whereB0isrequested("hangup"). */ unsignedint uart_get_baud_rate(structuart_port*port,structktermios*termios, structktermios*old,unsignedintmin,unsignedintmax) { unsignedinttry; unsignedintbaud; unsignedintaltbaud; inthung_up=0; upf_tflags=port->flags&UPF_SPD_MASK; switch(flags){ caseUPF_SPD_HI: altbaud=57600; break; caseUPF_SPD_VHI: altbaud=115200; break; caseUPF_SPD_SHI: altbaud=230400; break; caseUPF_SPD_WARP: altbaud=460800; break; default: altbaud=38400; break; } for(try=0;try2;?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; } /* *Asalastresort,iftherangecannotbemetthenclipto *thenearestchipsupportedrate. */ 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(即請(qǐng)求“掛起”),則設(shè)置波特率為 9600。如果波特率在 min 和 max 范圍內(nèi),則返回該波特率。
如果第一次獲取的波特率為 0,則函數(shù)會(huì)嘗試使用舊的終端設(shè)置。
如果仍然無(wú)法滿足要求,函數(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í)鐘分頻器值對(duì)應(yīng)著波特率,即時(shí)鐘分頻器值越小,波特率越高,傳輸速度越快。
/** *uart_get_divisor-returnuartclockdivisor *@port:uart_portstructuredescribingtheport. *@baud:desiredbaudrate * *Calculatetheuartclockdivisorfortheport. */ unsignedint uart_get_divisor(structuart_port*port,unsignedintbaud) { unsignedintquot; /* *Oldcustomspeedhandling. */ if(baud==38400&&(port->flags&UPF_SPD_MASK)==UPF_SPD_CUST) quot=port->custom_divisor; else quot=DIV_ROUND_CLOSEST(port->uartclk,16*baud); returnquot; }
該函數(shù)所作工作如下
首先根據(jù)給定的波特率計(jì)算出 UART 時(shí)鐘周期的長(zhǎng)度 period,這個(gè)周期的長(zhǎng)度是通過 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ù)男省6瑫r(shí)時(shí)間則是指在 FIFO 中沒有數(shù)據(jù)傳輸時(shí),等待多長(zhǎng)時(shí)間后自動(dòng)清空 FIFO。超時(shí)時(shí)間的設(shè)置可以影響串口傳輸?shù)姆€(wěn)定性和效率。
/** *uart_update_timeout-updateper-portFIFOtimeout. *@port:uart_portstructuredescribingtheport *@cflag:termioscflagvalue *@baud:speedoftheport * *SettheportFIFOtimeoutvalue.The@cflagvalueshould *reflecttheactualhardwaresettings. */ void uart_update_timeout(structuart_port*port,unsignedintcflag, unsignedintbaud) { unsignedintbits; /*bytesizeandparity*/ switch(cflag&CSIZE){ caseCS5: bits=7; break; caseCS6: bits=8; break; caseCS7: bits=9; break; default: bits=10; break;/*CS8*/ } if(cflag&CSTOPB) bits++; if(cflag&PARENB) bits++; /* *Thetotalnumberofbitstobetransmittedinthefifo. */ bits=bits*port->fifosize; /* *Figurethetimeouttosendtheabovenumberofbits. *Add.02secondsofslop */ 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è)串口端口是否相等。
/* *Arethetwoportsequivalent? */ intuart_match_port(structuart_port*port1,structuart_port*port2) { if(port1->iotype!=port2->iotype) return0; switch(port1->iotype){ caseUPIO_PORT: return(port1->iobase==port2->iobase); caseUPIO_HUB6: return(port1->iobase==port2->iobase)&& (port1->hub6==port2->hub6); caseUPIO_MEM: caseUPIO_MEM32: caseUPIO_MEM32BE: caseUPIO_AU: caseUPIO_TSI: return(port1->mapbase==port2->mapbase); } return0; }
根據(jù)兩個(gè)串口端口的 iotype 屬性進(jìn)行比較,如果不相等,則兩個(gè)端口不相等,函數(shù)返回 0。
根據(jù) iotype 屬性的不同,比較兩個(gè)端口的其他屬性。對(duì)于 UPIO_PORT 和 UPIO_HUB6 類型的端口,比較它們的 iobase 和 hub6 屬性是否相等;對(duì)于其他類型的端口,比較它們的 mapbase 屬性是否相等。如果所有屬性都相等,則兩個(gè)端口相等,函數(shù)返回 1,否則返回 0。
uart_console_write
uart_console_write用于將控制臺(tái)消息寫入串口。
在嵌入式系統(tǒng)中,通常需要將控制臺(tái)輸出重定向到串口,以便進(jìn)行調(diào)試和日志記錄。該函數(shù)實(shí)現(xiàn)了將一個(gè)字符串寫入串口的操作,其中需要將字符串中的換行符轉(zhuǎn)換為回車換行符。
/** *uart_console_write-writeaconsolemessagetoaserialport *@port:theporttowritethemessage *@s:arrayofcharacters *@count:numberofcharactersinstringtowrite *@putchar:functiontowritecharactertoport */ voiduart_console_write(structuart_port*port,constchar*s, unsignedintcount, void(*putchar)(structuart_port*,int)) { unsignedinti; for(i=0;i
該函數(shù)的實(shí)現(xiàn)主要是遍歷字符串中的所有字符,并將每個(gè)字符寫入串口。在寫入字符之前,需要判斷該字符是否為換行符。如果是換行符,則需要先將其轉(zhuǎn)換為回車換行符,再寫入串口。
總結(jié)
對(duì)接底層的部分,Kernel 主要是提供了兩個(gè)接口:
1、uart_register_driver (一次調(diào)用)
2、uart_add_one_port (多次調(diào)用)
通過這兩個(gè)接口,實(shí)現(xiàn)了芯片將自己的 UART 對(duì)接到 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 對(duì)串口的操作集(可能一個(gè),可能多個(gè))
所以從結(jié)構(gòu)上來看,整個(gè)對(duì)接過程為:
這里有一點(diǎn)需要特別注意,在對(duì)接底層的部分中,Kernel 定義了一個(gè)結(jié)構(gòu)體叫:struct uart_ops
在 tty 層,對(duì) tty_driver 初始化的時(shí)候(serial_core.c),調(diào)用到:
tty_set_operations(normal,&uart_ops);
而他的實(shí)現(xiàn)是:
voidtty_set_operations(structtty_driver*driver,conststructtty_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)定義的:
staticconststructtty_operationsuart_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, #ifdefCONFIG_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, #ifdefCONFIG_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),容易讓人眼花~~
-
uart
+關(guān)注
關(guān)注
22文章
1244瀏覽量
101797 -
數(shù)據(jù)結(jié)構(gòu)
+關(guān)注
關(guān)注
3文章
573瀏覽量
40235 -
串口驅(qū)動(dòng)
+關(guān)注
關(guān)注
2文章
82瀏覽量
18757
原文標(biāo)題:【驅(qū)動(dòng)】串口驅(qū)動(dòng)分析(三)-serial driver
文章出處:【微信號(hào):嵌入式與Linux那些事,微信公眾號(hào):嵌入式與Linux那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論