簡(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ù)男?。而超時(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文章
1254瀏覽量
102964 -
數(shù)據(jù)結(jié)構(gòu)
+關(guān)注
關(guān)注
3文章
573瀏覽量
40540 -
串口驅(qū)動(dòng)
+關(guān)注
關(guān)注
2文章
86瀏覽量
18997
原文標(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)先 登錄
【開源的串口可視化工具——Serial Studio】
串口設(shè)備框架serial_v2源碼分析-阻塞模式
關(guān)于串口調(diào)試助手和virtual serial port driver
請(qǐng)問driver/tty/serial/8250/8250-omap.c驅(qū)動(dòng)是什么串口驅(qū)動(dòng)?
什么是串口驅(qū)動(dòng)器?
虛擬串口Virtual Serial Port Driver使用報(bào)錯(cuò)怎么解決?
Open Universal Serial Bus Driv
Serial Monitor (串口監(jiān)視、檢測(cè)、分析工具)v
Proteus串口資料COMPIM Serial Port
Virtual Serial Port Driver 6.9(虛擬串口)
經(jīng)典實(shí)用USB轉(zhuǎn)串口驅(qū)動(dòng)STM32 Virtual COM Port Driver(V1.3.1)
matlab中的串口軟件serial_1
PL-2303 Vista Driver Installer12
NodeMCU V3.0 Arduino開發(fā)之串口使用

串口驅(qū)動(dòng)分析之serial driver
評(píng)論