linux源碼相關(guān)文件:
serial-core.c
include/linux/serial_core.h
unsetunset
一、底層串行硬件驅(qū)動(dòng)程序unset
底層串行硬件的驅(qū)動(dòng)程序負(fù)責(zé)向serial核心驅(qū)動(dòng)程序提供由struct uart_port定義的端口信息和一組由struct uart_ops定義的控制方法,底層驅(qū)動(dòng)程序還負(fù)責(zé)處理端口的中斷,并提供對控制臺(tái)的支持。
unsetunset
二、Console支持unset
serial核心提供了一些助手函數(shù):
uart_get_console()識(shí)別正確的端口結(jié)構(gòu)。
uart_parse_options()解析命令行參數(shù)。
uart_console_write()用于執(zhí)行逐字符寫入,將換行符轉(zhuǎn)換為CRLF序列。在驅(qū)動(dòng)程序編寫的時(shí)候建議使用此函數(shù),而不是實(shí)現(xiàn)新的寫入接口。
unsetunset
三、鎖支持
unset
底層硬件驅(qū)動(dòng)程序負(fù)責(zé)使用port->lock執(zhí)行必要的鎖定。支持兩把鎖:一個(gè)是端口自旋鎖,另一個(gè)是overall信號(hào)量。從uart核心驅(qū)動(dòng)程序的角度來看,port->lock用于鎖定以下的數(shù)據(jù):
port->mctrl port->icount port->state->xmit.head(circ_buf->head) port->state->xmit.tail(circ_buf->tail)
底層驅(qū)動(dòng)程序可以自由地使用該鎖來實(shí)現(xiàn)額外的鎖定,port_mutex互斥量用于防止在不適當(dāng)?shù)臅r(shí)間添加、刪除或重新配置端口。
unsetunset
四、核心數(shù)據(jù)結(jié)構(gòu)unset
1、struct uart_driver
struct uart_driver結(jié)構(gòu)表示具體UART驅(qū)動(dòng)。該結(jié)構(gòu)定義如下(/include/linux/serial_core.h):
structuart_driver{
structmodule*owner;//驅(qū)動(dòng)模塊的擁有者
constchar*driver_name;//驅(qū)動(dòng)名稱
constchar*dev_name;//設(shè)備名稱
intmajor;//主設(shè)備號(hào)
intminor;//從設(shè)備號(hào)
intnr;
structconsole*cons;//console
/*
*theseareprivate;thelowleveldrivershouldnot
*touchthese;theyshouldbeinitialisedtoNULL
*/
structuart_state*state;//uart狀態(tài)
structtty_driver*tty_driver;//描述ttydriver
};
2、struct uart_port
struct uart_port表示一個(gè)具體的port,該結(jié)構(gòu)定義如下(include/linux/serial_core.h):
structuart_port{
spinlock_tlock;/*port鎖*/
unsignedlongiobase;/*輸入/輸出地址*/
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;/*指向父狀態(tài)的指針*/
structuart_icounticount;/*通信信息*/
structconsole*cons;/*structconsole,ifany*/
#ifdefined(CONFIG_SERIAL_CORE_CONSOLE)||defined(SUPPORT_SYSRQ)
unsignedlongsysrq;/*sysrqtimeout*/
#endif
/*flagsmustbeupdatedwhileholdingportmutex*/
upf_tflags;
#if__UPF_CHANGE_MASK>ASYNC_FLAGS
#errorChangemasknotequivalenttouserspace-visiblebitdefines
#endif
/*
*Mustholdtermios_rwsem,portmutexandportlocktochange;
*canholdanyonelocktoread.
*/
upstat_tstatus;
inthw_stopped;/*sw-assistedCTSflowstate*/
unsignedintmctrl;/*當(dāng)前調(diào)制解調(diào)器CTRL設(shè)置*/
unsignedinttimeout;/*character-basedtimeout*/
unsignedinttype;/*port類型*/
conststructuart_ops*ops;
unsignedintcustom_divisor;
unsignedintline;/*port索引號(hào)*/
unsignedintminor;
resource_size_tmapbase;/*用于ioremap*/
resource_size_tmapsize;
structdevice*dev;/*父device*/
unsignedcharhub6;/*應(yīng)該在8250驅(qū)動(dòng)程序中使用*/
unsignedcharsuspended;
unsignedcharirq_wake;
unsignedcharunused[2];
structattribute_group*attr_group;/*port特殊的屬性*/
conststructattribute_group**tty_groups;/*所有的屬性(僅限于serialcore使用)*/
structserial_rs485rs485;
void*private_data;/*通用platformdata指針*/
};
3、struct uart_ops
struct uart_ops用于描述serial核心和驅(qū)動(dòng)程序之間的接口,實(shí)現(xià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(*start_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,conststructktermios*old);
void(*set_ldisc)(structuart_port*,structktermios*);
void(*pm)(structuart_port*,unsignedintstate,unsignedintoldstate);
constchar*(*type)(structuart_port*);
void(*release_port)(structuart_port*);
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:此函數(shù)用于測試端口的發(fā)送FIFO和移位器是否為空,如果是空的,這個(gè)函數(shù)應(yīng)該返回TIOCSER_TEMT,否則返回0。如果端口不支持此操作,則應(yīng)該返回TIOCSER_TEMT。沒有鎖定。中斷:依賴于調(diào)用者,這個(gè)調(diào)用不能導(dǎo)致睡眠。
set_mctrl:此功能將端口的調(diào)制解調(diào)器控制線設(shè)置為mcctrl所描述的狀態(tài)。mctrl支持的參數(shù)是:
TIOCM_RTS表示RTS信號(hào),TIOCM_DTR表示DTR信號(hào),TIOCM_OUT1表示OUT1信號(hào),TIOCM_OUT2表示OUT2信號(hào),TIOCM_LOOP表示設(shè)置端口為環(huán)回模式。如果設(shè)置了合適的位,則信號(hào)應(yīng)被驅(qū)動(dòng)激活;如果該位被清除,則信號(hào)應(yīng)被驅(qū)動(dòng)為非激活狀態(tài)。
在port->lock獲取的情況下鎖定。禁用本地中斷。該調(diào)用不能睡眠。
get_mctrl:返回端口調(diào)制解調(diào)器控制輸入的當(dāng)前狀態(tài)。輸出的狀態(tài)不應(yīng)該被返回,因?yàn)閮?nèi)核會(huì)跟蹤它們的狀態(tài)。狀態(tài)信息應(yīng)包括:TIOCM_CAR表示DCD的信號(hào)狀態(tài)。TIOCM_CTS表示CTS的信號(hào)狀態(tài),TIOCM_DSR表示DSR信號(hào)狀態(tài),TIOCM_RI表示RI信號(hào)狀態(tài)。
如果設(shè)置該位,則將信號(hào)驅(qū)動(dòng)為激活狀態(tài),如果端口不支持CTS, DCD或DSR,驅(qū)動(dòng)程序應(yīng)該表明信號(hào)是永久激活的。如果RI不可用,信號(hào)不應(yīng)該顯示為激活狀態(tài)。在port->lock獲取的情況下鎖定。禁用本地中斷。調(diào)用該函數(shù)必須不能睡眠。
stop_tx:停止傳輸字符。這可能是由于CTS線路沒有激活,或者tty層表明由于XOFF字符而停止傳輸。驅(qū)動(dòng)程序應(yīng)該盡快停止傳輸字符。在port->lock獲取的情況下鎖定,禁用本地中斷,該調(diào)用不能睡眠。
start_tx:開始傳輸字符。在port->lock獲取的情況下鎖定,禁用本地中斷,該調(diào)用不能睡眠。
throttle:通知串行驅(qū)動(dòng)程序,line規(guī)則的輸入緩沖區(qū)已接近滿,并且它應(yīng)該以某種方式發(fā)出信號(hào),不應(yīng)該再向串行端口發(fā)送字符,只有在啟用了硬件輔助流控制時(shí)才會(huì)調(diào)用該函數(shù)。由tty層通過unthrottle()和termios修改序列化鎖定。
unthrottle:通知串行驅(qū)動(dòng)程序,字符現(xiàn)在可以發(fā)送到串行端口,而不必?fù)?dān)心超出line規(guī)則的輸入緩沖區(qū)。只有在啟用了硬件輔助流控制時(shí)才會(huì)調(diào)用該函數(shù)。
send_xchar:發(fā)送一個(gè)高優(yōu)先級(jí)字符,即使端口停止。這是用來實(shí)現(xiàn)XON/XOFF流量控制和tcflow()。如果串行驅(qū)動(dòng)程序沒有實(shí)現(xiàn)這個(gè)函數(shù),那么tty內(nèi)核將把字符附加到循環(huán)緩沖區(qū),然后調(diào)用start_tx()/stop_tx()來清除數(shù)據(jù)。如果ch == '0' (__DISABLED_CHAR)則不傳輸。不需要鎖定,中斷情況依賴于調(diào)用者。
stop_rx:停止接收字符,該端口正在關(guān)閉中。在port->lock獲取的情況下鎖定。禁用本地中斷,該調(diào)用必須不能睡眠。
start_rx:開始接收字符。在port->lock獲取的情況下鎖定。禁用本地中斷,該調(diào)用必須不能睡眠。
enable_ms:啟用modem狀態(tài)中斷。這個(gè)方法可以被多次調(diào)用。在調(diào)用shutdown()方法時(shí),應(yīng)該禁用調(diào)制解調(diào)器狀態(tài)中斷。在``port->lock```獲取的情況下鎖定。禁用本地中斷,該調(diào)用必須不能睡眠。
break_ctl:控制中斷信號(hào)的傳輸,如果ctl不為零,則應(yīng)發(fā)送斷路信號(hào)。當(dāng)ctl=0進(jìn)行另一個(gè)調(diào)用時(shí),信號(hào)應(yīng)該終止。調(diào)用者持有tty_port->mutex鎖定。
startup:獲取中斷資源并初始化所有底層驅(qū)動(dòng)程序狀態(tài)。啟用接收端口。該函數(shù)不應(yīng)該激活RTS或DTR;這將通過單獨(dú)調(diào)用set_mctrl()來完成。此方法僅在端口初始化打開時(shí)調(diào)用。
shutdown:禁用端口,禁用可能生效的中斷條件,并釋放中斷資源。該函數(shù)不應(yīng)該禁用RTS或DTR;這已經(jīng)通過對set_mctrl()的單獨(dú)調(diào)用完成。一旦調(diào)用完成,驅(qū)動(dòng)程序不能訪問port->state。此方法僅在該端口沒有更多用戶時(shí)調(diào)用。
flush_buffer:刷新所有寫緩沖區(qū),重置所有DMA狀態(tài),并停止所有正在進(jìn)行的DMA傳輸。當(dāng)清除了port->state->xmit循環(huán)緩沖區(qū)時(shí),將調(diào)用該函數(shù)。在port->lock獲取的情況下鎖定。禁用本地中斷,該調(diào)用必須不能睡眠。
set_termios:更改端口參數(shù),包括字長,奇偶校驗(yàn),停止位。更新port->read_status_mask和port->ignore_status_mask,以指示接收的事件類型。
set_ldisc:描述線變更通知。在tty_port->mutex持有的情況下調(diào)用鎖定。
pm:在指定端口上執(zhí)行電源管理相關(guān)活動(dòng)。state指示由enum uart_pm_state定義的狀態(tài),oldstate 指示前一個(gè)狀態(tài)。該函數(shù)不應(yīng)該用來獲取任何資源。該函數(shù)將在端口最初打開并最終關(guān)閉時(shí)被調(diào)用,除非端口也是系統(tǒng)控制臺(tái)。即使沒有設(shè)置CONFIG_PM,也會(huì)發(fā)生這種情況。無鎖定的情況下調(diào)用。
type:返回一個(gè)指向描述指定端口的字符串常量的指針,或者返回NULL,在這種情況下,字符串'unknown'被替換。無鎖定,中斷設(shè)置依賴于調(diào)用者。
release_port:釋放端口當(dāng)前正在使用的所有內(nèi)存和IO區(qū)域資源。沒有鎖定,中斷設(shè)置依賴于調(diào)用者。
request_port:請求端口所需的任何內(nèi)存和IO區(qū)域資源。如果任何一個(gè)請求失敗,當(dāng)這個(gè)函數(shù)返回時(shí)不應(yīng)該注冊任何資源,并且它應(yīng)該在失敗時(shí)返回-EBUSY。無鎖定,中斷設(shè)置依賴于調(diào)用者。
config_port:執(zhí)行port所需的自動(dòng)配置步驟。type包含所需配置的位掩碼。UART_CONFIG_TYPE表示該端口需要檢測和識(shí)別。port->type應(yīng)該設(shè)置為找到的類型,如果沒有檢測到端口,則設(shè)置為PORT_UNKNOWN。
UART_CONFIG_IRQ表示中斷信號(hào)的自動(dòng)配置,應(yīng)該使用標(biāo)準(zhǔn)內(nèi)核自動(dòng)探測技術(shù)進(jìn)行探測。在端口有內(nèi)部硬連線中斷的平臺(tái)上(例如,片上系統(tǒng)實(shí)現(xiàn)),這是不必要的。無鎖定,中斷設(shè)置依賴于調(diào)用者。
verify_port:驗(yàn)證serinfo中包含的新串行端口信息是否適合此端口類型。無鎖定,中斷設(shè)置依賴于調(diào)用者。
ioctl:執(zhí)行任何端口特定的ioctl。IOCTL命令必須使用
unsetunset
四、常用API總結(jié)unset
//調(diào)度寫處理 voiduart_write_wakeup(structuart_port*port) //更新每個(gè)端口幀定時(shí)信息 voiduart_update_timeout(structuart_port*port,unsignedintcflag,unsignedintbaud) //返回特定端口的波特率 unsignedintuart_get_baud_rate(structuart_port*port,structktermios*termios,conststructktermios*old,unsignedintmin,unsignedintmax) //返回uart的時(shí)鐘分頻系數(shù) unsignedintuart_get_divisor(structuart_port*port,unsignedintbaud) //獲取行狀態(tài)寄存器信息 intuart_get_lsr_info(structtty_struct*tty,structuart_state*state,unsignedint__user*value) //將控制臺(tái)(console)消息寫入串口 voiduart_console_write(structuart_port*port,constchar*s,unsignedintcount,void(*putchar)(structuart_port*,unsignedchar)) //獲取控制臺(tái)(console)的端口 structuart_port*uart_get_console(structuart_port*ports,intnr,structconsole*co) //解析earlycon選項(xiàng)參數(shù) intuart_parse_earlycon(char*p,unsignedchar*iotype,resource_size_t*addr,char**options) //解析串口baud/parity/bits/flow控制 voiduart_parse_options(constchar*options,int*baud,int*parity,int*bits,int*flow) //設(shè)置串口控制臺(tái)參數(shù) intuart_set_options(structuart_port*port,structconsole*co,intbaud,intparity,intbits,intflow) //----------------------------Port/driver注冊和移除----------------------------// //向uart核心層注冊一個(gè)驅(qū)動(dòng)程序 intuart_register_driver(structuart_driver*drv) //從uart核心層移除驅(qū)動(dòng)程序。 //如果底層驅(qū)動(dòng)程序在uart_add_one_port()中注冊了端口,則必須通過uart_remove_one_port()刪除已經(jīng)注冊的端口。 voiduart_unregister_driver(structuart_driver*drv) intuart_add_one_port(structuart_driver*reg,structuart_port*port); voiduart_remove_one_port(structuart_driver*reg,structuart_port*port); //判斷兩個(gè)端口是否相等。 //此函數(shù)可用于確定兩個(gè)uart_port結(jié)構(gòu)是否描述相同的端口。 booluart_match_port(conststructuart_port*port1,conststructuart_port*port2) //電源管理 intuart_suspend_port(structuart_driver*reg,structuart_port*port); intuart_resume_port(structuart_driver*reg,structuart_port*port);
底層驅(qū)動(dòng)的助手函數(shù)
voiduart_handle_dcd_change(structuart_port*uport,boolactive) voiduart_handle_cts_change(structuart_port*uport,boolactive) voiduart_insert_char(structuart_port*port,unsignedintstatus,unsignedintoverrun,u8ch,u8flag); voiduart_xchar_out(structuart_port*uport,intoffset); booluart_try_toggle_sysrq(structuart_port*port,u8ch) uart_port_tx_limited(port,ch,count,tx_ready,put_char,tx_done) //uart端口的發(fā)送助手函數(shù) uart_port_tx(port,ch,tx_ready,put_char)
unsetunset
五、uart驅(qū)動(dòng)示例剖析unset
1、原廠設(shè)計(jì)的uart驅(qū)動(dòng)
有些芯片原廠會(huì)針對自家的芯片設(shè)計(jì)開發(fā)出uart驅(qū)動(dòng),例如nxp的imx6ull,針對該系列的SOC,NXP原廠設(shè)計(jì)出了一個(gè)名為imx.c的驅(qū)動(dòng),位于/drivers/tty/serial目錄中。該驅(qū)動(dòng)以平臺(tái)驅(qū)動(dòng)為框架設(shè)計(jì):
staticstructplatform_driverserial_imx_driver={
.probe=serial_imx_probe,
.remove=serial_imx_remove,
.suspend=serial_imx_suspend,
.resume=serial_imx_resume,
.id_table=imx_uart_devtype,
.driver={
.name="imx-uart",
.of_match_table=imx_uart_dt_ids,
},
};
設(shè)備樹匹配表是:

.probe對應(yīng)的serial_imx_probe()實(shí)現(xiàn)如下:
staticintserial_imx_probe(structplatform_device*pdev)
{
structimx_port*sport;
void__iomem*base;
intret=0;
structresource*res;
inttxirq,rxirq,rtsirq;
sport=devm_kzalloc(&pdev->dev,sizeof(*sport),GFP_KERNEL);
if(!sport)
return-ENOMEM;
ret=serial_imx_probe_dt(sport,pdev);
if(ret>0)
serial_imx_probe_pdata(sport,pdev);
elseif(ret0)
??return?ret;
?res?=?platform_get_resource(pdev,?IORESOURCE_MEM,?0);
?base?=?devm_ioremap_resource(&pdev->dev,res);
if(IS_ERR(base))
returnPTR_ERR(base);
rxirq=platform_get_irq(pdev,0);
txirq=platform_get_irq(pdev,1);
rtsirq=platform_get_irq(pdev,2);
sport->port.dev=&pdev->dev;
sport->port.mapbase=res->start;
sport->port.membase=base;
sport->port.type=PORT_IMX,
sport->port.iotype=UPIO_MEM;
sport->port.irq=rxirq;
sport->port.fifosize=32;
sport->port.ops=&imx_pops;
sport->port.rs485_config=imx_rs485_config;
sport->port.rs485.flags=
SER_RS485_RTS_ON_SEND|SER_RS485_RX_DURING_TX;
sport->port.flags=UPF_BOOT_AUTOCONF;
init_timer(&sport->timer);
sport->timer.function=imx_timeout;
sport->timer.data=(unsignedlong)sport;
sport->clk_ipg=devm_clk_get(&pdev->dev,"ipg");
if(IS_ERR(sport->clk_ipg)){
ret=PTR_ERR(sport->clk_ipg);
dev_err(&pdev->dev,"failedtogetipgclk:%d
",ret);
returnret;
}
sport->clk_per=devm_clk_get(&pdev->dev,"per");
if(IS_ERR(sport->clk_per)){
ret=PTR_ERR(sport->clk_per);
dev_err(&pdev->dev,"failedtogetperclk:%d
",ret);
returnret;
}
sport->port.uartclk=clk_get_rate(sport->clk_per);
if(sport->port.uartclk>IMX_MODULE_MAX_CLK_RATE){
ret=clk_set_rate(sport->clk_per,IMX_MODULE_MAX_CLK_RATE);
if(ret0)?{
???dev_err(&pdev->dev,"clk_set_rate()failed
");
returnret;
}
}
sport->port.uartclk=clk_get_rate(sport->clk_per);
/*
*AllocatetheIRQ(s)i.MX1hasthreeinterruptswhereaslater
*chipsonlyhaveoneinterrupt.
*/
if(txirq>0){
ret=devm_request_irq(&pdev->dev,rxirq,imx_rxint,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;
ret=devm_request_irq(&pdev->dev,txirq,imx_txint,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;
}else{
ret=devm_request_irq(&pdev->dev,rxirq,imx_int,0,
dev_name(&pdev->dev),sport);
if(ret)
returnret;
}
imx_ports[sport->port.line]=sport;
platform_set_drvdata(pdev,sport);
returnuart_add_one_port(&imx_reg,&sport->port);
}
從上述代碼可知,依然是常規(guī)驅(qū)動(dòng)程序設(shè)計(jì)的思路。在.probe中進(jìn)行的步驟有:
(1)為描述imx的uart的struct imx_port分配內(nèi)存。
(2)解析設(shè)備樹中信息,獲取resource。
(3)獲取中斷相關(guān)配置參數(shù)。
(4)初始化struct imx_port中的組成元素。
(5)uart時(shí)鐘參數(shù)配置和使能。
(6)使用uart_add_one_port()向uart_driver添加uart_port,在這里就是向imx_reg添加sport->port。imx_reg是struct uart_driver的具體實(shí)例;sport->port是struct imx_port中關(guān)聯(lián)的struct uart_port。
2、8250標(biāo)準(zhǔn)uart驅(qū)動(dòng)
本小節(jié)中的uart驅(qū)動(dòng)指單純針對一款SOC設(shè)計(jì)的驅(qū)動(dòng),該部分驅(qū)動(dòng)一般由芯片原廠提供。除此之外,有些SOC設(shè)計(jì)公司會(huì)基于標(biāo)準(zhǔn)(例如16550A)的uart通信機(jī)制設(shè)計(jì)UART硬件部分。從而軟件驅(qū)動(dòng)上也能使用標(biāo)準(zhǔn)的uart驅(qū)動(dòng)進(jìn)行通信。例如:8250。linux內(nèi)核中,8250串口通用驅(qū)動(dòng)的主要文件如下:
drivers/tty/serial/8250/8250_core.c :8250串口驅(qū)動(dòng)核心。
drivers/tty/serial/8250/8250_dw.c :Synopsis DesignWare 8250串口驅(qū)動(dòng)。
drivers/tty/serial/8250/8250_dma.c :8250串口DMA驅(qū)動(dòng)。
drivers/tty/serial/8250/8250_port.c :8250串口端口操作。
drivers/tty/serial/8250/8250_early.c :8250串口early console驅(qū)動(dòng)。
例如rk3568,對于rk3568關(guān)于uart的設(shè)備,是使用設(shè)備樹進(jìn)行描述:

主機(jī)側(cè)對應(yīng)的驅(qū)動(dòng)程序則是Synopsis DesignWare 8250串口驅(qū)動(dòng),由drivers/tty/serial/8250/8250_dw.c文件描述。在該驅(qū)動(dòng)程序中,使用platform驅(qū)動(dòng)方案實(shí)現(xiàn)驅(qū)動(dòng)的設(shè)計(jì):

當(dāng)設(shè)備和驅(qū)動(dòng)匹配后,會(huì)執(zhí)行dw8250_probe()函數(shù),該函數(shù)實(shí)現(xiàn)如下:
staticintdw8250_probe(structplatform_device*pdev)
{
structuart_8250_portuart={},*up=&uart;
structuart_port*p=&up->port;
structdevice*dev=&pdev->dev;
structdw8250_data*data;
structresource*regs;
intirq;
interr;
u32val;
regs=platform_get_resource(pdev,IORESOURCE_MEM,0);
if(!regs)
returndev_err_probe(dev,-EINVAL,"noregistersdefined
");
irq=platform_get_irq_optional(pdev,0);
/*nointerrupt->fallbacktopolling*/
if(irq==-ENXIO)
irq=0;
if(irq0)
??return?irq;
?spin_lock_init(&p->lock);
p->mapbase=regs->start;
p->irq=irq;
p->handle_irq=dw8250_handle_irq;
p->pm=dw8250_do_pm;
p->type=PORT_8250;
p->flags=UPF_SHARE_IRQ|UPF_FIXED_PORT;
p->dev=dev;
p->iotype=UPIO_MEM;
p->serial_in=dw8250_serial_in;
p->serial_out=dw8250_serial_out;
p->set_ldisc=dw8250_set_ldisc;
p->set_termios=dw8250_set_termios;
p->membase=devm_ioremap(dev,regs->start,resource_size(regs));
if(!p->membase)
return-ENOMEM;
data=devm_kzalloc(dev,sizeof(*data),GFP_KERNEL);
if(!data)
return-ENOMEM;
data->data.dma.fn=dw8250_fallback_dma_filter;
data->pdata=device_get_match_data(p->dev);
p->private_data=&data->data;
data->uart_16550_compatible=device_property_read_bool(dev,
"snps,uart-16550-compatible");
err=device_property_read_u32(dev,"reg-shift",&val);
if(!err)
p->regshift=val;
err=device_property_read_u32(dev,"reg-io-width",&val);
if(!err&&val==4){
p->iotype=UPIO_MEM32;
p->serial_in=dw8250_serial_in32;
p->serial_out=dw8250_serial_out32;
}
if(device_property_read_bool(dev,"dcd-override")){
/*AlwaysreportDCDasactive*/
data->msr_mask_on|=UART_MSR_DCD;
data->msr_mask_off|=UART_MSR_DDCD;
}
if(device_property_read_bool(dev,"dsr-override")){
/*AlwaysreportDSRasactive*/
data->msr_mask_on|=UART_MSR_DSR;
data->msr_mask_off|=UART_MSR_DDSR;
}
if(device_property_read_bool(dev,"cts-override")){
/*AlwaysreportCTSasactive*/
data->msr_mask_on|=UART_MSR_CTS;
data->msr_mask_off|=UART_MSR_DCTS;
}
if(device_property_read_bool(dev,"ri-override")){
/*AlwaysreportRingindicatorasinactive*/
data->msr_mask_off|=UART_MSR_RI;
data->msr_mask_off|=UART_MSR_TERI;
}
/*Alwaysaskforfixedclockratefromaproperty.*/
device_property_read_u32(dev,"clock-frequency",&p->uartclk);
/*Ifthereisseparatebaudclk,gettheratefromit.*/
data->clk=devm_clk_get_optional(dev,"baudclk");
if(data->clk==NULL)
data->clk=devm_clk_get_optional(dev,NULL);
if(IS_ERR(data->clk))
returnPTR_ERR(data->clk);
INIT_WORK(&data->clk_work,dw8250_clk_work_cb);
data->clk_notifier.notifier_call=dw8250_clk_notifier_cb;
err=clk_prepare_enable(data->clk);
if(err)
returndev_err_probe(dev,err,"couldnotenableoptionalbaudclk
");
err=devm_add_action_or_reset(dev,dw8250_clk_disable_unprepare,data->clk);
if(err)
returnerr;
if(data->clk)
p->uartclk=clk_get_rate(data->clk);
/*Ifnoclockrateisdefined,fail.*/
if(!p->uartclk)
returndev_err_probe(dev,-EINVAL,"clockratenotdefined
");
data->pclk=devm_clk_get_optional(dev,"apb_pclk");
if(IS_ERR(data->pclk))
returnPTR_ERR(data->pclk);
err=clk_prepare_enable(data->pclk);
if(err)
returndev_err_probe(dev,err,"couldnotenableapb_pclk
");
err=devm_add_action_or_reset(dev,dw8250_clk_disable_unprepare,data->pclk);
if(err)
returnerr;
data->rst=devm_reset_control_get_optional_exclusive(dev,NULL);
if(IS_ERR(data->rst))
returnPTR_ERR(data->rst);
reset_control_deassert(data->rst);
err=devm_add_action_or_reset(dev,dw8250_reset_control_assert,data->rst);
if(err)
returnerr;
dw8250_quirks(p,data);
/*IftheBusyFunctionalityisnotimplemented,don'thandleit*/
if(data->uart_16550_compatible)
p->handle_irq=NULL;
if(!data->skip_autocfg)
dw8250_setup_port(p);
/*Ifwehaveavalidfifosize,tryhookingupDMA*/
if(p->fifosize){
data->data.dma.rxconf.src_maxburst=p->fifosize/4;
data->data.dma.txconf.dst_maxburst=p->fifosize/4;
up->dma=&data->data.dma;
}
data->data.line=serial8250_register_8250_port(up);
if(data->data.line0)
??return?data->data.line;
/*
*Someplatformsmayprovideareferenceclocksharedbetweenseveral
*devices.Inthiscaseanyclockstatechangemustbeknowntothe
*UARTportatleastpostfactum.
*/
if(data->clk){
err=clk_notifier_register(data->clk,&data->clk_notifier);
if(err)
returndev_err_probe(dev,err,"Failedtosettheclocknotifier
");
queue_work(system_unbound_wq,&data->clk_work);
}
platform_set_drvdata(pdev,data);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return0;
}
在上述probe中,主要執(zhí)行的操作如下:
(1)從platform_device中提取中struct resource。
(2)設(shè)置struct uart_port中組成元素的初始化參數(shù)值和一些必要的callback。
(3)讀取dev中的參數(shù)值。
(4)設(shè)置時(shí)鐘。
(5)調(diào)用serial8250_register_8250_port()注冊8250端口。
unsetunset
六、總結(jié)unset
1、本文描述了linux下的uart框架,因uart隸屬于tty,故而芯片原廠一般會(huì)將與uart相關(guān)的驅(qū)動(dòng)放置于/drivers/tty/serial目錄中。
2、關(guān)于linux下的uart驅(qū)動(dòng),芯片原廠一般都會(huì)去實(shí)現(xiàn),而不用再去開發(fā)這一層的驅(qū)動(dòng)。但是uart驅(qū)動(dòng)框架還是值得去了解和學(xué)習(xí)。本文總結(jié)了一些常用的API(以具體linux版本為主),也簡要分析了兩款芯片的uart驅(qū)動(dòng)程序。
3、基于linux,作為uart的使用者,只需要通過設(shè)備樹傳遞uart相關(guān)的參數(shù)(假如linux支持設(shè)備樹),這時(shí)候uart驅(qū)動(dòng)程序會(huì)自動(dòng)加載運(yùn)行,向用戶空間暴露出設(shè)備節(jié)點(diǎn),這時(shí)候用戶空間就可以方便的使用uart進(jìn)行通信了。
4、芯片原廠設(shè)計(jì)的驅(qū)動(dòng),往往具有兼容性,支持多款同系列或者同類型的芯片!
審核編輯:湯梓紅
-
Linux
+關(guān)注
關(guān)注
88文章
11622瀏覽量
217830 -
驅(qū)動(dòng)程序
+關(guān)注
關(guān)注
19文章
868瀏覽量
49935 -
源碼
+關(guān)注
關(guān)注
8文章
682瀏覽量
31065 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4405瀏覽量
66795
原文標(biāo)題:玩玩linux的uart,爽歪歪!
文章出處:【微信號(hào):嵌入式小生,微信公眾號(hào):嵌入式小生】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評(píng)論請先 登錄
UART串口通信示例:開啟通信寶藏之門的定位模組LuatOS入門
RTOS版本用示例提供的UART驅(qū)動(dòng),編譯失敗提示找不到函數(shù)uart_div_modify(),是怎么回事?
linux內(nèi)核深度剖析,另附有光盤資料
淺析linux UART驅(qū)動(dòng)和tty架構(gòu)
全面剖析嵌入式Linux開發(fā)
嵌入式Linux UART
STM32 UART串口驅(qū)動(dòng)程序

linux的uart驅(qū)動(dòng)示例剖析
評(píng)論