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也是一个文本文件,它为子目录中的源代码设置了不少宏定义。

代码

 
TARGETNAME
= SimpleDriver
RELEASETYPE
= PLATFORM
TARGETTYPE
= DYNLINK

TARGETLIBS
= $(_COMMONSDKROOT)\lib\$(_CPUINDPATH)\coredll.lib    
   
DEFFILE
= $(TARGETNAME).def
DLLENTRY
= DllEntry

SOURCES
= SimpleDriver.c

以上是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的导出函数列表。
 这里包括的内容如下:

LIBRARY SimpleDriver

EXPORTS 
    SPL_Init
    SPL_Deinit
    SPL_Open
    SPL_Close
    SPL_Read
    SPL_Write
    SPL_Seek
    SPL_IOControl
    SPL_PowerDown
    SPL_PowerUp

主要是针对当前流接口函数,将相应的函数导出。

4. SimpleDriver.h

这个而就不用多介绍了吧,主要是一些头文件的声明,定义等等

 5.SimpleDriver.c

下面给出基本的代码,有些函数给出了空定义,方便以后实现,同时方便理解。

代码

#include 
< windows.h >
#include 
< types.h >


static  BYTE g_Tmp  =   0 ;                         /*  暂存数据变量  */
static  DWORD g_OpenCount  =   0 ;                 /*  驱动打开计数器  */  

/* ******************************************************************************************
函数名称: DllEntry
描    述: 驱动程序动态库入口
输入参数:     
输出参数:
返    回: 
******************************************************************************************
*/
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);
}


/* ******************************************************************************************
函数名称: SPL_Init
描    述: 驱动程序初始化函数
输入参数: DWORD dwContext: 设备管理器传递给本驱动的参数, 通常为流接口驱动在注册表内的位置     
输出参数: 无
返    回: 驱动程序句柄
******************************************************************************************
*/
DWORD SPL_Init(DWORD dwContext)
{
    RETAILMSG(
1 , (TEXT( " ::: SPL_Init.\r\n " )));         /*  提示驱动加载  */

    g_Tmp 
=   0 ;                                         /*  初始化全局变量的值  */
    g_OpenCount 
=   0

    
return   1 ;                                         /*  返回一个不为零的数表示成功  */
}


/* ******************************************************************************************
函数名称: SPL_Deinit
描    述: 驱动程序卸载函数
输入参数: DWORD dwContext: 驱动程序句柄
输出参数: 无
返    回: FALSE: 失败    TRUE: 成功
******************************************************************************************
*/
BOOL SPL_Deinit(DWORD dwContext)
{
    RETAILMSG(
1 , (TEXT( " ::: SPL_Deinit.\r\n " )));     /*  提示驱动卸载  */
    
    g_Tmp 
=   0 ;                                         /*  恢复全局变量的值  */
    g_OpenCount 
=   0

    
return  TRUE;
}


/* ******************************************************************************************
函数名称: SPL_Open
描    述: 打开驱动程序
输入参数: DWORD hDeviceContext: 设备驱动程序引用实例句柄
          DWORD AccessCode    : 访问请求代码,是读和写的组合
          DWORD ShareMode      : 共享模式  
输出参数:
返    回: 驱动程序引用事例句柄
******************************************************************************************
*/
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
++ ;                                      /*  驱动打开计数器加1  */

    
return  g_OpenCount;                                  /*  必须返回一个不为空的句柄  */
}


/* ******************************************************************************************
函数名称: SPL_Close
描    述: 驱动程序关闭函数
输入参数: DWORD hOpenContext:驱动程序引用事例句柄
输出参数: 无
返    回: FALSE: 失败    TRUE: 成功 
******************************************************************************************
*/
BOOL SPL_Close(DWORD hOpenContext)
{
    RETAILMSG(
1 , (TEXT( " ::: SPL_Close.\r\n " )));              /*  提示驱动关闭  */
    
    
if  (g_OpenCount  !=   0
        g_OpenCount
-- ;                                      /*  驱动打开计数减1  */
        
    
return  TRUE;
}


/* ******************************************************************************************
函数名称: SPL_IOControl
描    述: 驱动程序 I/O 请求
输入参数: 
输出参数:
返    回: TRUE: 成功   FALSE: 失败
******************************************************************************************
*/
BOOL SPL_IOControl(DWORD hOpenContext,
                   DWORD dwCode,
                   PBYTE pBufIn,
                   DWORD dwLenIn,
                   PBYTE pBufOut,
                   DWORD dwLenOut,
                   PDWORD pdwActualOut)
{
    RETAILMSG(
1 , (TEXT( " ::: SPL_IOControl.\r\n " )));          /*  提示I/O请求函数执行  */
    
    
return  TRUE;
}


/* ******************************************************************************************
函数名称: SPL_Read
描    述: 从本驱动读取数据
输入参数: DWORD hOpenContext: 驱动程序引用事例句柄
          DWORD Count        : 要读的字节数
输出参数: LPVOID pBuffer    : 接收缓冲区
返    回: 实际读到的字节数
******************************************************************************************
*/
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 ;                                                 /*  返回读取的字节数  */
}


/* ******************************************************************************************
函数名称: SPL_Write
描    述: 向本驱动写入数据
输入参数: DWORD hOpenContext: 驱动程序引用事例句柄
          LPVOID pBuffer    : 发送缓冲区
          DWORD Count        : 要写入的字节数
输出参数: 无
返    回: 实际写入的字节数
******************************************************************************************
*/
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 ;
}


/* ******************************************************************************************
函数名称: SPL_Seek
描    述: 对设备的数据指针进行操作,本驱动不支持该函数
输入参数: 
输出参数:
返    回:
******************************************************************************************
*/
DWORD SPL_Seek(DWORD hOpenContext, 
long  Amount, DWORD Type)
{
    RETAILMSG(
1 , (TEXT( " ::: SPL_Seek.\r\n " )));                   /*  提示执行本函数  */

    
return   0 ;
}


/* ******************************************************************************************
函数名称: SPL_PowerUp
描    述: 电源上电驱动处理函数
输入参数: 
输出参数:
返    回: 无
******************************************************************************************
*/
void  SPL_PowerUp( void )
{
    RETAILMSG(
1 , (TEXT( " ::: SPL_PowerUp.\r\n " )));              /*  提示执行本函数  */
}


/* ******************************************************************************************
函数名称: SPL_PowerDown
描    述: 电源下电驱动处理函数
输入参数: 
输出参数:
返    回: 无
******************************************************************************************
*/
void  SPL_PowerDown( void )
{
    RETAILMSG(
1 , (TEXT( " ::: SPL_PowerDown.\r\n " )));              /*  提示执行本函数  */
}

OK!

到这里点击编译就可以了。这个新驱动程序的动态链接库将会被编译进内核。当调用CreateFile()函数(第一个参数是SPL)时就搜索注册表,设备管理器可以找到这个驱动,驱动的入口点可以用到其他程序。

 

你可能感兴趣的:(WinCE)