0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

串口驅(qū)動分析之serial driver

嵌入式與Linux那些事 ? 來源:嵌入式與Linux那些事 ? 2024-09-04 14:23 ? 次閱讀

簡介

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

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

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

struct uart_driver

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

uart_driver 結(jié)構(gòu)體表示 UART 驅(qū)動, 它定義在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ū)動程序的擁有者模塊的指針,即加載該驅(qū)動程序的內(nèi)核模塊。

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

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

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

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

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

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

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

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

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

struct uart_port

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

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

當(dāng)應(yīng)用程序通過打開設(shè)備節(jié)點(diǎn)來訪問串口設(shè)備時,內(nèi)核會通過設(shè)備節(jié)點(diǎn)獲取對應(yīng)的 struct uart_port 數(shù)據(jù)結(jié)構(gòu),然后通過這個數(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 控制信號。

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

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è)備的時鐘頻率。

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: 用于存儲串口設(shè)備的統(tǒng)計信息。

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

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

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

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

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

unsigned int custom_divisor:自定義除數(shù),用于實(shí)現(xiàn)非標(biāo)準(zhǔn)波特率。這個值通常由驅(qū)動程序設(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ū)動程序設(shè)置,用于將UART端口的物理地址映射到虛擬地址。

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

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

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ù)的指針。這個指針通常由驅(qū)動程序使用,用于保存驅(qū)動程序特定的數(shù)據(jù)。

struct uart_ops

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

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

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

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

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

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

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 控制信號,如 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ā)送一個 XON 或 XOFF 字符,用于流控。

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

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

break_ctl():發(fā)送一個 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)可以包含多個,因此可以在同一時刻使用多個 UART 狀態(tài)來管理多個 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ū),用于存儲待發(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ù)據(jù)將被存儲到 xmit 緩沖區(qū)中,并且將觸發(fā)串口驅(qū)動程序的數(shù)據(jù)發(fā)送處理函數(shù)。這個函數(shù)會從 xmit 緩沖區(qū)中取出數(shù)據(jù),并通過 uart_port 中的函數(shù)指針將數(shù)據(jù)發(fā)送到物理串口。在發(fā)送數(shù)據(jù)時,驅(qū)動程序還會根據(jù)串口的流控狀態(tài)進(jìn)行數(shù)據(jù)流控制。

當(dāng)收到數(shù)據(jù)時,數(shù)據(jù)將被存儲到 port 的接受緩沖區(qū)中,并且將觸發(fā)串口驅(qū)動程序的數(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ū)動模塊的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()注冊所做工作如下:

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

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

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

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

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

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

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

uart_unregister_driver

uart_unregister_driver是一個Linux內(nèi)核中的串口驅(qū)動反注冊函數(shù),用于將之前注冊的驅(qū)動程序與系統(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ū)動程序關(guān)聯(lián)的tty_driver實(shí)例*/
tty_unregister_driver(p);

/*取消注冊驅(qū)動程序,將它與系統(tǒng)中的tty設(shè)備斷開關(guān)聯(lián)*/
put_tty_driver(p);

/*釋放該tty_driver實(shí)例,如果此時該實(shí)例的使用計數(shù)為零,即沒有其他模塊在使用該實(shí)例,那么它將會被完全卸載并釋放所有內(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用于將一個UART端口添加到UART驅(qū)動程序的狀態(tài)表中,并注冊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;

/*檢查是否在中斷上下文中,如果是則直接返回錯誤*/
BUG_ON(in_interrupt());

/*檢查所添加的端口是否超出驅(qū)動程序支持的范圍,如果是則返回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;
gotoout;
}

/*鏈接端口和驅(qū)動程序狀態(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;

/*
*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;

/*注冊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,"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ū)動程序中分離(斷開)一個指定的端口結(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端口指針,如果不是則打印一條錯誤消息*/
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è)備對應(yīng)的tty結(jié)構(gòu)體,并使用tty_vhangup()函數(shù)關(guān)閉該tty設(shè)備的控制終端。最后,使用tty_kref_put()函數(shù)釋放tty結(jié)構(gòu)體的引用計數(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,表示該端口不再存在。同時將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);

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

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í)行其他操作時不會發(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);
return0;
}
/*如果找到了子設(shè)備但該設(shè)備不能喚醒系統(tǒng),則釋放tty_dev*/
put_device(tty_dev);

