vxworks嵌入式操作系統(tǒng)串行設備驅動程序的編寫
摘要:目前,基于嵌入式操作系統(tǒng)的軟件開發(fā)是國內外研究的熱點,vxworks嵌入式操作系統(tǒng)又是目前最流行的嵌入式操作系統(tǒng)之一。本文的目的在于通過分析vxworks操作系統(tǒng)下串行通信設備驅動程序的運行機制,提出在此操作系統(tǒng)下開發(fā)串行設備驅動程序開發(fā)的基本思路。
本文引用地址:http://2s4d.com/article/148563.htm1. 概 述
我們在基于vxworks嵌入式操作系統(tǒng)開發(fā)產品時,經常會根據(jù)自行設計的硬件電路開發(fā)專用的驅動程序。Vxworks下的驅動程序根據(jù)設備的不同特性,,大體可分為:char driver、serial driver、block driver、end driver、scsi driver等類型,其中以char driver最簡單,最基礎,以serial driver最常用。掌握驅動程序的基本工作流程,無論對我們開發(fā)上層的應用還是自己編寫相應的驅動程序,都很有幫助。本文主要以i8250串口驅動程序為例,介紹一下串行驅動程序編寫的基本思路。
驅動程序,簡而言之就是對具體的硬件設備進行管理和服務的程序。為了提高代碼的可移植性,vxworks將所有的輸入/輸出設備都看成是一個文件,我們對設備的輸入/輸出操作,都可以看作是對指定文件的讀寫操作。例如,我們用c 標準庫函數(shù)open()打開一個文件,可以是打開一個傳統(tǒng)意義上的文本文件,也可以是指定一個輸入/輸出設備,如指定對某一個串口的輸入/輸出操作。在vxworks操作系統(tǒng)中,驅動程序的主要作用是完成對相關設備的讀、寫、打開、建立、關閉及控制等功能中的一項或幾項,具體情況視具體的設備及設計要求而定。
概括的說,驅動程序主要完成以下幾項工作:
(1)相關設備的初始化。
(2)底層輸入/輸出函數(shù)與上層標準輸入/輸出函數(shù)的掛接。
(3)相關設備與對應驅動程序的掛接。
我們就按照這個思路,以I8250串口為例,分析一下串行設備驅動程序的編寫及加載流程。首先,給出串行設備驅動的結構框圖:
需要說明的是,ttyDrv是一個虛擬的設備驅動,與tylib一起,用于處理I/O系統(tǒng)與底層實際設備之間的通信。主要完成以下工作:
(1)處理I/O系統(tǒng)的各種需求,如在driver talbe 中添加相應的驅動條目、創(chuàng)建設備標識符(devise descriptor)。
(2)實現(xiàn)與上層標準I/O函數(shù)及實際驅動程序的無縫連接。其中,ttyDrv完成open和ioctl兩項功能(ttyopen()和ttyioctl())。Tylib完成read和write兩項功能(tyRead()和tyWrite())。
(3)管理輸入/輸出數(shù)據(jù)緩沖區(qū)。
下面,我們結合圖(一)給出的框圖,以i8250為例,開始分析串行設備驅動的設計流程。用戶在編寫自己的驅動程序時,可以不按照系統(tǒng)函數(shù)命名的方法命名,也可以不按照系統(tǒng)給定的方法進行函數(shù)功能的劃分,但其初始化及實現(xiàn)流程卻不能改變。
2. 驅動程序設計流程分析
⑴ i8250相關硬件設備的初始化。
編寫驅動程序的第一步是完成相關硬件的初始化。與I8250相關的硬件初始化函數(shù)主要有以下三個:sysSerialHwInit()、i8250HrdInit()、i8250InitChannel(),其調用順序是:sysSerialHwInit()ài8250HrdInit()ài8250InitChannel(),這條工作鏈的主要作用是,完成對I8250_CHAN數(shù)據(jù)結構的初始化。
下面對分別這幾個函數(shù)的功能介紹一下:
l
sysSerialHwInit()
本函數(shù)完成的主要任務是初始化設備的中斷向量、串口的通信模式及相關存貯器,在函數(shù)的最后調用i8250HrdInit()對I8250_CHAN結構進一步初始化。
void sysSerialHwInit (void)
{
int i;
for (i=0;i
{
i8250Chan[i].int_vec = devParas[i].vector; /*初始化中斷向量*/
i8250Chan[i].channelMode = 0; /*初始化SIO_MODE 可以是INT或POLL*/
i8250Chan[i].lcr = UART_REG(UART_LCR,i); /*初始化line control register*/
………………………
i8250Chan[i].outByte = sysOutByte; /*掛接輸出函數(shù),此函數(shù)向指定的I/O地址寫入1bye*/
i8250Chan[i].inByte = sysInByte; /*掛接輸出函數(shù),此函數(shù)從指定的I/O地址讀出1byte*/
i8250HrdInit(i8250Chan[i]);/*調用i8250HrdInit()進一步完成初始化*/
}
}
l i8250HrdInit()
本函數(shù)完成的主要工作是掛接相應的入口函數(shù),具體說明如下:
void i8250HrdInit
(
I8250_CHAN * pChan /* 指向相應設備的指針*/
)
{
if (i8250SioDrvFuncs.ioctl == NULL)
{
i8250SioDrvFuncs.ioctl = (int (*)())i8250Ioctl;/*掛接用于處理控制I8250相關輸入
輸出命令的函數(shù)*/
i8250SioDrvFuncs.txStartup = (int (*)())i8250Startup;/*如果設備工作于中斷模式下,
啟用此函數(shù)用于打開中斷,使設備開始工作*/
i8250SioDrvFuncs.callbackInstall = i8250CallbackInstall;/*安裝上層提供的回調函數(shù),
本例中是安裝的tyIRd()、tyITx()*/
i8250SioDrvFuncs.pollInput = (int (*)())i8250PRxChar;/*掛接輸入輪詢函數(shù)*/
i8250SioDrvFuncs.pollOutput = (int (*)(SIO_CHAN *,char))i8250PTxChar;/*掛接輸出輪詢函數(shù)*/
}
pChan->pDrvFuncs = i8250SioDrvFuncs;/*初始化CHAN結構,掛接接口函數(shù)列表*/
i8250InitChannel(pChan); /* reset the channel */
}
由上面掛接的函數(shù)可以看出,i8250驅動主要實現(xiàn)了三個功能:read、write、ioctl,而并沒有實現(xiàn)所有和七項功能。同時,值的注意的是,對同一種設備的驅動只需掛接一次。
同時ttyDrv通過SIO_DRV_FUNCS使用xxDrv(i8250Drv)提供的服務,而xxDrv通過回調函數(shù)(本例中是由i8250CallbackInstall()安裝的tyIRd()、tyITx())完成ttyDrv提出的請求。原理如下圖示:
i8250InitChannel()
本函數(shù)的主要作用是初始化特定的CHAN所描述的信道。具體分析如下。
static void i8250InitChannel
(
I8250_CHAN * pChan /* pointer to device */
)
{
int oldLevel;
ldLevel = intLock (); /*關中斷進入臨界區(qū)*
(void) i8250BaudSet(pChan, I8250_DEFAULT_BAUD);/*設置信道的波特率*/
…………………………………
intUnlock (oldLevel); /*開中斷響應,出臨界區(qū)*/
}
⑵ 掛接中斷服務程序
對i8250的硬件初始化完成后,接著掛接相關的中斷服務程序。主要有sysSerialHwinit2()函數(shù)完成。需要注意的是,掛接中斷應放在系統(tǒng)初始化的最后,主要是因為中斷掛接函數(shù)intCONnect()需要調用malloc()函數(shù),如果在系統(tǒng)的內存分配還未初始化前調用,則會出錯。下面請看源代碼:
void sysSerialHwInit2 (void)
{
int i;
for (i=0;i
if (i8250Chan[i].int_vec)
{
(void) intConnect (INUM_TO_IVEC (i8250Chan[i].int_vec), i8250Int, (int)i8250Chan[i] );
sysIntEnablePIC (devParas[i].intLevel);
}
}
其中,宏INUM_TO_IVEC的作用是把中斷號轉為中斷向量。i8250Int是指向輸入/輸出中斷處理函數(shù)的指針。描述相應硬件的結構i8250Chan為函數(shù)i8250int()的入口參數(shù)。
至此,設備硬件的初始化、相關的低層函數(shù)的掛接、中斷初始化基本完成。開始進行下一步,將設備的驅動函數(shù)安裝在Driver Table 中。
⑶ 與上層標準輸入/輸出函數(shù)的掛接
在此處I/O系統(tǒng)通過調用ttyDrv()(在沒有定義INCLUDE_TYCODRV_5_2的情況下)將相應驅動函數(shù)添加到Driver Table中,從而完成與上層標準輸入/輸出函數(shù)的掛接。
由上圖知,iosDrvInstall()函數(shù)在Driver Table中掛接的函數(shù)是tyWrite()和tyRead(),而不是我們實際編寫的輸入/輸出函數(shù)。其具體的調用過程是:
① 當用戶調用write函數(shù)進行寫操作時,根據(jù)相應的fd調用在Driver Table中注冊的函數(shù)tyWrite(),此函數(shù)的作用是將用戶緩沖區(qū)的內容寫入相應的輸出ring buffer,當發(fā)現(xiàn)緩沖區(qū)內有內容時,開始調用回調函數(shù)tyITX(),從ring buffer讀取字符,由I8250Startup()啟動中斷輸出,最后由設備的輸出中斷服務程序(在本例中調用的是sysOutbyte())將字符發(fā)往指定的串口。
② 當串口接收到數(shù)據(jù)時會調用輸入中斷服務程序(在本例中是sysInbyte()),將輸入的字符寫入指定的緩沖區(qū)。然后由回調函數(shù)tyIRd()將緩沖區(qū)的內容讀入ring buffer,當用戶調用read函數(shù)進行寫操作時,會根據(jù)相應的fd調用在Driver Table中注冊的函數(shù)tyRead(),此函數(shù)會將ring buffer中的內容讀入用戶緩沖區(qū)。
關于具體的中斷輸入/輸出函數(shù)如何調用,本文不做詳細分析,請參閱i8250int()及i8250Startup()。
對于輸入/輸出控制函數(shù)ioctl()的掛接,則是直接將命令傳到由用戶編寫的i8250ioctl()函數(shù),其具體的實現(xiàn)代碼與驅動的設計思路無緊密的聯(lián)系,本文也不做具體分析。
⑷ 具體設備與相關驅動的掛接
當Driver Table中相應的驅動函數(shù)掛接完成,開始編寫驅動程序的最后一步:在Device Table中加入設備,完成具體設備與相關驅動的掛接。此項工作是由ttyDevCreat()函數(shù)完成的。本函數(shù)主要實現(xiàn)以下功能:
① 分配并初始化一個device descriptor。
② 通過調用tyDevInit()初始化tyLib。此處主要完成輸入/輸出ring buffer的創(chuàng)建、建立用與相關函數(shù)的信號量、初始化selectLib。
③ 調用iosDevAdd()將串口設備加入Device Table。對于設備特性的描述信息是由sysSerialChanGet()函數(shù)得到,并以參數(shù)形式傳入的。
④ 為底層設備安裝回調函數(shù),在本例中是為i8250CHAN 安裝tyIRd()、tyITx()兩處回調函數(shù)。
⑤ 開中斷,設備開始以中斷方式工作。
至此,驅動程序的分析全部完成。與掛接驅動函數(shù)不同,在安裝設備的過程中,無論設備相同與否,有幾個設備則上述過程需調用幾次。以上各函數(shù)的加載主要在usrinit()函數(shù)中完成。
3. 結束語
需要說明的是,在VxWorks下,設備驅動程序既可以嵌入內核隨系統(tǒng)一起啟動,也可以作為可加載模塊在系統(tǒng)啟動之后運行。相比之下,后一種方式比較簡單,不用修改系統(tǒng)內核,引入錯誤的可能性小。但是無論采取哪種方式,其基本思路及需要完成的工作是相同的。本文沒有按照系統(tǒng)的調用過程進行一步步分析,主要基于上述考慮。用戶在編寫相關驅動程序時,中心任務是按步驟完成上述功能,而沒有必要去死搬系統(tǒng)的加載步驟。
linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)
評論