WINCE驱动程序快速入门
有人也許和我一樣比較心急,想盡快知道如何去寫一個具體的驅(qū)動程序,這里,假設(shè)您對驅(qū)動程序已經(jīng)有比較好的了解,告訴大家一個快速上路的方法。當然,如果您有足夠的時間我建議在動手之前還是深入了解一下CE整個系統(tǒng)架構(gòu)。
在CE中,最簡單的一個驅(qū)動程序莫過于一個內(nèi)置(Built-in)設(shè)備的流接口驅(qū)動。對于一個不支持熱拔插的設(shè)備,最快捷的方法就是為其實現(xiàn)一個內(nèi)置的流接口的驅(qū)動。
對于這樣一類驅(qū)動程序,我們只需要按一種特定的規(guī)則實現(xiàn)一個動態(tài)庫,其中實現(xiàn)對所有的硬件功能的調(diào)用,再將這個動態(tài)庫加入系統(tǒng)中,然后設(shè)置相關(guān)的注冊表項,使得在系統(tǒng)啟動時設(shè)備管理器能識別并且加載這個設(shè)備即可。
3.1?????????????? 實現(xiàn)動態(tài)鏈接庫
此動態(tài)鏈接庫與應用程序?qū)铀玫膸觳⒉缓艽蟛顒e,源文件可以是C、C++、甚至匯編,,只是它要實現(xiàn)以下函數(shù)。
l???????? DllEntry(HINSTANCE DllInstance, INT Reason, LPVOID Reserved)
這個函數(shù)是動態(tài)鏈接庫的入口,每個動態(tài)鏈接庫都需要輸出這個函數(shù),它只在動態(tài)庫被加載和卸載時被調(diào)用,也就是設(shè)備管理器調(diào)用LoadLibrary而引起它被裝入內(nèi)存和調(diào)用UnloadLibrary將其從內(nèi)存釋放時被調(diào)用,因而它是每個動態(tài)鏈接庫最早被調(diào)用的函數(shù),一般用它做一些全局變量的初始化。
?
參數(shù):
DllInstance:DLL的句柄,與一個EXE文件的句柄功能類似,一般可以通過它在得到DLL中的一些資源,例如對話框,除此之外一般沒什么用處。
Reason:
一般我們只關(guān)心兩個值:DLL_PROCESS_ATTACH與DLL_PROCESS_DETACH,Reason等于前者是動態(tài)庫被加載,等于后者是動態(tài)庫被釋放。
所以,我們可以在Reason等于前者是初始化一些資源,等于后者時將其釋放。
l??????? DWORD XXX_Init( LPCTSTR pContext, LPCVOID lpvBusContext);它是驅(qū)動程序的動態(tài)庫被成功裝載以后第一個被調(diào)用的函數(shù)。其調(diào)用時間僅次與DllEntry,而且,當一個庫用來生成多于一個的驅(qū)動程序?qū)嵗龝r僅調(diào)用一次DllEntry,而xxx_Init會被調(diào)用多次。驅(qū)動程序應當在這個函數(shù)中初始化硬件,如果初始化成功,就分配一個自已的內(nèi)存空間(通常用結(jié)構(gòu)體表示),將自已的狀態(tài)保存起來,并且將此內(nèi)存塊的地址做為一個DWORD值返回給上層。設(shè)備管理器就會用在調(diào)用XXX_Open時將此句柄傳回,我們就能訪問自已的狀態(tài)。如果初始化失敗,則返回0以通知這個驅(qū)動程序沒有成功加載,先前所分配的系統(tǒng)資源應該全部釋放,此程序的生命即告終至。當這個函數(shù)成功返回,設(shè)備管理器對這個程序就不做進一步處理,除非它設(shè)置了更多的特性。至此一個各為XXX的設(shè)備就已經(jīng)加載成功,當用戶程序調(diào)用CreateFile來打開這個設(shè)備時,設(shè)備管理器就會調(diào)XXX_Open函數(shù)。 參數(shù):pContext:系統(tǒng)傳入的注冊表鍵,通過它可以講到我們在注冊表中設(shè)置的配置信息。lpvBusContext:一般不用,在這先不做講解實際上,很多程序中將這個函數(shù)寫成了DWORD XXX_Init( DWORD pContext )我們只需要將pContext轉(zhuǎn)化成LPCTSTR即可。 l??????? DWORD XXX_Open(DWORD hDeviceContext,DWORD dwAccess, DWORD dwShareMode);當用戶程序調(diào)用CreateFile打開這個設(shè)備時,設(shè)備管理器就會調(diào)用此驅(qū)動程序的XXX_Open函數(shù)。參數(shù):hDeviceContext XXX_Init 返回給上層的值,也就是我們在XXX_Init中分配的用來記錄驅(qū)動程序信息的那個結(jié)構(gòu)體的指針,我們可以在這個函數(shù)中直接將其轉(zhuǎn)化成所定義的結(jié)構(gòu),從而獲取驅(qū)動程序的信息。dwAccess上層所要求的訪問方式,可以是讀或者寫,或者是0,即不讀也不寫dwShareMode 上層程序所請求的共享模式,可以是共享讀、共享寫這兩個值的邏輯或,或者是0,即獨占式訪問。系統(tǒng)層對設(shè)備文件的存取權(quán)限及共享方法已經(jīng)做了處理,所以在驅(qū)動程序中對這兩個參數(shù)一般可以不用理會。這個函數(shù)一般不用做太多處理,可以直接返回hDeviceContext表示成功,對于一個不支持多個用戶的硬件,在設(shè)備已經(jīng)打開后,應該總是返回0以至失敗,則CreateFile調(diào)用不成功。 l??????? DWORD XXX_Close( DWORD hDeviceContext );???? 當用戶程序調(diào)用CloseHandle關(guān)閉這個設(shè)備句柄時,這個函數(shù)就會被設(shè)備管理器調(diào)用。參數(shù):????? hDeviceContext 為XXX_Open返回給上層的那個值。????? 這個函數(shù)應該做與XXX_Open相反的事情,具體包括:釋放XXX_Open分配的內(nèi)存,將驅(qū)動程序被打開的記數(shù)減少等。 l??????? DWORD XXX_Deinit( DWORD hDeviceContext );?????? 這個函數(shù)在設(shè)備被卸載時被調(diào)用,它應該實現(xiàn)與XXX_Init相反的操作,主要為釋放前者占用的所有系統(tǒng)資源。參數(shù):?????? hDeviceContext XXX_Init函數(shù)返回給上層的那個句柄l??????? void XXX_PowerUp( DWORD hDeviceContext );l??????? void XXX_PowerDown(DWORD hDeviceContext );????? 正如其名稱中體現(xiàn)的那樣,這兩個函數(shù)在系統(tǒng)PowerUp與PowerDown時被調(diào)用,這兩個函數(shù)中不能使用任何可能引起線程切換的函數(shù),否則會引起系統(tǒng)死機。所以,在這兩個函數(shù)中,實際上幾乎是什么做不了,一般在PowerDown時做一個標志,讓驅(qū)動程序知道自已曾經(jīng)被Power Down過。在Power Down/On的過程中硬件可能會掉電,所以,盡管Power On以后,原來的IO操作仍然會從接著執(zhí)行,但可能會失敗。這時,當我們發(fā)現(xiàn)一次IO操作失敗是因為程序曾經(jīng)進入過Power Down狀態(tài),就重新初始化一次硬件,再做同樣的IO操作。l??????? BOOL XXX_IOControl(??????????????????? DWORD hDeviceContext,??????????????????? DWORD dwCode,??????????????????? PBYTE pBufIn,??????????????????? DWORD dwLenIn,??????????????????? PBYTE pBufOut,??????????????????? DWORD dwLenOut,??????????????????? PDWORD pdwActualOut???????????????? );幾乎可以說一個驅(qū)動程序的所有功能都可以在這個函數(shù)中實現(xiàn)。對于一類CE自身已經(jīng)支持的設(shè)備,它們已經(jīng)被定義了一套IO操作定,我們只需按照各類設(shè)備已經(jīng)定義的內(nèi)容去實現(xiàn)所有的IO操作。但當我們實現(xiàn)一個自定義的設(shè)備時,我們就可以隨心所欲定義我們自已的IO操作。參數(shù):hDeviceContext XXX_Open返回給上層的那個句柄,即我們自已定義的,用來存放程序所有信息的一個結(jié)構(gòu)。dwCode???? IO操作碼,如果是CE已經(jīng)支持的設(shè)備類,就用它已經(jīng)定義好碼值,否則就可以自已定義。pBufIn傳入的Buffer,每個IO操作碼都會定義自已的Buffer結(jié)構(gòu)dwLenIn pBufIn以字節(jié)記的大小 pBufOut,dwLenOut分別為傳出的Buffer,及其以字節(jié)記的大小pdwActualOut?? 驅(qū)動程序?qū)嶋H在pBufOut中填入的數(shù)據(jù)以字節(jié)記的大小 其中,前兩個參數(shù)是必須的,其它的任何一個都有可能是NULL或0。所以,當給pdwActualOut賦值時應該先判斷它是否為一個有效的地址 3.2注冊表設(shè)置
l???????? 在注冊表中添加如下項目。(一般放在Platform.reg)
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SampleDev]
?? "Prefix"="XXX"
?? "Dll"="MyDev.Dll"
?? "Order"=dword:1
?
l???????? 在BIB文件中添加項目,將所用到的文件加入BIN文件(一般放在Platform.bib)。
MyDev.dll????? $(_FLATRELEASEDIR)\MyDev.dll?????? NK SH
l???????? 注:
SampleDev為任意與其它項目不重名的字符串.
每個函數(shù)名的前綴XXX可以是任意大寫的字符串,只要保證與注冊表中Prefix后面的值相同就行。
3.3編譯程序
?????? 現(xiàn)在,已經(jīng)知道了需要實現(xiàn)哪些東西,一定想知道如何去實現(xiàn)它。一個最直接的方法就是在platform/BSP/drivers 下新建一個目錄,然后在drivers目錄中的dirs文件中加入以你剛新建的目錄名。
?????? 在剛新建的目錄下,新建你的C源代碼文件,在其中實現(xiàn)上面所述的函數(shù),及其功能。新建名稱分別為sources, makefile, mydev.def的文件。其內(nèi)容如下:
?????? makefile: 只需要這樣一行
!INCLUDE $(_MAKEENVROOT)\makefile.def
?
mydriver.def文件定義需要輸出的函數(shù),這些函數(shù)能夠被其它代碼用動態(tài)加載的方法調(diào)用。格式:
?
LIBRARY?????? MyDev(這個字符串要和將要生成的動態(tài)庫的文件名一樣)
?
EXPORTS
?????? XXX_Init
?????? XXX_Deinit
?????? XXX_Open
?????? XXX_Close
?????? XXX_PowerOff
?????? XXX_Power_Down
?????? XXX_IOControl
Sources:這個文件很重要,內(nèi)容也多,最基本的一個文件該有如下內(nèi)容。
?
TARGETNAME= MyDev(指定要生成的動態(tài)庫的名稱)
TARGETTYPE=DYNLINK(指定要生成的是一個動態(tài)庫)
(下面兩項指定需要與哪些動態(tài)庫鏈接,一般要第一項就足夠了)
TARGETLIBS=$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib
SOURCELIBS= $(_COMMONOAKROOT)\lib\$(_CPUINDPATH)\ceddk.lib \
?
DEFFILE=MyDev.def (指定def文件)
?
DLLENTRY=DllEntry(指定動態(tài)庫的入口函數(shù))
?
SOURCES=(請在這寫上你所有源文件的名字,它們將會被編譯)
?
?
?????? 好了,現(xiàn)在萬事俱備,只剩編譯啦。
?
3.4用一個Project文件來編譯出驅(qū)動程序庫文件
如果您在用CE5.0,那用一個Project來構(gòu)造一個驅(qū)動程序?qū)⑹且粋€不錯的選擇。即在新建一個Project時設(shè)置其類型為DLL,其它設(shè)置根據(jù)提示即可。并且可以將注冊表設(shè)置放在Project所在文件夾。
3.5基本調(diào)試方法
一般驅(qū)動程序可以用DEBUG版來調(diào)試,也可以用輸出調(diào)試信息的方法。我們一般用這兩個函數(shù)輸出調(diào)試信息:RETAILMSG和 DEBUGMS,后者只能在DEBUG版中輸出,而前者在RELEASE和DEBUG版中都可以輸出,而且,可以在系統(tǒng)運行時刻根據(jù)Debug Zone選擇讓DEBUGMSG輸出哪些調(diào)試信息。
驅(qū)動程序的調(diào)試一般可以分為以下幾步:
1.看驅(qū)動程序的DllEntry是否被調(diào)用。如果這個函數(shù)被調(diào)用,說明驅(qū)動程序的文件已經(jīng)在CE的image中,而且與注冊表中設(shè)置的文件名相同。
2.看Init 函數(shù)是否被調(diào)用。如果它被調(diào)用,剛說明注冊表設(shè)置正確。如果它沒有被調(diào)用,一般是因為注冊表中的Prefix設(shè)置與Init函數(shù)前面那三個字符不相同?;蛘遜ef文件中沒有定義Init函數(shù)。如果這個函數(shù)能夠被調(diào)用,但驅(qū)動程序還是不能正確加載,請詳細檢查代碼。
本文來自CSDN博客,轉(zhuǎn)載請標明出處:http://blog.chinaunix.net/uid-1818867-id-2835874.html
總結(jié)
以上是生活随笔為你收集整理的WINCE驱动程序快速入门的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 巴菲特为什么被称作股神?他有投资三原则
- 下一篇: make menuconfig 报错解决