摘 要: 簡(jiǎn)要介紹了Linux操作系統(tǒng)和PCI-Express(PCI-E)總線的特點(diǎn)以及Linux設(shè)備驅(qū)動(dòng)的作用。以PEX8311時(shí)統(tǒng)卡為例,,闡述了Linux系統(tǒng)下PCI-E驅(qū)動(dòng)程序開發(fā)的流程和技巧,,并通過DMA模式測(cè)試了驅(qū)動(dòng)程序的可行性。
關(guān)鍵詞: 設(shè)備驅(qū)動(dòng),;Linux,;PCI-Express;PEX8311,;DMA
0 引言
Linux操作系統(tǒng)憑借其開放的源代碼,、良好的擴(kuò)展性以及安全高效等特點(diǎn),受到越來越多領(lǐng)域開發(fā)者的重視,,并逐步成為各種計(jì)算機(jī)終端,、服務(wù)器工作站及嵌入式平臺(tái)的主流操作系統(tǒng),。PCI-Express(PCI-E)作為最新一代的總線接口,其點(diǎn)對(duì)點(diǎn)的串行設(shè)計(jì)以及雙通道高帶寬的傳輸模式,,大大提高了數(shù)據(jù)的傳輸速率[1],,它的廣泛應(yīng)用將全面取代PCI、AGP等總線,。
目前基于Linux平臺(tái)下的PCI-E總線的應(yīng)用十分廣泛,,小到微型嵌入式系統(tǒng),大到超大型服務(wù)器系統(tǒng),,都可以看到二者的完美結(jié)合,。而驅(qū)動(dòng)程序作為硬件設(shè)備與操作系統(tǒng)之間的橋梁,對(duì)硬件的工作起著至關(guān)重要的作用,。本文介紹的是Linux下基于PCI-E時(shí)統(tǒng)卡的驅(qū)動(dòng)程序的開發(fā)過程,。
1 PCI-E時(shí)統(tǒng)卡
本文中使用的PCI-E時(shí)統(tǒng)卡是自主研發(fā)的一款硬件設(shè)備。該時(shí)統(tǒng)卡通過接受B碼終端發(fā)來的信號(hào),,然后經(jīng)FPGA進(jìn)行解碼,,獲得時(shí)間信息,并以1 pps脈沖為基準(zhǔn)產(chǎn)生用戶所需要的20 Hz,、100 Hz等中斷脈沖信號(hào),,最后通過PCI-E橋接芯片PEX8311進(jìn)行數(shù)據(jù)交互,使得時(shí)統(tǒng)卡中的時(shí)間信息以及中斷信息能夠傳到計(jì)算機(jī)終端或服務(wù)器中,。而要想讓安裝在計(jì)算機(jī)終端或者服務(wù)器中的時(shí)統(tǒng)卡能夠正常工作,,就需要為其開發(fā)配套的驅(qū)動(dòng)程序,主要就是針對(duì)PEX8311芯片的驅(qū)動(dòng),。圖1所示為時(shí)統(tǒng)卡PEX8311芯片的結(jié)構(gòu)簡(jiǎn)圖,。數(shù)據(jù)經(jīng)由PFGA傳到Local Bus,然后通過內(nèi)部總線再到PCI-E總線,,最后傳到計(jì)算機(jī)終端中,。
2 Linux設(shè)備驅(qū)動(dòng)程序
Linux設(shè)備驅(qū)動(dòng)程序是一種使計(jì)算機(jī)軟件與硬件設(shè)備進(jìn)行交互的特殊程序。圖2所示為L(zhǎng)inux設(shè)備驅(qū)動(dòng)與操作系統(tǒng)及外設(shè)的關(guān)系,。設(shè)備驅(qū)動(dòng)程序位于Linux操作系統(tǒng)的內(nèi)核空間,,它相當(dāng)于操作系統(tǒng)內(nèi)核空間與物理層硬件設(shè)備之間的接口,它還為用戶層提供系統(tǒng)調(diào)用的接口函數(shù),。用戶層的應(yīng)用程序不能直接訪問操作物理層的外部硬件設(shè)備,只有通過系統(tǒng)調(diào)用才可以訪問操作外部硬件設(shè)備[2],。因此可以看出設(shè)備驅(qū)動(dòng)程序在操作系統(tǒng)中起到了相當(dāng)大的作用,。
3 Linux設(shè)備驅(qū)動(dòng)開發(fā)過程
Linux設(shè)備驅(qū)動(dòng)程序的編寫可以模塊化,主要包括:設(shè)備的初始化,、驅(qū)動(dòng)模塊的加載與卸載,、設(shè)備的打開與釋放,、數(shù)據(jù)讀寫與操作、中斷響應(yīng),。
3.1 設(shè)備的初始化
Linux系統(tǒng)啟動(dòng)后會(huì)自動(dòng)檢測(cè)計(jì)算機(jī)終端上所有的PCI-E設(shè)備的信息,,并記錄在pci_dev結(jié)構(gòu)體中,其中包括硬件設(shè)備的廠商號(hào),、設(shè)備號(hào)等大部分的硬件信息,。PCI-E驅(qū)動(dòng)程序就是根據(jù)廠商號(hào)和設(shè)備號(hào)來連接設(shè)備并加載驅(qū)動(dòng)的,這就需要在驅(qū)動(dòng)程序中定義該驅(qū)動(dòng)所支持的硬件參數(shù)信息,。本文中使用的時(shí)統(tǒng)卡的PCI-E橋接芯片是PEX8311,,其硬件參數(shù)信息定義分別為廠商號(hào)、設(shè)備號(hào),、子廠商號(hào),、子設(shè)備號(hào)、類別和類別掩碼,。初始化代碼如下,。
static struct pci_device_id PlxPciIdTable()=
{
#if(PLX_CHIP==8311)
{0x10B5,0x8311,,PCI_ANY_ID,,PCI_ANY_ID,0x000000,,0x000000},,
{0x10B5,0x86E1,,PCI_ANY_ID,,PCI_ANY_ID,0x000000,,0x000000},,
#endif
};
MODULE_DEVICE_TABLE(pci,,PlxPciIdTable),;
3.2 驅(qū)動(dòng)模塊的加載與卸載
硬件設(shè)備驅(qū)動(dòng)的加載,必須要有一個(gè)主設(shè)備號(hào),。設(shè)備號(hào)的分配有兩種方式:靜態(tài)分配和動(dòng)態(tài)分配,。靜態(tài)分配指的是由開發(fā)人員指定一個(gè)固定的設(shè)備號(hào);動(dòng)態(tài)分配則是由操作系統(tǒng)自動(dòng)分配設(shè)備號(hào),。在不能明確某設(shè)備號(hào)是否被使用的情況下,,建議使用動(dòng)態(tài)分配的方式獲得設(shè)備號(hào),這樣就避免了因設(shè)備號(hào)沖突導(dǎo)致硬件設(shè)備不能正常工作的情況出現(xiàn),。分配了設(shè)備號(hào)就可以注冊(cè)設(shè)備并加載設(shè)備驅(qū)動(dòng)了,。而當(dāng)該設(shè)備不再使用時(shí),,可以將該設(shè)備的驅(qū)動(dòng)模塊卸載掉,以此來減少系統(tǒng)內(nèi)核的占用以及其他系統(tǒng)資源的開銷,。驅(qū)動(dòng)模塊加載與卸載的代碼如下,。
//驅(qū)動(dòng)模塊的加載
static int__init plxpci_init(void)
{
……
/*注冊(cè)設(shè)備,register_chrdev函數(shù)的第一個(gè)參數(shù)為0,,表示系統(tǒng)自動(dòng)分配一個(gè)空閑的主設(shè)備號(hào)*/
card->MajorID=register_chrdev(0,,PLX_DRIVER_NAME,&plxpci_fops),;
pci_register_driver(&PlxPciDriver),;
……
}
//驅(qū)動(dòng)模塊的卸載
static void__exit plxpci_cleanup(void)
{
unregister_chrdev(major,PLX_DRIVER_NAME),;
pci_unregister_driver(&plxpci_driver),;
}
3.3 設(shè)備的打開與釋放
Linux系統(tǒng)內(nèi)核在驅(qū)動(dòng)模塊加載之后就可以打開硬件設(shè)備。設(shè)備的打開模塊主要是獲取設(shè)備的控制權(quán),,允許中斷的產(chǎn)生等,。而當(dāng)不再使用該設(shè)備時(shí),就需要釋放該設(shè)備,。設(shè)備的釋放模塊的任務(wù)與設(shè)備的打開模塊的任務(wù)正好相反,,主要是釋放對(duì)設(shè)備的控制權(quán)、中斷以及之前系統(tǒng)分配的一些資源等,。設(shè)備打開與釋放的代碼如下,。
//設(shè)備的打開
static int plxpci_open(struct inode*inode,struct file*file)
{
……
/*獲取設(shè)備的控制權(quán)*/
dev->open_mode|=file->f_mode&(FMODE_READ|FMODE_WRITE),;
/*允許中斷產(chǎn)生*/
plxpci_enable_IRQ(dev),;
return 0;
}
//設(shè)備的釋放
static int plxpci_release(struct inode*inode,,struct file*file)
{
……
/*釋放對(duì)設(shè)備的控制權(quán)*/
dev->open_mode&=(~file->f_mode)&(FMODE_READ|FMODE_WRITE),;
free_irq(card->irq,card),;
kfree(card),;
return0;
}
3.4 數(shù)據(jù)讀寫與操作
本文中驅(qū)動(dòng)程序使用的是DMA(Direct Memory Access)傳輸模式,。DMA傳輸模式無需計(jì)算機(jī)或本地控制器的干預(yù),,傳輸效率很高,從而大大降低了控制器的工作量且提高了數(shù)據(jù)的傳輸速率及效率[3],。要完成DMA傳輸模式就需要了解時(shí)統(tǒng)卡上主要的PCI-E橋接芯片PEX8311的工作模式,。從參考文獻(xiàn)[4]中可知,PEX8311芯片中有幾個(gè)重要的寄存器:(1)LCS_DMAMODE0,地址是80h,,該寄存器主要用來設(shè)置DMA的模式。(2)LCS_DMADPR0,,地址是90h,,該寄存器主要用來設(shè)置DMA的傳輸方向。當(dāng)LCS_DMADPR0[3]=1,,表示傳輸方向從Local Bus到PCI-E,,若為0,則方向相反,。(3)LCS_DMACSR0,,地址是A8h,該寄存器主要用來啟動(dòng)DMA傳輸,。成功設(shè)置了DMA的傳輸模式,,就可以從時(shí)統(tǒng)卡中讀出時(shí)間信息。DMA傳輸?shù)拇a如下,。
//DMA傳輸模式
{
……
/*設(shè)置DMA傳輸方向*/
PlxPci_PlxRegisterWrite(pDevice,,0x90,SglPciAddress|(1<<0)|(1<<3)),;
/*設(shè)置DMA模式*/
PlxPci_PlxRegisterWrite(pDevice,,0x80,0x00020642),;
/*啟動(dòng)DMA傳輸*/
RegValue=PlxPci_PlxRegisterRead(pDevice,,0xA8,NULL),;
RegValue|=(1<<0),;
PlxPci_PlxRegisterWrite(pDevice,0xA8,,RegValue),;
RegValue|=(1<<1);
PlxPci_PlxRegisterWrite(pDevice,,0xA8,,RegValue);
……
}
3.5 中斷響應(yīng)
中斷是Linux系統(tǒng)中非常寶貴的資源,,任何驅(qū)動(dòng)程序都需要申請(qǐng)中斷并注冊(cè)中斷處理才可以使用中斷,。可以使用中斷的方式來讀取硬件設(shè)備中的數(shù)據(jù),。而如果硬件設(shè)備不支持中斷,,則只能采用輪詢的方式來讀取數(shù)據(jù)。硬件設(shè)備中一般包含好幾種不同的中斷,,例如1 Hz,、20 Hz,、100 Hz等。因此,,當(dāng)讀取中斷狀態(tài)位之后還需要將不同的中斷識(shí)別區(qū)分開來才能使用,。另外,為方便用戶層的應(yīng)用軟件對(duì)中斷的使用,,使用信號(hào)機(jī)制來向用戶層發(fā)送中斷信號(hào),,通知用戶層的應(yīng)用軟件獲取中斷狀態(tài)位。中斷響應(yīng)程序如下,。
//中斷響應(yīng)
irq_handler_t plxpci_interrupt(int irq,,void*dev_id,struct pt_regs*regs)
{
……
/*讀取中斷狀態(tài)位,,其中包含多種中斷,,需要在下一步識(shí)別并解析出不同的中斷*/
status=inb(PLXPCI_IRQ_REG(dev));
if(status)
{
/*識(shí)別20 Hz中斷*/
if(status&IRQ_FLAG_20 Hz)
{
……
}
……
/*通知調(diào)度函數(shù)向應(yīng)用層軟件發(fā)送中斷信號(hào)*/
tasklet_schedule(&dev->tlet),;
}
return(IRQ_HANDLED),;
}
4 驅(qū)動(dòng)程序的測(cè)試
4.1 驅(qū)動(dòng)程序的加載
本文中開發(fā)及測(cè)試平臺(tái)所使用的操作系統(tǒng)是中標(biāo)麒麟Linux操作系統(tǒng),該系統(tǒng)的內(nèi)核版本是2.6.32,。Linux下驅(qū)動(dòng)程序模塊的加載通常有動(dòng)態(tài)加載和靜態(tài)加載兩種方式,。靜態(tài)加載就是把編譯生成的驅(qū)動(dòng)程序文件plx8311.ko編譯到內(nèi)核中,每次系統(tǒng)啟動(dòng)時(shí)自動(dòng)調(diào)用,,這種方式比較適合最終版本的驅(qū)動(dòng)程序,。動(dòng)態(tài)加載就是通過insmod命令加載驅(qū)動(dòng)程序,通過rmmod命令可以卸載驅(qū)動(dòng)程序,,這樣隨時(shí)可以修改驅(qū)動(dòng)程序,,對(duì)于還在調(diào)試階段的程序比較方便。
4.2 測(cè)試過程與結(jié)果
測(cè)試前首先保證在計(jì)算機(jī)終端中安裝好時(shí)統(tǒng)卡,,并連接B碼終端,,然后加載驅(qū)動(dòng)程序,使用lsmod命令查看驅(qū)動(dòng)程序是否已經(jīng)加載好,。圖3所示為plx8311驅(qū)動(dòng)加載成功,。當(dāng)驅(qū)動(dòng)程序可以正常加載,并且能夠通過測(cè)試程序讀出時(shí)統(tǒng)卡中的時(shí)間信息和中斷信息,,則說明編寫的驅(qū)動(dòng)程序是可行的,。圖4所示為測(cè)試結(jié)果,前面顯示的是從時(shí)統(tǒng)卡中讀出的當(dāng)前時(shí)間,,后面3個(gè)數(shù)字表示從啟動(dòng)測(cè)試程序到當(dāng)前時(shí)刻所獲得的1 Hz,、20 Hz、100 Hz中斷信號(hào)的個(gè)數(shù)。
5 結(jié)論
Linux系統(tǒng)的開源性加上PCI-E總線在計(jì)算機(jī)系統(tǒng)中的廣泛應(yīng)用,,使得其兩者的結(jié)合越來越緊密,,Linux系統(tǒng)下的PCI-E的驅(qū)動(dòng)開發(fā)也得到了廣泛的關(guān)注。本文結(jié)合實(shí)際項(xiàng)目開發(fā),,通過對(duì)PEX8311時(shí)統(tǒng)卡的驅(qū)動(dòng)程序編寫過程中的各模塊的介紹,,闡述了整個(gè)驅(qū)動(dòng)的開發(fā)流程和相關(guān)技巧,并通過編寫測(cè)試程序完成了驅(qū)動(dòng)程序的測(cè)試工作,,驗(yàn)證了驅(qū)動(dòng)的可用性。
參考文獻(xiàn)
[1] BUDRUK R,, ANDERSON D,, SHANLEY T. PCI Express系統(tǒng)體系結(jié)構(gòu)標(biāo)準(zhǔn)教材[M].田玉敏,王崧,,張波,,譯.北京:電子工業(yè)出版社,2005.
[2] 鄭強(qiáng).Linux驅(qū)動(dòng)開發(fā)入門與實(shí)踐[M].北京:清華大學(xué)出版社,,2010.
[3] 范晶,,胡愛蘭.基于狀態(tài)機(jī)的PEX8311的DMA實(shí)現(xiàn)[J].微型機(jī)與應(yīng)用,2014,,33(22):30-33.
[4] PLX. PEX8311 AA data book version 1.0[OL]. [2015-04-15].http://www.plxtech.com/mydata.