天天看點

WINCE入門的第一個驅動程式

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也是一個文本檔案,它為子目錄中的源代碼設定了不少宏定義。 

WINCE入門的第一個驅動程式

TARGETNAME = SimpleDriver

RELEASETYPE = PLATFORM

TARGETTYPE = DYNLINK

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

DEFFILE = $(TARGETNAME).def

DLLENTRY = DllEntry

SOURCES = SimpleDriver.c

WINCE入門的第一個驅動程式

以上是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的導出函數清單。

 這裡包括的内容如下:

WINCE入門的第一個驅動程式

LIBRARY SimpleDriver

EXPORTS 

    SPL_Init

    SPL_Deinit

    SPL_Open

    SPL_Close

    SPL_Read

    SPL_Write

    SPL_Seek

    SPL_IOControl

    SPL_PowerDown

    SPL_PowerUp

WINCE入門的第一個驅動程式

主要是針對目前流接口函數,将相應的函數導出。

4. SimpleDriver.h

這個而就不用多介紹了吧,主要是一些頭檔案的聲明,定義等等 

 5.SimpleDriver.c

下面給出基本的代碼,有些函數給出了空定義,友善以後實作,同時友善了解。

WINCE入門的第一個驅動程式

#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 " )));             

}

WINCE入門的第一個驅動程式

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

繼續閱讀