WinCE 流驅動一步一步來(轉載)

 WinCE 流驅動一步一步來(理論基礎)

WinCE.net中,最簡單的一個驅動程序莫過於一個內置(Built-in)設備的流接口驅動。對於一個不支持熱拔插的設備,最快捷的方法就是爲其實現一個內置的流接口的驅動。

對於這樣一類驅動程序,我們只需要按一種特定的規則實現一個動態庫,其中實現對所有的硬件功能的調用,再將這個動態庫加入系統中,然後設置相關的註冊表項,使得在系統啓動時設備管理器能識別並且加載這個設備即可。

1.  實現動態鏈接庫

此動態鏈接庫與應用程序層所用的庫並不很大差別,源文件可以是CC++、甚至彙編,只是它要實現以下函數。

u       DllEntry(HINSTANCE DllInstance, INT Reason, LPVOID Reserved )

這個函數是動態鏈接庫的入口,每個動態鏈接庫都需要輸出這個函數,它只在動態庫被加載和卸載時被調用,也就是設備管理器調用LoadLibrary而引起它被裝入內存和調用UnloadLibrary將其從內存釋放時被調用,因而它是每個動態鏈接庫最早被調用的函數,一般用它做一些全局變量的初始化。

參數:

DllInstanceDLL的句柄,與一個EXE文件的句柄功能類似,一般可以通過它在得到DLL中的一些資源,例如對話框,除此之外一般沒什麼用處。

Reason:

一般我們只關心兩個值:DLL_PROCESS_ATTACHDLL_PROCESS_DETACHReason等於前者是動態庫被加載,等於後者是動態庫被釋放。

所以,我們可以在Reason等於前者是初始化一些資源,等於後者時將其釋放。

u       DWORD XXX_Init( LPCTSTR pContext, LPCVOID lpvBusContext);
它是驅動程序的動態庫被成功裝載以後第一個被調用的函數。其調用時間僅次與DllEntry,而且,當一個庫用來生成多於一個的驅動程序實例時僅調用一次DllEntry,而xxx_Init會被調用多次。驅動程序應當在這個函數中初始化硬件,如果初始化成功,就分配一個自已的內存空間(通常用結構體表示),將自已的狀態保存起來,並且將此內存塊的地址做爲一個DWORD值返回給上層。設備管理器就會用在調用XXX_Open時將此句柄傳回,我們就能訪問自已的狀態。如果初始化失敗,則返回0以通知這個驅動程序沒有成功加載,先前所分配的系統資源應該全部釋放,此程序的生命即告終至。
當這個函數成功返回,設備管理器對這個程序就不做進一步處理,除非它設置了更多的特性。至此一個各爲XXX的設備就已經加載成功,當用戶程序調用CreateFile來打開這個設備時,設備管理器就會調XXX_Open函數。
參數:
pContext系統傳入的註冊表鍵,通過它可以講到我們在註冊表中設置的配置信息。
lpvBusContext一般不用。
實際上,很多程序中將這個函數寫成了DWORD XXX_Init( DWORD pContext ),我們只需要將pContext轉化成LPCTSTR即可。
u       DWORD XXX_Open(DWORD hDeviceContext,DWORD dwAccess, DWORD dwShareMode);
當用戶程序調用CreateFile打開這個設備時,設備管理器就會調用此驅動程序的XXX_Open函數。
參數:
hDeviceContext XXX_Init 返回給上層的值,也就是我們在XXX_Init中分配的用來記錄驅動程序信息的那個結構體的指針,我們可以在這個函數中直接將其轉化成所定義的結構,從而獲取驅動程序的信息。
dwAccess 上層所要求的訪問方式,可以是讀或者寫,或者是0,即不讀也不寫。
dwShareMode 上層程序所請求的共享模式,可以是共享讀、共享寫這兩個值的邏輯或,或者是0,即獨佔式訪問。
系統層對設備文件的存取權限及共享方法已經做了處理,所以在驅動程序中對這兩個參數一般可以不用理會。
這個函數一般不用做太多處理,可以直接返回hDeviceContext表示成功對於一個不支持多個用戶的硬件,在設備已經打開後,應該總是返回0以至失敗,則CreateFile調用不成功。
u       DWORD XXX_Close(  DWORD hDeviceContext );
      當用戶程序調用CloseHandle關閉這個設備句柄時,這個函數就會被設備管理器調用。
