WinCE5.0 SMDK2410 BSP的串口驱动由两个文件组成.一个在BSP目录下的/SRC/DRIVERS/SERIAL/ser_smdk2410.cpp,另一个在/WINCE500/PUBLIC/COMMON/OAK/CSP/ARM/SAMSUNG/S3C2410X/SERIAL/pdds3c2410_ser.cpp,为了实现BSP的完整性,并方便修改,在移植到GEC2410的时候,将该目录下的所有文件放到BSP目录下,特别的对于串口驱动,我把pdds3c2410_ser.cpp同样也放到BSP目录/SRC/DRIVERS/SERIAL/下,只需修改下source文件,将pdds3c2410_ser.cpp加入编译文件即可.
SOURCES= ser_smdk2410.cpp /
pdds3c2410_ser.cpp
其实在WinCE6.0的DEVICEEMULATOR BSP下就是采用这样的目录文件结构.
接下来我们就进入正题.
一. 驱动结构
串口驱动是个流接口驱动,其中流接口函数,比如COM_Init,COM_Open,COM_Read,COM_Write等实现在MDD库com_mdd2.lib中(/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/SERIAL/COM_MDD2/mdd.c),同样还有一个PDD的库serpddcm.lib(/WINCE500/PUBLIC/COMMON/OAK/DRIVERS/SERIAL/SERPDDCM/cserpdd.cpp),这个实际上相当于PDD COMMON层,也是平台无关的.
BSP目录下的SERIAL驱动实际上是真正的PDD层,与实际硬件平台相关的代码.
这里先简单分析下MDD,PDD COMMON和PDD的结构,三者是如何联系的.
MDD(mdd.c)实现了COM_**的流接口函数,中间调用了PDD COMMON层的函数,而PDD COMMON又调用PDD的函数.其中前两者的联系在COM_Init中的:
// Initialize hardware dependent data. pSerialHead->pHWObj = GetSerialObject( DevIndex ); if ( !pSerialHead->pHWObj ) { DEBUGMSG(ZONE_ERROR | ZONE_INIT, (TEXT("Error in GetSerialObject, COM_Init failed/n/r"))); LocalFree(pSerialHead); return(NULL); } DEBUGMSG (ZONE_INIT, (TEXT("About to call HWInit(%s,0x%X)/r/n"), Identifier, pSerialHead)); pHWHead = pSerialHead->pHWObj->pFuncTbl->HWInit(Identifier, pSerialHead, pSerialHead->pHWObj); pSerialHead->pHWHead = pHWHead;
pSerialHead是HW_INDEP_INFO结构的指针,相当于串口设备的句柄,定义在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. HANDLE hSerialEvent; // @field Serial event, both rx and tx HANDLE hReadEvent; // @field Serial event, both rx and tx HANDLE hKillDispatchThread; // @field Synchonize thread end HANDLE hTransmitEvent; // @field transmit event, both rx and tx HANDLE pDispatchThread;// @field ReceiveThread ULONG Priority256; // @field CeThreadPriority of Dispatch Thread. ULONG DroppedBytesMDD;// @field Record of bytes dropped by MDD. ULONG DroppedBytesPDD;// @field Record of bytes dropped by PDD. ULONG RxBytes; // @field Record of total bytes received. ULONG TxBytes; // @field Record of total bytes transmitted. ULONG TxBytesPending; // @field Record of total bytes awaiting transmit. ULONG TxBytesSent; // @field Record of bytes sent in one transmission DCB DCB; // @field DCB (see Win32 Documentation. COMMTIMEOUTS CommTimeouts; // @field Time control field. DWORD OpenCnt; // @field Protects use of this port DWORD KillRxThread:1; // @field Flag to terminate SerialDispatch thread. DWORD XFlow:1; // @field True if Xon/Xoff flow ctrl. DWORD StopXmit:1; // @field Stop transmission flag. DWORD SentXoff:1; // @field True if XOFF sent. DWORD DtrFlow:1; // @field True if currently DTRFlowed DWORD RtsFlow:1; // @field True if currently RTSFlowed DWORD fAbortRead:1; // @field Used for PURGE DWORD fAbortTransmit:1;// @field Used for PURGE DWORD Reserved:24; // @field remaining bits. ULONG fEventMask; // @field Sum of event mask for all opens RX_BUFFER_INFO RxBufferInfo; // @field rx buffer info. TX_BUFFER_INFO TxBufferInfo; // @field tx buffer info. 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;
pHWObj是HWOBJ结构的指针.HWOBJ是HW_INDEP_INFO成员,被称作硬件相关数据.在serhw.h中定义如下:
typedef struct __HWOBJ { ULONG BindFlags; DWORD dwIntID; PHW_VTBL pFuncTbl; } HWOBJ, *PHWOBJ;
其中pFuncTbl指向的函数指针数组HW_VTBL就是PDD COMMON层(cserpdd.cpp)中实现的各个函数,如SerInit,SerOpen等,如下代码所示:
const HW_VTBL IoVTbl = { SerInit, SerPostInit, SerDeinit, SerOpen, SerClose, SerGetInterruptType, SerRxIntr, SerTxIntrEx, SerModemIntr, SerLineIntr, SerGetRxBufferSize, SerPowerOff, SerPowerOn, SerClearDTR, SerSetDTR, SerClearRTS, SerSetRTS, SerEnableIR, SerDisableIR, SerClearBreak, SerSetBreak, SerXmitComChar, SerGetStatus, SerReset, SerGetModemStatus, SerGetCommProperties, SerPurgeComm, SerSetDCB, SerSetCommTimeouts, SerIoctl };
COM_Init通过调用GetSerialObject来获得相应PDD的HWOBJ结构,GetSerialObject是MDD连接PDD的关键函数,而DeviceArrayIndex对应的就是串口的端口序号,一个端口序号对应一个UART端口,也对应一个串口驱动程序PDD层运行时代码,这个值可以从注册表中DeviceArrayIndex项获得.
COM_Init函数调用GetSerialObject将DeviceArrayIndex传递到HWOBJ的dwIntID成员再传给CreateSerialObject函数.这样COM_Init通过适用不同的参数调用GetSerialObject就可以获得同一平台上同时存在不同串口的PDD对象,可以使多个PDD对象共享一个MDD.
以下是GetSerialObject的代码,位于cserpdd.cpp,获得了PDD COMMON中的HW_VTBL结构的函数指针,同时将DeviceArrayIndex传递给dwIntID.
extern "C" PHWOBJ GetSerialObject( DWORD DeviceArrayIndex ) { PHWOBJ pSerObj; // Unlike many other serial samples, we do not have a statically allocated // array of HWObjs. Instead, we allocate a new HWObj for each instance // of the driver. The MDD will always call GetSerialObj/HWInit/HWDeinit in // that order, so we can do the alloc here and do any subsequent free in // HWDeInit. // Allocate space for the HWOBJ. pSerObj=(PHWOBJ)LocalAlloc( LPTR ,sizeof(HWOBJ) ); if ( !pSerObj ) return (NULL); // Fill in the HWObj structure that we just allocated. pSerObj->BindFlags = THREAD_IN_PDD; // PDD create thread when device is first attached. pSerObj->dwIntID = DeviceArrayIndex; // Only it is useful when set set THREAD_AT_MDD. We use this to transfer DeviceArrayIndex pSerObj->pFuncTbl = (HW_VTBL *) &IoVTbl; // Return pointer to appropriate functions // Now return this structure to the MDD. return (pSerObj); }
COM_Init中的:
pHWHead = pSerialHead->pHWObj->pFuncTbl->HWInit(Identifier, pSerialHead, pSerialHead->pHWObj);
pSerialHead->pHWHead = pHWHead;
HWInit实际上调用的就是cserpdd.cpp中的SerInit,它将调用CreateSerialObject函数来获得串口驱动的PDD层.实际上就是返回PDD中需要实现的串口类的指针.
PVOID SerInit( ULONG Identifier, // @parm Device identifier. PVOID pMddHead, // @parm First argument to mdd callbacks. PHWOBJ pHWObj // @parm Pointer to our own HW OBJ for this device ) { DEBUGMSG (ZONE_CLOSE,(TEXT("+SerInit, 0x%X/r/n"), Identifier)); CSerialPDD * pSerialPDD = NULL; if (pHWObj) { DWORD dwIndex= pHWObj->dwIntID; pHWObj->dwIntID = 0; pSerialPDD = CreateSerialObject((LPTSTR)Identifier,pMddHead, pHWObj,dwIndex); } if (pSerialPDD==NULL) { ASSERT(FALSE); LocalFree(pHWObj); } DEBUGMSG (ZONE_CLOSE,(TEXT("-SerInit, 0x%X/r/n"), pSerialPDD)); return pSerialPDD; }
CreateSerialObject定义在PDD层,这里是Ser_smdk2410.cpp中的代码:
CSerialPDD * CreateSerialObject(LPTSTR lpActivePath, PVOID pMdd,PHWOBJ pHwObj, DWORD DeviceArrayIndex) { CSerialPDD * pSerialPDD = NULL; switch (DeviceArrayIndex) { case 0: pSerialPDD = new CPdd2410Serial1(lpActivePath,pMdd, pHwObj); break; case 1: pSerialPDD = new CPdd2410Serial2(lpActivePath,pMdd, pHwObj); break; } if (pSerialPDD && !pSerialPDD->Init()) { delete pSerialPDD; pSerialPDD = NULL; } return pSerialPDD; }
这里的CPdd2410Serial1和CPdd2410Serial2就是PDD需要实现的两个类,分别对应UART0和UART1.下一篇我们就来详细看看串口驱动的PDD层.
关于MDD层更详细的内容,可以参考这篇文章:
http://nasiry.cnblogs.com/archive/2005/04/12/136175.html