WINCE的第一個驅動程式是什麼呢?有的說是GPIO,有的說是LED,也有的說是SimpleDriver,我個人也是同意後者的。
是以這裡就以SimpleDriver為例講解如何開始自己的第一個驅動程式。
首先,要認識到一個驅動有哪些開發模型。這個相信大家都知道,比如本機驅動,分層驅動,流接口驅動等等,這些都閉着眼睛都知道,可是真正的實作呢?具體什麼情況用什麼模型呢?
關于分層驅動,介紹餓時最多的,尤其是網上和教科書還有一些所謂的論文,真正在産品中用這種方法的又有幾個呢?不是分層驅動不好,隻是要看具體情況,這個主要看個人喜好,個人能力還有就是在PUBLIC目錄下是否有驅動的MDD。
是以,對于一個外設來說,尤其是一片子來說,個人認為單體驅動和流接口驅動用的更實際點,尤其涉及資料的傳輸,個人認為流接口驅動更容易一些。以上僅代表個人觀點,歡迎拍磚。
其次,對于一個驅動我們要認清楚裡面到底有哪些檔案,他們的作用又是幹什麼的呢?下面以SimpleDriver為例,進行第一個簡單流接口驅動的講解。
1.Makefile檔案
這裡的Makefile檔案請不要和其他環境下(GCC,VS2005)的Makefile檔案弄混,它是BSP裡面的Makefile。Windows CE中的Makefile比較特别,它包含對所有項目都通用的配置資訊。
其内容很簡單,隻有一句話:
! INCLUDE $(_MAKEENVROOT)\makefile.def
當build.exe查找dirs和source檔案之後,它就會設定一個内部環境變量。這個環境變量可以被Nmake.exe傳遞給編譯器、連接配接器或其他工具。
2.source檔案
source也是一個文本檔案,它為子目錄中的源代碼設定了不少宏定義。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
TARGETNAME = SimpleDriver
RELEASETYPE = PLATFORM
TARGETTYPE = DYNLINK
TARGETLIBS = $(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib
DEFFILE = $(TARGETNAME).def
DLLENTRY = DllEntry
SOURCES = SimpleDriver.c
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
以上是simpledriver裡面source的内容,具體解釋如下:
TARGETNAME=SimpleDriver; 指定生成最終生成的.exe,.lib,.dll檔案的名稱,這裡是SimpleDriver.dll
RELEASETYPE=PLATFORM;它設定兩種旗标:RELEASEDIR和RELEASELIBDIR,用于指定編譯生成二進制和庫檔案存放的目錄。預設情況下,為目标生成的二進制和庫檔案存放在
;目錄%_PROJECTROOT%\oak下,這裡存放在D:\WINCE600\PLATFORM\Mini2440\target\ARMV4I\retail目錄下。
TARGETTYPE=DYNLINK;這個宏定義指定建構檔案的最終類型,可以把TARGETTYPE類型設定為一下四種類型中的任意一種。
1)MANAGED_EXE;
2) MANAGED_DLL;
3) MANAGED_WINEXE;
4) MANAGED_MODULE;
這裡設定的最終類型為dll。
TARGETLIBS=$(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib ;它指定了額外的庫檔案(.lib)和目标檔案(.Obj)連結為目标可執行檔案(.exe或.dll).
;這裡将 coredll.lib 連結生成最終的目标檔案SimpleDriver.dll
DEFFILE=$(TARGETNAME).def;它指定子產品定義檔案(.def)的名稱,這裡指定了子產品定義檔案的名稱為SimpleDriver.def
DLLENTRY=DllEntry;它為一個DLL檔案指定DLL的入口函數,此時TARGETTYPE被設定為DYNLINK。如果 DLLENTRY對應的值沒有被設定時,那麼_DllMainCRTStartUp是DLL的C程式
;運作入口點。這裡 DLLENTRY的入口函數被指定為DllEntry,因而,上面的 TARGETTYPE被設定成DYNLINK;
SOURCES=SimpleDriver.c;它包含編譯過程的檔案清單,這些清單中包含彙編檔案和源檔案,這些檔案的類型有.cxx,.cpp,.c,.asm,.s,.src,.rc,.obj,.ire,.res,.odl,.tlb,.i,.cs,.resx等。
;這些檔案編譯之後可能是靜态庫檔案(.lib),也有可能是動态庫檔案(.dll)。這裡編譯過程中需要用到的源檔案有SimpleDriver.c,
;編譯之後的生成SimpleDriver.dll的動态連結庫檔案。
3.SimpleDriver.def檔案
.def檔案定義了DLL的導出函數清單。
這裡包括的内容如下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
LIBRARY SimpleDriver
EXPORTS
SPL_Init
SPL_Deinit
SPL_Open
SPL_Close
SPL_Read
SPL_Write
SPL_Seek
SPL_IOControl
SPL_PowerDown
SPL_PowerUp
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
主要是針對目前流接口函數,将相應的函數導出。
4. SimpleDriver.h
這個而就不用多介紹了吧,主要是一些頭檔案的聲明,定義等等
5.SimpleDriver.c
下面給出基本的代碼,有些函數給出了空定義,友善以後實作,同時友善了解。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
#include < windows.h >
#include < types.h >
static BYTE g_Tmp = 0 ;
static DWORD g_OpenCount = 0 ;
BOOL WINAPI DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved)
{
switch ( dwReason )
{
case DLL_PROCESS_ATTACH:
RETAILMSG( 1 , (TEXT( " SPL: DLL_PROCESS_ATTACH.\r\n " )));
DisableThreadLibraryCalls((HMODULE) hInstDll);
break ;
case DLL_PROCESS_DETACH:
RETAILMSG( 1 , (TEXT( " SPL: DLL_PROCESS_DETACH.\r\n " )));
break ;
}
return (TRUE);
}
DWORD SPL_Init(DWORD dwContext)
{
RETAILMSG( 1 , (TEXT( " ::: SPL_Init.\r\n " )));
g_Tmp = 0 ;
g_OpenCount = 0 ;
return 1 ;
}
BOOL SPL_Deinit(DWORD dwContext)
{
RETAILMSG( 1 , (TEXT( " ::: SPL_Deinit.\r\n " )));
g_Tmp = 0 ;
g_OpenCount = 0 ;
return TRUE;
}
DWORD SPL_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode)
{
RETAILMSG( 1 , (TEXT( " ::: SPL_Open.\r\n " )));
// 不允許多個應用程式打開本驅動
if (g_OpenCount != 0 )
{
RETAILMSG( 1 , (TEXT( " SPL Open failed.\r\n " )));
return 0 ;
}
g_OpenCount ++ ;
return g_OpenCount;
}
BOOL SPL_Close(DWORD hOpenContext)
{
RETAILMSG( 1 , (TEXT( " ::: SPL_Close.\r\n " )));
if (g_OpenCount != 0 )
g_OpenCount -- ;
return TRUE;
}
BOOL SPL_IOControl(DWORD hOpenContext,
DWORD dwCode,
PBYTE pBufIn,
DWORD dwLenIn,
PBYTE pBufOut,
DWORD dwLenOut,
PDWORD pdwActualOut)
{
RETAILMSG( 1 , (TEXT( " ::: SPL_IOControl.\r\n " )));
return TRUE;
}
DWORD SPL_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count)
{
uchar * pReadBuffer;
RETAILMSG( 1 , (TEXT( " ::: SPL_Read.\r\n " )));
if ((pBuffer == NULL) || (Count <= 0 ))
{
RETAILMSG( 1 , (TEXT( " ::: SPL_Read() parameter is error.\r\n " )));
return 0 ;
}
// 映射位址空間
pReadBuffer = MapPtrToProcess(pBuffer, GetCallerProcess());
* pReadBuffer = g_Tmp;
return 1 ;
}
DWORD SPL_Write(DWORD hOpenContext, LPCVOID pBuffer, DWORD Count)
{
uchar * pWriteBuffer;
RETAILMSG( 1 , (TEXT( " ::: SPL_Write.\r\n " )));
if ((pBuffer == NULL) || (Count <= 0 ))
{
RETAILMSG( 1 , (TEXT( " ::: SPL_Write() parameter is error.\r\n " )));
return 0 ;
}
// 擷取應用程式位址空間資料指針
pWriteBuffer = MapPtrToProcess((LPVOID)pBuffer, GetCallerProcess());
g_Tmp = * pWriteBuffer;
return 1 ;
}
DWORD SPL_Seek(DWORD hOpenContext, long Amount, DWORD Type)
{
RETAILMSG( 1 , (TEXT( " ::: SPL_Seek.\r\n " )));
return 0 ;
}
void SPL_PowerUp( void )
{
RETAILMSG( 1 , (TEXT( " ::: SPL_PowerUp.\r\n " )));
}
void SPL_PowerDown( void )
{
RETAILMSG( 1 , (TEXT( " ::: SPL_PowerDown.\r\n " )));
}
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLlR2bjlHcvN2LcNXZnFWbp9CXt92YuM3ZvxmYuNmLu9Wbt92Yvw1LcpDc0RHaiojIsJye.gif)
OK!
到這裡點選編譯就可以了。這個新驅動程式的動态連結庫将會被編譯進核心。當調用CreateFile()函數(第一個參數是SPL)時就搜尋系統資料庫,裝置管理器可以找到這個驅動,驅動的入口點可以用到其他程式。
上文介紹了WINCE一個簡單驅動的編寫,這節我們接着介紹一些配置檔案的編寫。涉及到的檔案有.platform.bib,platform.reg,dirs,source,SimpleDriver.def,其中後面兩個檔案在前文已有介紹,這裡就以前兩個配置檔案為主。
首先,從系統資料庫說起,先簡單的介紹一下系統資料庫:Makeimg.exe使用.reg檔案來為CE鏡像建立系統資料庫并添加預設的鍵值。也就是說,在.reg中寫入的系統資料庫的鍵值會被預設地放入CE鏡像的初始化系統資料庫中。其中Platform.reg定義了目标裝置硬體的系統資料庫設定,Project.reg定義基于Windows CE項目工程的系統資料庫設定。系統資料庫鍵值的類型如下:
1. REG_SZ表示一個字元串類型,如reg_sz:”my string”。
2. REG_DWORD表示一個雙位元組類型,如dword:12345678(十六進制數)。
3. REG_MULTI_SZ表示多字元串類型,如multi_sz:“my string”,“my string”。
4. REG_BINARY 二進制類型。
在實際應用中可以使用IF/ENDIF關鍵字來引入一個系統資料庫設定塊,通過設定一個環境變量或一個特殊的值來達到這個目的。
為了包含一個系統資料庫設定塊,當一個環境變量沒有被設定或者沒有等于一個特定的值的時候,引入的系統資料庫設定塊的行尾應使用一個空格和“!”。
這裡比較好找,一目了然,在目錄D:\WINCE600\PLATFORM\SMDKXXXX\Files\platform.reg下添加:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SPL]
" Prefix " = " SPL "
" Dll " = " SimplDriver.Dll "
" FriendlyName " = " Simple Driver "
" Order " = dword: 0
" Index " = dword: 0
這樣在WINCE啟動時,就自動加載該驅動了。
接着我們介紹platform.bib檔案,關于bib檔案,這裡做一個簡要的介紹:二進制鏡像檔案建構檔案(.bib)定義了哪個子產品或者檔案被包含到運作時的CE鏡像中。在編譯期間,makeimg.exe使若幹個.bib檔案合并成ce.bib檔案,然後,romimage.exe使用ce.bib決定哪些檔案應該被打包進運作時CE鏡像中,它同時還使用ce.bib來決定如何加載子產品和檔案到CE鏡像(下載下傳到目标裝置的CE鏡像)所在的記憶體中。
按照功能劃分,.bib檔案可以分為如下幾種類型。
(1) Platform.bib。Platform.bib位于目錄D:\WINCE600\PLATFORM\SMDK6410\Files,它包含硬體平台相關的子產品和檔案,如目标裝置的驅動檔案。這些子產品和檔案是CE鏡像的入口,比如.exe檔案,如波形音頻檔案(.wav)等。
(2) Project.bib。Project.bib weiyumulu D:\WINCE600\PUBLIC\CEBASE\OAK\FILES,如果我們建立一個工程項目(OSDesign1)則這個工程位于目錄:D:\WINCE600\OSDesigns\OSDesign1\OSDesign1\Wince600\SMDK6410_ARMV4I\OAK\files下。Project.bib檔案定義與建立CE鏡像的工程相關的子產品,如果在OSDesign1中建立了一個自己的子產品或者應用程式,那麼就要把它們添加到Project.bib檔案中的MODILES部分。
(3) Common.bib。Common.bib位于目錄D:\WINCE600\PUBLIC\COMMON\OAK\FILES下,它定義了CE鏡像檔案包含的通用顯示驅動和核心系統子產品。
(4) Config.bib。Config.bib位于目錄D:\WINCE600\PLATFORM\SMDK6410\Files下,它定義了ROM和RAM的配置資訊。它同樣包含了CE鏡像檔案的MEMORY和CONFIG部分。Config.bib的MEMORY部分定義了運作時CE鏡像記憶體配置設定表,指定了名稱、位址、大小和MEMORY區域的類型。
.bib檔案可以被分為四個部分,分别是MEMORY、CONFIG、MODULES和FILES。下面将分别說明這四個部分各自的含義。
(1) MEMORY。定義可用的實體記憶體,包括起始位址、大小和記憶體類型。
(2) CONFIG。定義romimage.exe輸出的配置選項。預設情況下,這個區域是在config.bib檔案中。不過,也并不是.bib檔案中必須要包含CONFIG部分。
(3) FILES。指定放在CE鏡像中的檔案清單。
(4) 指定放在CE鏡像總的子產品清單,包括EXE和DLL檔案,與FILES的差別是放在MODULES中的檔案通常是代碼檔案,并且建構系統時不會壓縮這些檔案。
這裡我們主要介紹Modules部分。
Modules部分指定了哪些基于Windows CE的子產品包含到CE鏡像中,以及如何給加載到config.bib檔案中的MEMORY部分建立記憶體表。這個部分可以包含200個子產品,這些子產品有源代碼和資料兩個部分組成。
MODULES的文法格式如下:
; Name Path Memory block Section override Memory Type
; --------- -------- ---------------- ----------- -------------
各參數之間用空格分隔。
NAME:這個參數指定了MODULES子產品的名稱。通常情況下,它就像被路徑引用的檔案名稱一樣。
PATH:指定要打包進CE鏡像的MODULES子產品的完整路徑。
MEMORY BLOCK:這個參數指定romimage.exe加載目标子產品到記憶體區域的ramimage部分。這個記憶體位于config.bib檔案中memory部分指定的某一段記憶體。
SECTION OVERRIDE:這個參數的設定時可選的,它可以為modules、files或者空。如果設定了這個參數,那麼建構系統就會根據它來決定這一項是modules還是files。
TYPE:這個參數指定檔案的類型,主要有以下幾種類型,在實際的使用中可以選用其中的一種或者多種組合。
1. S:定義一個系統檔案
2. H:定義一個隐藏檔案
3. R:壓縮資源,隻應用于MODULES部分
4. C:如果應用于一個子產品,則壓縮全部内容
5. D:運作時不允許調試。
6. N:标記一個子產品為不可信任的,隻應用MODULES部分。
7. K:指定romimage.exe必須修正子產品到一個核心位址。在這個過程中,romimage.exe配置設定一個固定的虛拟位址給DLL,設定了此标志的子產品隻可以被loadKernelLibrary()函數加載。
8. P:指定romimage.exe禁止在頭檔案中檢查指定的CPU類型。這個旗标隻用于資源dll,可以在一種CPU傷編譯,在不同CPU上使用。
9. M:表示對此頁禁止按序調頁。
10. U:表示不壓縮此檔案。
在目錄D:\WINCE600\PLATFORM\SMDKXXXX\Files\platform.bib檔案中有如下定義:
; Name Path Memory Type
; -------------- ---------------------------------- -----------
;-------------
;--------------Simple Driver ([email protected]) ------------------------------------------------------
simpledriver.dll $(_FLATRELEASEDIR)\simpledriver.dll NK SHK
; @CESYSGEN ENDIF CE_MODULES_DEVICE
;---------------------------------------------------------------------------------------
其中,_$(_FLATRELEASEDIR)\ 指的是D:\WINCE600\OSDesigns\SMDKXXXX\SMDKXXXX\RelDir
結合上面的說明,我們可以知道,上面的語句的意思是将編譯生成的simpledriver.dll子產品加載到CE運作時鏡像NK中。它的檔案屬性是系統檔案,隐藏檔案和核心子產品,由romimage.exe配置設定一個固定的虛拟位址給simpledriver.DLL。
接着在D:\WINCE600\PLATFORM\SMDKXXXX\Src\Drivers目錄下找到dirs檔案,在裡面添加:SimpleDriver\
最後點選build或者在dos環境下build -c