0引言
PCI總線是一種成熟的計算機標準總線,而Linux操作系統則是一種源代碼公開的操作系統。Linux構架完全沿襲了UNIX的系統架構,它不但擁有UNIX的全部功能,而且具有UNIX穩定、可靠、安全的優點,尤其是Linux加入GNU并遵循公共版權許可證(GPL)之后,幾乎所有的GNU軟件都可以移植到Linux,從而完善和提高了Linux系統的使用性,并逐步成為通信、工業控制、消費電子等領域的主流操作系統。
本文主要對Linux環境下開發PCI9054芯片驅動的具體方法進行描述,并給出了如何將驅動程序編譯進內核,以使驅動模塊靜態加載的方法。
1 PCI9054簡介
PCI總線協議一般需要繁瑣的邏輯驗證和時序分析工作,而且開發周期較長,因此,更多的做法是采用通用PCI接口芯片,這樣,只需要控制接口芯片的幾根控制線,就可以完成PCI總線的數據傳輸,故可大大減少開發時間和成本。
PCI9054是由美國PLX公司生產的PCI橋接芯片,該芯片采用先進的PLX流水線結構技術,符合PCI本地總線規范2.2版,并配有可選的串行EEPROM接口。芯片的本地總線時鐘可與PCI時鐘異步,其內部有6種可編程FIFO,可以實現零等待突發傳輸及本地總線與PCI總線的異步操作,同時支持主模式、從模式、DMA傳輸模式,可廣泛用于嵌入式系統中。
PCI9054的地址資源是由其基址寄存器來設置的,PCI9054的配置空間有六個基址寄存器:BARO~BAR5,其中BARO映射到配置空間的基地址,BARl為映射到I/O空間的基地址,BAR2~BAR5可以定義為映射到內存地址空間的基地址。配置空間的內容可被Linux核心中的PCI初始化代碼使用,I/O空間和內存地址空間可提供給設備驅動程序使用。
2 Linux下的PCI驅動程序
Linux將所有外部設備看成是一類特殊文件,即“設備文件”,它可分為兩大類:字符設備和塊設備。字符設備是必須以串行順序依次進行訪問的設備,如觸摸屏、磁帶驅動器、鼠標等。塊設備則是利用一塊系統內存作為緩沖區,以塊為單位進行操作,如硬盤、軟驅等。字符設備不經過系統的快速緩沖,而塊設備則需經過系統的快速緩沖。此外,Linux下還存在一類外設,即“網絡設備”,網絡設備主要針對數據包的接收和發送而設計,它并不對應于文件系統的節點。PCI驅動通常可以看做字符設備的驅動來設計。
Linux的操作過程分為兩個步驟:首先用PCI驅動將內核與設備掛接起來;緊接著通過應用程序來根據設備文件所屬類型并通過驅動提供的接口函數來操作設備。這使得PCI驅動至少應該包含兩部分內容:一是PCI設備驅動,二是具體設備本身的驅動。
Linux系統啟動后會自動檢測PCI設備信息,并分別記錄在pci_dev結構體中形成一個PCI設備鏈表pci_devices。這個結構體記錄了PCI設備幾乎所有的硬件信息,包括廠商ID、設備ID、各種資源等,PCI驅動就是根據廠商ID和設備ID來連接設備并加載驅動的。
驅動加載以后,為了操作設備文件,Linux會在include/linux/fs.h中提供一個數據結構file_operatiONs,該數據結構可向Linux文件系統注冊一組文件操作,以定義設備提供的諸如open、close、read、write等操作,內核則將通過file_operations結構訪問驅動程序所提供的函數。
3 PCI9054驅動的實現
Linux下設備驅動程序的編寫應遵循一定的框架結構進行,大致可分為模塊的加載與卸載、設備的初始化、設備的打開及操作、中斷處理和設備的釋放等幾部分。
3.1 模塊的加載與初始化
當Linux內核啟動時,會完成對所有PCI設備的掃描、登錄和資源分配等初始化操作,并建立起所有PCI設備的拓撲結構,此后,在加載PCI驅動程序時,就會通過加載驅動程序模塊入口跳轉到設備初始化模塊。Linux2.4內核與Linux2.6內核的初始化方式是不同的,在Linux-2.4內核中,初始化程序首先會使用pci_present函數來判斷PCI總線是否被內核支持,然后使用regiSTer_chrdev函數來注冊設備,這樣就可以輪詢PCI總線上的設備,并利用pci_find_device函數檢查設備是否插在總線插槽上。如果在,則保存其所占用的插槽位置信息,并返回pci_dev結構,然后將pci_dev結構加入到設備鏈表中,直到輪詢完畢。圖1所示是Linux下PCI驅動程序的流程圖。
3.2 設備的打開及操作
通過上述步驟,系統內核就可以發現并打開設備了。設備打開模塊中主要完成檢查讀寫模式以及申請對設備的控制權等工作。同時可分配并填private_data數據結構、申請PCI設備I/O資源的占用、申請中斷、注冊中斷處理程序。
Linux是以操作文件的方式來操作設備的,也就是通過系統定義的file_operation結構體向Linux文件系統注冊一些操作設備的函數。其file_operation結構體的定義如下:
當應用程序對設備文件進行諸如open、close、read、write等操作時,Linux內核將通過file_operation結構訪問驅動程序提供的函數。
簡單的讀寫任務直接用read、write函數就可以完成,而復雜的控制則需要使用ioctl函數。ioctl函數是設備驅動程序中對設備的I/O通道進行管理的函數,可以對設備的一些特性進行控制。因此,為實現對PCI設備各種I/O資源的訪問和實現不同的傳輸方式,通常都需要使用ioc-tl函數,驅動程序的ioctl控制命令差不多完全是用一個switch語句來實現的,可實現初始化信息的初始化控制、讀寫配置空間、讀寫I/O端口地址空間、讀寫I/O內存空間、DMA傳輸等多種控制操作。
3.3 中斷處理
Linux將中斷處理程序分解為頂半部和底半部兩個半部。驅動程序在打開設備時,為系統安裝了中斷處理程序,當硬件設備觸發中斷時,中斷處理程序首先調用頂半部程序以判斷中斷類型,對中斷類型的判斷可通過訪問PCI設備處理中斷的寄存器來實現。當有中斷信號來臨時,驅動程序將中斷處理任務排進任務隊列中進行“登記”并清除中斷標志,這樣做的目的是占用較少的內核時間,然后再通過調度底半部來運行,這樣,內核負責底半部的函數就會處理任務隊列中的中斷任務。可以說,底半部幾乎做了中斷處理程序所有的事情。
驅動程序在設備驅動對象數據結構中為中斷處理定義了一個任務隊列Task_DpcForIsr,并將其routine參數設置為負責底半部的函數,data參數設置為傳遞給底半部函數的參數,這樣,當系統獲得中斷信號時,驅動程序就可將中斷處理任務排進該任務隊列中,以提供給底半部應用。
3.4 釋放設備模塊及卸載
釋放設備模塊主要負責釋放對設備的控制權,同時釋放所占用的內存和中斷等。具體來說,首先是釋放對設備的控制權。即對設備控制權的釋放,這只需簡單地將控制設備控制權的信號量釋放即可。在Linux下可使用up,其調用形式是up(&sem),它可遞增信號量的值,并換醒所有正在等待信號量轉為可用狀態的進程。其次是釋放由open分配的、保存在filp->private_data中的所有內容,將其值設為NULL。接著釋放中斷。最后釋放PCI設備I/O資源的占用權。對PCI設備的I/O端口資源而言,實現I/O端口資源占有權的釋放是通過release_region函數來完成的,而對于I/O內存資源,則需要使用iounmap函數同時釋放其內存映射。
4 Linux下驅動模塊的加載
Linux下驅動程序模塊的加載通常有靜態加載和動態加載兩種方式。動態加載是利用Linux的module特性,在系統啟動后使用insmode命令把驅動程序(.o文件)添加上去,然后通過rmmod命令卸載,這種加載方式有利于程序的調試,可以隨時更改;靜態加載就是把驅動程序直接編譯到內核里,在系統啟動后直接調用,顯然這種方式對于調試階段的程序比較麻煩,而且效率較低,因而只適用于最終版本的程序。因此,程序開發者通常先用動態加載方式來調試,調試完畢后,再編譯到內核里使用。
靜態加載時,通常先把驅動程序原文件放在內核驅動相應類型的文件夾下,這里的PCI驅動屬于字符類型, 可拷貝到…\linux-2.4.x\drivers\char下。然后再更改…\linux-2.4.x\drivers\char\Makefile文件,并添加如下語句:
這樣做的目的是根據編譯選項$( CON-FIG_PLX9054)來決定是否要添加設備驅動。
之后,再在…\linux-2.4.x\drivers\char\Config.in中添加語句
tristate’PLX9054 Support’CONFIG_PLX9054,這樣就可以在運行menuconfig時產生與設備對應的編譯選項,圖2所示是其編譯選項界面圖。圖中,在前面的尖括號中點Y表示靜態加載,點M表示作為模塊動態加載。
最后,運行make menuconfig,剪切內核,并選中需要的模塊所對應的編譯選項,編譯內核。
5 驅動程序的測試
由于所編寫的驅動程序是在PLX官方驅動的基礎上得來的,因此,在這里,為了方便測試起見,也應使用PLX公司的SDK包中的測試程序來
查看PCI9054的配置情況,圖3所示為五個基址寄存器的映射情況。圖4所示為公共緩沖區的映射情況示意圖。
6 結束語
本文首先介紹了在Linux下開發PCI驅動的方法,然后介紹了如何將驅動靜態編譯到內核,這種方法非常適用于開發以Linux作為嵌入式系統的驅動程序。可以看出,由于Linux系統完全開源,其驅動程序較Windows下的驅動程序簡單易懂,框架感更強。此外,由于其具有豐富的開源資源,故其開發成本較低。因此,我們有理由相信,Linux在未來的操作系統市場中的地位會變得越來越重要。
PCI總線是一種成熟的計算機標準總線,而Linux操作系統則是一種源代碼公開的操作系統。Linux構架完全沿襲了UNIX的系統架構,它不但擁有UNIX的全部功能,而且具有UNIX穩定、可靠、安全的優點,尤其是Linux加入GNU并遵循公共版權許可證(GPL)之后,幾乎所有的GNU軟件都可以移植到Linux,從而完善和提高了Linux系統的使用性,并逐步成為通信、工業控制、消費電子等領域的主流操作系統。
本文主要對Linux環境下開發PCI9054芯片驅動的具體方法進行描述,并給出了如何將驅動程序編譯進內核,以使驅動模塊靜態加載的方法。
1 PCI9054簡介
PCI總線協議一般需要繁瑣的邏輯驗證和時序分析工作,而且開發周期較長,因此,更多的做法是采用通用PCI接口芯片,這樣,只需要控制接口芯片的幾根控制線,就可以完成PCI總線的數據傳輸,故可大大減少開發時間和成本。
PCI9054是由美國PLX公司生產的PCI橋接芯片,該芯片采用先進的PLX流水線結構技術,符合PCI本地總線規范2.2版,并配有可選的串行EEPROM接口。芯片的本地總線時鐘可與PCI時鐘異步,其內部有6種可編程FIFO,可以實現零等待突發傳輸及本地總線與PCI總線的異步操作,同時支持主模式、從模式、DMA傳輸模式,可廣泛用于嵌入式系統中。
PCI9054的地址資源是由其基址寄存器來設置的,PCI9054的配置空間有六個基址寄存器:BARO~BAR5,其中BARO映射到配置空間的基地址,BARl為映射到I/O空間的基地址,BAR2~BAR5可以定義為映射到內存地址空間的基地址。配置空間的內容可被Linux核心中的PCI初始化代碼使用,I/O空間和內存地址空間可提供給設備驅動程序使用。
2 Linux下的PCI驅動程序
Linux將所有外部設備看成是一類特殊文件,即“設備文件”,它可分為兩大類:字符設備和塊設備。字符設備是必須以串行順序依次進行訪問的設備,如觸摸屏、磁帶驅動器、鼠標等。塊設備則是利用一塊系統內存作為緩沖區,以塊為單位進行操作,如硬盤、軟驅等。字符設備不經過系統的快速緩沖,而塊設備則需經過系統的快速緩沖。此外,Linux下還存在一類外設,即“網絡設備”,網絡設備主要針對數據包的接收和發送而設計,它并不對應于文件系統的節點。PCI驅動通常可以看做字符設備的驅動來設計。
Linux的操作過程分為兩個步驟:首先用PCI驅動將內核與設備掛接起來;緊接著通過應用程序來根據設備文件所屬類型并通過驅動提供的接口函數來操作設備。這使得PCI驅動至少應該包含兩部分內容:一是PCI設備驅動,二是具體設備本身的驅動。
Linux系統啟動后會自動檢測PCI設備信息,并分別記錄在pci_dev結構體中形成一個PCI設備鏈表pci_devices。這個結構體記錄了PCI設備幾乎所有的硬件信息,包括廠商ID、設備ID、各種資源等,PCI驅動就是根據廠商ID和設備ID來連接設備并加載驅動的。
驅動加載以后,為了操作設備文件,Linux會在include/linux/fs.h中提供一個數據結構file_operatiONs,該數據結構可向Linux文件系統注冊一組文件操作,以定義設備提供的諸如open、close、read、write等操作,內核則將通過file_operations結構訪問驅動程序所提供的函數。
3 PCI9054驅動的實現
Linux下設備驅動程序的編寫應遵循一定的框架結構進行,大致可分為模塊的加載與卸載、設備的初始化、設備的打開及操作、中斷處理和設備的釋放等幾部分。
3.1 模塊的加載與初始化
當Linux內核啟動時,會完成對所有PCI設備的掃描、登錄和資源分配等初始化操作,并建立起所有PCI設備的拓撲結構,此后,在加載PCI驅動程序時,就會通過加載驅動程序模塊入口跳轉到設備初始化模塊。Linux2.4內核與Linux2.6內核的初始化方式是不同的,在Linux-2.4內核中,初始化程序首先會使用pci_present函數來判斷PCI總線是否被內核支持,然后使用regiSTer_chrdev函數來注冊設備,這樣就可以輪詢PCI總線上的設備,并利用pci_find_device函數檢查設備是否插在總線插槽上。如果在,則保存其所占用的插槽位置信息,并返回pci_dev結構,然后將pci_dev結構加入到設備鏈表中,直到輪詢完畢。圖1所示是Linux下PCI驅動程序的流程圖。
3.2 設備的打開及操作
通過上述步驟,系統內核就可以發現并打開設備了。設備打開模塊中主要完成檢查讀寫模式以及申請對設備的控制權等工作。同時可分配并填private_data數據結構、申請PCI設備I/O資源的占用、申請中斷、注冊中斷處理程序。
Linux是以操作文件的方式來操作設備的,也就是通過系統定義的file_operation結構體向Linux文件系統注冊一些操作設備的函數。其file_operation結構體的定義如下:
當應用程序對設備文件進行諸如open、close、read、write等操作時,Linux內核將通過file_operation結構訪問驅動程序提供的函數。
簡單的讀寫任務直接用read、write函數就可以完成,而復雜的控制則需要使用ioctl函數。ioctl函數是設備驅動程序中對設備的I/O通道進行管理的函數,可以對設備的一些特性進行控制。因此,為實現對PCI設備各種I/O資源的訪問和實現不同的傳輸方式,通常都需要使用ioc-tl函數,驅動程序的ioctl控制命令差不多完全是用一個switch語句來實現的,可實現初始化信息的初始化控制、讀寫配置空間、讀寫I/O端口地址空間、讀寫I/O內存空間、DMA傳輸等多種控制操作。
3.3 中斷處理
Linux將中斷處理程序分解為頂半部和底半部兩個半部。驅動程序在打開設備時,為系統安裝了中斷處理程序,當硬件設備觸發中斷時,中斷處理程序首先調用頂半部程序以判斷中斷類型,對中斷類型的判斷可通過訪問PCI設備處理中斷的寄存器來實現。當有中斷信號來臨時,驅動程序將中斷處理任務排進任務隊列中進行“登記”并清除中斷標志,這樣做的目的是占用較少的內核時間,然后再通過調度底半部來運行,這樣,內核負責底半部的函數就會處理任務隊列中的中斷任務。可以說,底半部幾乎做了中斷處理程序所有的事情。
驅動程序在設備驅動對象數據結構中為中斷處理定義了一個任務隊列Task_DpcForIsr,并將其routine參數設置為負責底半部的函數,data參數設置為傳遞給底半部函數的參數,這樣,當系統獲得中斷信號時,驅動程序就可將中斷處理任務排進該任務隊列中,以提供給底半部應用。
3.4 釋放設備模塊及卸載
釋放設備模塊主要負責釋放對設備的控制權,同時釋放所占用的內存和中斷等。具體來說,首先是釋放對設備的控制權。即對設備控制權的釋放,這只需簡單地將控制設備控制權的信號量釋放即可。在Linux下可使用up,其調用形式是up(&sem),它可遞增信號量的值,并換醒所有正在等待信號量轉為可用狀態的進程。其次是釋放由open分配的、保存在filp->private_data中的所有內容,將其值設為NULL。接著釋放中斷。最后釋放PCI設備I/O資源的占用權。對PCI設備的I/O端口資源而言,實現I/O端口資源占有權的釋放是通過release_region函數來完成的,而對于I/O內存資源,則需要使用iounmap函數同時釋放其內存映射。
4 Linux下驅動模塊的加載
Linux下驅動程序模塊的加載通常有靜態加載和動態加載兩種方式。動態加載是利用Linux的module特性,在系統啟動后使用insmode命令把驅動程序(.o文件)添加上去,然后通過rmmod命令卸載,這種加載方式有利于程序的調試,可以隨時更改;靜態加載就是把驅動程序直接編譯到內核里,在系統啟動后直接調用,顯然這種方式對于調試階段的程序比較麻煩,而且效率較低,因而只適用于最終版本的程序。因此,程序開發者通常先用動態加載方式來調試,調試完畢后,再編譯到內核里使用。
靜態加載時,通常先把驅動程序原文件放在內核驅動相應類型的文件夾下,這里的PCI驅動屬于字符類型, 可拷貝到…\linux-2.4.x\drivers\char下。然后再更改…\linux-2.4.x\drivers\char\Makefile文件,并添加如下語句:
這樣做的目的是根據編譯選項$( CON-FIG_PLX9054)來決定是否要添加設備驅動。
之后,再在…\linux-2.4.x\drivers\char\Config.in中添加語句
tristate’PLX9054 Support’CONFIG_PLX9054,這樣就可以在運行menuconfig時產生與設備對應的編譯選項,圖2所示是其編譯選項界面圖。圖中,在前面的尖括號中點Y表示靜態加載,點M表示作為模塊動態加載。
最后,運行make menuconfig,剪切內核,并選中需要的模塊所對應的編譯選項,編譯內核。
5 驅動程序的測試
由于所編寫的驅動程序是在PLX官方驅動的基礎上得來的,因此,在這里,為了方便測試起見,也應使用PLX公司的SDK包中的測試程序來
查看PCI9054的配置情況,圖3所示為五個基址寄存器的映射情況。圖4所示為公共緩沖區的映射情況示意圖。
6 結束語
本文首先介紹了在Linux下開發PCI驅動的方法,然后介紹了如何將驅動靜態編譯到內核,這種方法非常適用于開發以Linux作為嵌入式系統的驅動程序。可以看出,由于Linux系統完全開源,其驅動程序較Windows下的驅動程序簡單易懂,框架感更強。此外,由于其具有豐富的開源資源,故其開發成本較低。因此,我們有理由相信,Linux在未來的操作系統市場中的地位會變得越來越重要。
評論
查看更多