/*Nothingtodoiftheconsoleisnotsuspending*/
/*如果控制臺未啟用掛起并且uport是控制臺,則跳轉(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是控制臺,則停止控制臺*/
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ù)一個已經(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.
*/
/*如果端口是控制臺端口,則函數(shù)將termios結(jié)構(gòu)設(shè)置為控制臺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;
/*如果啟用了控制臺掛起,則函數(shù)使用uart_change_pm將電源管理狀態(tài)更改為打開狀態(tài),使用uport->ops->set_termios設(shè)置termios,并使用console_start啟動控制臺*/
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啟動端口*/
ret=ops->startup(uport);
if(ret==0){
/*如果端口成功啟動,則使用uart_change_speed更改端口速度,使用ops->start_tx啟動傳輸,并在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.
*/
/*如果端口無法恢復(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è)置和范圍,獲取一個可用的波特率。如果無法獲取滿足要求的波特率,則會盡可能地使用最接近的波特率。

/**
*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;try=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)志位解析出一個備用波特率 altbaud。

函數(shù)會嘗試兩次獲取波特率。第一次,函數(shù)會從 termios 中解析出當(dāng)前波特率,如果它等于 38400,則將波特率設(shè)置為備用波特率 altbaud。如果波特率等于 0(即請求“掛起”),則設(shè)置波特率為 9600。如果波特率在 min 和 max 范圍內(nèi),則返回該波特率。

如果第一次獲取的波特率為 0,則函數(shù)會嘗試使用舊的終端設(shè)置。

如果仍然無法滿足要求,函數(shù)會將波特率剪裁到最接近的支持的波特率。剪裁的方式是,如果波特率小于等于最小值 min,則設(shè)置波特率為 min + 1,否則設(shè)置波特率為 max - 1。

uart_get_divisor

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

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

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

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

uart_update_timeout

uart_update_timeout用于設(shè)置串口的 FIFO 超時時間。FIFO(First-In-First-Out)是串口硬件中用于緩存數(shù)據(jù)的一種常見結(jié)構(gòu),它可以提高串口傳輸?shù)男?。而超時時間則是指在 FIFO 中沒有數(shù)據(jù)傳輸時,等待多長時間后自動清空 FIFO。超時時間的設(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 值,計算出每個字節(jié)需要傳輸?shù)奈粩?shù) bits。根據(jù) cflag 中的 CSIZE 標(biāo)志位,確定每個字節(jié)的位數(shù)(5、6、7 或 8 位),并根據(jù) CSTOPB 和 PARENB 標(biāo)志位,增加停止位和奇偶校驗(yàn)位的位數(shù)。

將每個字節(jié)需要傳輸?shù)奈粩?shù) bits 乘以 FIFO 的大小,得到總共需要傳輸?shù)奈粩?shù)。

根據(jù)波特率和總共需要傳輸?shù)奈粩?shù),計算出超時時間。將總共需要傳輸?shù)奈粩?shù)除以波特率,得到傳輸這些數(shù)據(jù)所需要的時間,再加上一些額外的時間(0.02 秒)作為緩沖,得到超時時間。

最后,將計算出來的超時時間賦值給端口結(jié)構(gòu)體中的 timeout 成員變量,從而完成 FIFO 超時時間的設(shè)置。

uart_match_port

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

/*
*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ù)兩個串口端口的 iotype 屬性進(jìn)行比較,如果不相等,則兩個端口不相等,函數(shù)返回 0。

根據(jù) iotype 屬性的不同,比較兩個端口的其他屬性。對于 UPIO_PORT 和 UPIO_HUB6 類型的端口,比較它們的 iobase 和 hub6 屬性是否相等;對于其他類型的端口,比較它們的 mapbase 屬性是否相等。如果所有屬性都相等,則兩個端口相等,函數(shù)返回 1,否則返回 0。

uart_console_write

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

嵌入式系統(tǒng)中,通常需要將控制臺輸出重定向到串口,以便進(jìn)行調(diào)試和日志記錄。該函數(shù)實(shí)現(xiàn)了將一個字符串寫入串口的操作,其中需要將字符串中的換行符轉(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)主要是遍歷字符串中的所有字符,并將每個字符寫入串口。在寫入字符之前,需要判斷該字符是否為換行符。如果是換行符,則需要先將其轉(zhuǎn)換為回車換行符,再寫入串口。

總結(jié)

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

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

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

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

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

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

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

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

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

71ea8d618cd911e8ca713408e39c1bbd.png
b1f23b238e2e3853a9dab36cc3dcd8ca.png

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

在 tty 層,對 tty_driver 初始化的時候(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 并非那個 struct uart_ops,而是這個 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
};

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

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • uart
    +關(guān)注

    關(guān)注

    22

    文章

    1214

    瀏覽量

    100993
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    569

    瀏覽量

    40063
  • 串口驅(qū)動
    +關(guān)注

    關(guān)注

    2

    文章

    82

    瀏覽量

    18570

原文標(biāo)題:【驅(qū)動】串口驅(qū)動分析(三)-serial driver

文章出處:【微信號:嵌入式與Linux那些事,微信公眾號:嵌入式與Linux那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    【開源的串口可視化工具——Serial Studio】

    分享一個開源的串口項(xiàng)目——Serial Studio,這是一個強(qiáng)大的數(shù)據(jù)可視化軟件,支持串口通信,串口終端,網(wǎng)絡(luò)通信 TCP/UDP,MQTT通信協(xié)議。這個項(xiàng)目遵循MIT協(xié)議,所以是可
    的頭像 發(fā)表于 01-18 15:03 ?1.2w次閱讀
    【開源的<b class='flag-5'>串口</b>可視化工具——<b class='flag-5'>Serial</b> Studio】

    串口設(shè)備框架serial_v2源碼分析-阻塞模式

    serial_v2中,串口設(shè)備以應(yīng)用層視角,即阻塞模式或非阻塞模式來作為該串口設(shè)備的開啟標(biāo)志.
    的頭像 發(fā)表于 09-14 11:34 ?1796次閱讀

    USB To Serial Driver

    USB To Serial Driver  
    發(fā)表于 09-28 15:57

    關(guān)于串口調(diào)試助手和virtual serial port driver

    想問下proteus做單片機(jī)和pc通信提示需要virtual serial port driver串口調(diào)試助手,想問下怎么用,下載下來的這兩個好像不起作用
    發(fā)表于 11-17 16:55

    請問driver/tty/serial/8250/8250-omap.c驅(qū)動是什么串口驅(qū)動?

    是CONFIG_SERIAL_8250,而不是CONFIG_SERIAL_OMAP,那么這里為什么要配置成8250呢?8250的串口驅(qū)動是干嘛用的?
    發(fā)表于 06-21 02:01

    什么是串口驅(qū)動器?

    什么是串口驅(qū)動器?串口驅(qū)動的作用?以及它如何適用于GSM M66模塊。以上來自于谷歌翻譯以下為原文 what is serial
    發(fā)表于 01-14 15:24

    虛擬串口Virtual Serial Port Driver使用報錯怎么解決?

    使用虛擬串口到底是干什么?虛擬串口Virtual Serial Port Driver使用報錯怎么解決?
    發(fā)表于 02-22 06:57

    Open Universal Serial Bus Driv

    Open Universal Serial Bus Driver Interface (OpenUSBDI) Specification This document specifies
    發(fā)表于 04-11 19:16 ?15次下載

    Serial Monitor (串口監(jiān)視、檢測、分析工具)v

    Serial Monitor:Serial Monitor是一款功能強(qiáng)大的串口監(jiān)視、檢測、分析工具,軟件使用更加簡單,尤其適合開發(fā)人員使用。 
    發(fā)表于 05-26 09:03 ?86次下載

    Proteus串口資料COMPIM Serial Port

    Proteus串口資料COMPIM Serial Port Model The COMPIM model is a Physical Interface Model of a serial port. Incoming
    發(fā)表于 04-17 16:13 ?0次下載

    Virtual Serial Port Driver 6.9(虛擬串口)

    電子發(fā)燒友網(wǎng)站提供《Virtual Serial Port Driver 6.9(虛擬串口).rar》資料免費(fèi)下載
    發(fā)表于 08-02 00:00 ?38次下載

    經(jīng)典實(shí)用USB轉(zhuǎn)串口驅(qū)動STM32 Virtual COM Port Driver(V1.3.1)

    電子發(fā)燒友網(wǎng)站提供《經(jīng)典實(shí)用USB轉(zhuǎn)串口驅(qū)動STM32 Virtual COM Port Driver(V1.3.1).zip》資料免費(fèi)下載
    發(fā)表于 08-01 10:32 ?1283次下載

    matlab中的串口軟件serial_1

    電子發(fā)燒友網(wǎng)站提供《matlab中的串口軟件serial_1.zip》資料免費(fèi)下載
    發(fā)表于 07-07 16:32 ?1次下載

    PL-2303 Vista Driver Installer12

    PL-2303 Vista Driver Installer串口驅(qū)動
    發(fā)表于 12-09 15:45 ?1次下載

    NodeMCU V3.0 Arduino開發(fā)串口使用

    NodeMCU V3.0 Arduino開發(fā)串口使用串口使用串口使用void setup() { // put your setup code here, to run once
    發(fā)表于 11-16 09:51 ?1次下載
    NodeMCU V3.0 Arduino開發(fā)<b class='flag-5'>之</b><b class='flag-5'>串口</b>使用