參數:
hDeviceContext XXX_Open返回給上層的那個值。
這個函數應該做與XXX_Open相反的事情,具體包括:釋放XXX_Open分配的內存,將驅動程序被打開的記數減少等。
u       DWORD XXX_Deinit( DWORD hDeviceContext );
這個函數在設備被卸載時被調用,它應該實現與XXX_Init相反的操作,主要爲釋放前者佔用的所有系統資源。
參數:
hDeviceContext XXX_Init函數返回給上層的那個句柄
u       void XXX_PowerUp(  DWORD hDeviceContext );
u       void XXX_PowerDown(DWORD hDeviceContext );
正如其名稱中體現的那樣,這兩個函數在系統PowerUpPowerDown時被調用,這兩個函數中不能使用任何可能引起線程切換的函數,否則會引起系統死機。所以,在這兩個函數中,實際上幾乎是什麼做不了,一般在PowerDown時做一個標誌,讓驅動程序知道自已曾經被Power Down過。在Power Down/On的過程中硬件可能會掉電,所以,儘管Power On以後,原來的IO操作仍然會從接着執行,但可能會失敗。這時,當我們發現一次IO操作失敗是因爲程序曾經進入過Power Down狀態,就重新初始化一次硬件,再做同樣的IO操作。
u       BOOL XXX_IOControl(
                        DWORD hDeviceContext,
                        DWORD dwCode,
                        PBYTE pBufIn,
                        DWORD dwLenIn,
                        PBYTE pBufOut,
                        DWORD dwLenOut,
                        PDWORD pdwActualOut
                   );
幾乎可以說一個驅動程序的所有功能都可以在這個函數中實現。對於一類CE自身已經支持的設備,它們已經被定義了一套IO操作定,我們只需按照各類設備已經定義的內容去實現所有的IO操作。但當我們實現一個自定義的設備時,我們就可以隨心所欲定義我們自已的IO操作。
參數:
hDeviceContext XXX_Open返回給上層的那個句柄,即我們自已定義的,用來存放程序所有信息的一個結構。
dwCode IO操作碼,如果是CE已經支持的設備類,就用它已經定義好碼值,否則就可以自已定義。
pBufIn 傳入的Buffer,每個IO操作碼都會定義自已的Buffer結構
dwLenIn pBufIn以字節記的大小 
pBufOutdwLenOut分別爲傳出的Buffer,及其以字節記的大小
pdwActualOut 驅動程序實際在pBufOut中填入的數據以字節記的大小
其中,前兩個參數是必須的,其它的任何一個都有可能是NULL0。所以,當給pdwActualOut賦值時應該先判斷它是否爲一個有效的地址。

2.    註冊表的設置

系統啓動時啓動設備管理程序,設備管理程序讀取HKEY_LOCAL_MACHINE/Drivers/BuiltIn鍵的內容並加載已列出的流接口驅動程序。因此註冊表對於驅動的加載有着關鍵作用。下面我們以設備名是“STR”來舉一個例子:

[HKEY_LOCAL_MACHINE/Drivers/BuiltIn/STR]
  "Prefix"="STR"
  "Dll"="MyDev.Dll"
  "Order"=dword:1

        

其中  "Prefix"="STR" 表示流接口的前綴,我們以後可以通過Creatfile 函數來實現對這個流接口的操作,實現如下:HANDLE hStr = CreateFile(TEXT("str1:"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0) 。其中“str1:”中的表示設備好“1”是必需的,不管你的設備當中是不是剛好只有一個,不然的話會出現打開不了這個設備的情況。

"Dll"="MyDev.Dll" 指的是“STR”設備調用的驅動程序 DLL

HKEY_LOCAL_MACHINE/Drivers/BuiltIn/SampleDev 表示該註冊表存在於CE系統註冊表的 HKEY_LOCAL_MACHINE/Drivers/BuiltIn/SampleDev 目錄下。

3.    創建MakefileSources.def文件

這三個文件主要是來控制編譯的,Makefile .def 文件實現比較簡單,Sources 文件相對比較複雜。

Makefile 只需要這樣一行!INCLUDE $(_MAKEENVROOT)/makefile.def ,具體的實現什麼任務還不知道。

.def 文件定義需要輸出的函數,這些函數能夠被其它代碼用動態加載的方法調用。

Sources 這個文件很重要,內容也多,最基本的一個文件該有如下內容。

 

TARGETNAME=MyDev(指定要生成的動態庫的名稱)

TARGETTYPE=DYNLINK(指定要生成的是一個動態庫)

TARGETLIBS=$(_COMMONSDKROOT)/lib/$(_CPUINDPATH)/coredll.lib /

              $(_COMMONOAKROOT)/lib/$(_CPUINDPATH)/ceddk.lib

(下面兩項指定需要與哪些動態庫鏈接,一般要第一項就足夠了)

DLLENTRY=DllEntry(指定動態庫的入口函數)

SOURCES= (請在這寫上你所有源文件的名字,它們將會被編譯)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章