WinCE5串口驱动mdd层分析

以前版本的CE支持2MDD(MDD表示分层结构的上层.PDD表示下层,平台相关层.),com_mddcom_mdd2. CE5.0只支持com_mdd2,好处是简化PDD,并且能够实现多个物理串口共用一个dll驱动.使用com_mdd2要做的是在驱动的SOURCES里面把TARGETLIBScom_mdd.lib改成com_mdd2.lib,增加-DUSE_NEW_SERIAL_MODELCDEFINE变量.(以前写过,这个选项表示定义了一个宏#define USE_SERIAL_MODEL)

 

device.exe在加载流驱动时候会调用驱动公开的***_Init函数初始化.位于在mdd.c.

COM_Init()中主要是初始化一个pSerialHead变量,这个是一个PHW_INDEP_INFO的结构.它的定义在在com_mdd2的目录下找到serpriv.h里面.

 

typedef struct __HW_INDEP_INFO {

       CRITICAL_SECTION      TransmitCritSec1;              // @field Protects tx action

       CRITICAL_SECTION      ReceiveCritSec1;         // @field Protects rx action

 

       PHWOBJ                pHWObj;                            // @field Represents PDD object.

    PVOID               pHWHead;                          // @field Device context for PDD.

   

       (省略)

 

    LIST_ENTRY      OpenList;       // @field Head of linked list of OPEN_INFOs   

       CRITICAL_SECTION OpenCS;         // @field Protects Open Linked List + ref counts

 

    PHW_OPEN_INFO   pAccessOwner;   // @field Points to whichever open has acess permissions

} HW_INDEP_INFO, *PHW_INDEP_INFO;

 

 

仔细查看COM_Init(),大致就可以了解驱动工作的机制.明白怎么和下层联系上.

 

COM_Init():

 

1.从注册表获得参数.读取builtin下面的串口注册内容.

2.GetSerialHead(DevIndex);获得下层接口

3.使用下层接口的初始化函数初始化具体硬件.

4.根据下层接口传来的参数,决定是否启动StartDispatchThread线程

 

注册表的位置是由device.exe在加载时候传递过来的,就是COM_Init的参数,也就是builtin下面的那些东西.通过源码可以看到,mdd只用了DeviceArrayIndex, Priority256的内容.

 

GetSerialHead()获得与下层的接口,参数DevIndex是从注册表DeviceArrayIndex中获得的.返回一个pHWObj的指针.pHWObj结构如下:

typedef struct __HWOBJ {

    ULONG           BindFlags; // Flags controlling MDD behaviour.  Se above.

    DWORD                dwIntID;   // Interrupt Identifier used if THREAD_AT_INIT or THREAD_AT_OPEN

    PHW_VTBL             pFuncTbl;

    } HWOBJ, *PHWOBJ;

pHWObj指针包含了一个函数列表的指针pFuncTbl,这指针指向的函数就是下层的函数接口了.如下:

typedef struct __HW_VTBL    {

    PVOID      (*HWInit)(ULONG Identifier, PVOID pMDDContext, PHWOBJ pHWObj);

    BOOL       (*HWPostInit)(PVOID pHead);

    ULONG    (*HWDeinit)(PVOID pHead);

    BOOL       (*HWOpen)(PVOID pHead);

ULONG    (*HWClose)(PVOID pHead);

(省略)

    BOOL    (*HWIoctl)(PVOID pHead, DWORD dwCode,PBYTE pBufIn,DWORD dwLenIn,

                       PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut);

    } HW_VTBL, *PHW_VTBL;

2个结构定义都位于public/common/oak/inc/serhw.h,在这里还可以看到前面定义的宏USE_NEW_SERIAL_MODEL产生的具体的影响.

 

至此,已经明白了mdd层和下层如何接口了.mdd层当然还做了许多其他的机制.比如超时,打开计数器,DCB,如果关心应用的可以自己继续看mdd的代码.接下来分析pdd

========================================================

 

pdd层的工作大概就2,一是控制硬件,二是和上层打好关系. 先说上层接口,前面看到上层用了GetSerialHead()来获得接口.所以pdd里面要实现GetSerialHead()的函数,并且将接口返回给上层.在示例里面使用了一个serpddcm.lib.这个也可以不用的,搞的又多了一个中间层.也许是为了进一步抽象和简化pdd开发,也许提供什么机制(粗略看来,没发现).反正既然提供了就用吧.serpddcm.lib的源代码在serpddcm/cserpdd.cpp里面.关键是实现GetSerialHead函数:

GetSerialObject( DWORD DeviceArrayIndex )

{

    PHWOBJ pSerObj;

    pSerObj=(PHWOBJ)LocalAlloc( LPTR ,sizeof(HWOBJ) );

    if ( !pSerObj )

        return (NULL);

 

    pSerObj->BindFlags = THREAD_IN_PDD;

    pSerObj->dwIntID = DeviceArrayIndex;

    pSerObj->pFuncTbl = (HW_VTBL *) &IoVTbl;

 

    // Now return this structure to the MDD.

    return (pSerObj);

}

关键是提供一个IoVTbl的函数列表:

const HW_VTBL IoVTbl = {

    SerInit,

    SerPostInit,

    SerDeinit,

    SerOpen,

    SerClose,

(省略)

    SerSetDCB,

    SerSetCommTimeouts,

    SerIoctl

    };

逐个实现列表里面的函数.这里关注SerInit(),这个函数在COM_Init中被调用.如同mdd层一样的原理,这里使用了CreateSerialObject()函数来和下层接口.下层实现这个函数并返回一个类指针CSerialPDD给中间层.这个类的定义在public/common/oak/inc/cserpdd.h.

========================================================

终于来到最底层的pdd.先看接口

CSerialPDD * CreateSerialObject(LPTSTR lpActivePath, PVOID pMdd,PHWOBJ pHwObj, DWORD DeviceArrayIndex)

{

    CSerialPDD * pSerialPDD = NULL;

    switch (DeviceArrayIndex) {

      case 0:

        pSerialPDD = new CPdd2440Serial1(lpActivePath,pMdd, pHwObj);

        break;

      case 1:

        pSerialPDD = new CPdd2440Serial2(lpActivePath,pMdd, pHwObj);

        break;

    }

    if (pSerialPDD && !pSerialPDD->Init()) {

        delete pSerialPDD;

        pSerialPDD = NULL;

    }   

    return pSerialPDD;

}

返回一个类指针给上层.c语言返回函数指针表一个道理了.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值