wince串口之MDD分析
(作者:wogoyixikexie@gliet)
以前曾经看过一阵子串口,只了解一点皮毛,现在在5.0 2440BSP上增加串口遇到阻碍,现在不得不仔细研究一下。MDD是驱动的最上层,应用程序等都是和它打交道的,所以从它开始看才合理。
首先看Com_Init这个函数吧
- HANDLE
- COM_Init(
- ULONG Identifier
- )
- {
- PVOID pHWHead = NULL;
- PHW_INDEP_INFO pSerialHead = NULL;
- ULONG HWBufferSize;
- DWORD DevIndex;
- HKEY hKey;
- ULONG kreserved = 0, kvaluetype;
- ULONG datasize = sizeof(ULONG);
-
- DEBUGMSG (ZONE_INIT | ZONE_FUNCTION, (TEXT("+COM_Init/r/n")));
-
- pSerialHead = (PHW_INDEP_INFO)LocalAlloc(LPTR, sizeof(HW_INDEP_INFO));
-
- if ( !pSerialHead ) {
- DEBUGMSG(ZONE_INIT | ZONE_ERROR,
- (TEXT("Error allocating memory for pSerialHead, COM_Init failed/n/r")));
- return(NULL);
- }
-
- InitializeListHead( &pSerialHead->OpenList );
- InitializeCriticalSection(&(pSerialHead->OpenCS));
- pSerialHead->pAccessOwner = NULL;
- pSerialHead->fEventMask = 0;
-
- pSerialHead->CommTimeouts.ReadIntervalTimeout = READ_TIMEOUT;
- pSerialHead->CommTimeouts.ReadTotalTimeoutMultiplier =
- READ_TIMEOUT_MULTIPLIER;
- pSerialHead->CommTimeouts.ReadTotalTimeoutConstant =
- READ_TIMEOUT_CONSTANT;
- pSerialHead->CommTimeouts.WriteTotalTimeoutMultiplier= 0;
- pSerialHead->CommTimeouts.WriteTotalTimeoutConstant = 0;
-
- pSerialHead->hSerialEvent = CreateEvent(0,FALSE,FALSE,NULL);
- pSerialHead->hKillDispatchThread = CreateEvent(0, FALSE, FALSE, NULL);
- pSerialHead->hTransmitEvent = CreateEvent(0, FALSE, FALSE, NULL);
- pSerialHead->hReadEvent = CreateEvent(0, FALSE, FALSE, NULL);
- if ( !pSerialHead->hSerialEvent || !pSerialHead->hKillDispatchThread ||
- !pSerialHead->hTransmitEvent || !pSerialHead->hReadEvent ) {
- DEBUGMSG(ZONE_ERROR | ZONE_INIT,
- (TEXT("Error creating event, COM_Init failed/n/r")));
- LocalFree(pSerialHead);
- return(NULL);
- }
-
- InitializeCriticalSection(&(pSerialHead->ReceiveCritSec1));
- InitializeCriticalSection(&(pSerialHead->TransmitCritSec1));
-
- DEBUGMSG (ZONE_INIT,(TEXT("Try to open %s/r/n"), (LPCTSTR)Identifier));
- hKey = OpenDeviceKey((LPCTSTR)Identifier);
- if ( !hKey ) {
- DEBUGMSG (ZONE_INIT | ZONE_ERROR,
- (TEXT("Failed to open devkeypath, COM_Init failed/r/n")));
- LocalFree(pSerialHead);
- return(NULL);
- }
- datasize = sizeof(DWORD);
- //注册表中的DeviceArrayIndex被读出
- if ( RegQueryValueEx(hKey, L"DeviceArrayIndex", NULL, &kvaluetype,
- (LPBYTE)&DevIndex, &datasize) ) {
- DEBUGMSG (ZONE_INIT | ZONE_ERROR,
- (TEXT("Failed to get DeviceArrayIndex value, COM_Init failed/r/n")));
- RegCloseKey (hKey);
- LocalFree(pSerialHead);
- return(NULL);
- }
- datasize = sizeof(DWORD);
- if ( RegQueryValueEx(hKey, L"Priority256", NULL, &kvaluetype,
- (LPBYTE)&pSerialHead->Priority256, &datasize) ) {
- pSerialHead->Priority256 = DEFAULT_CE_THREAD_PRIORITY;
- DEBUGMSG (ZONE_INIT | ZONE_WARN,
- (TEXT("Failed to get Priority256 value, defaulting to %d/r/n"), pSerialHead->Priority256));
- }
- RegCloseKey (hKey);
- DEBUGMSG (ZONE_INIT,
- (TEXT("DevIndex %X/r/n"), DevIndex));
-
- 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));
- //其实下面执行的是SerInit函数(由一个表完成了初始化的)
- pHWHead = pSerialHead->pHWObj->pFuncTbl->HWInit(Identifier, pSerialHead, pSerialHead->pHWObj);
- pSerialHead->pHWHead = pHWHead;
-
- if ( !pHWHead ) {
- DEBUGMSG (ZONE_INIT | ZONE_ERROR,
- (TEXT("Hardware doesn't init correctly, COM_Init failed/r/n")));
- COM_Deinit(pSerialHead);
- return(NULL);
- }
- DEBUGMSG (ZONE_INIT,
- (TEXT("Back from hardware init/r/n")));
-
- HWBufferSize = 2 * pSerialHead->pHWObj->pFuncTbl->HWGetRxBufferSize(pHWHead);
-
- pSerialHead->RxBufferInfo.Length =
- HWBufferSize > RX_BUFFER_SIZE ? HWBufferSize:RX_BUFFER_SIZE;
- pSerialHead->RxBufferInfo.RxCharBuffer =
- LocalAlloc(LPTR, pSerialHead->RxBufferInfo.Length);
- if ( !pSerialHead->RxBufferInfo.RxCharBuffer ) {
- DEBUGMSG(ZONE_INIT|ZONE_ERROR,
- (TEXT("Error allocating receive buffer, COM_Init failed/n/r")));
- COM_Deinit(pSerialHead);
- return(NULL);
- }
- DEBUGMSG (ZONE_INIT, (TEXT("RxHead init'ed/r/n")));
- RxResetFifo(pSerialHead);
- InitializeCriticalSection(&(pSerialHead->RxBufferInfo.CS));
- InitializeCriticalSection(&(pSerialHead->TxBufferInfo.CS));
- DEBUGMSG (ZONE_INIT, (TEXT("RxBuffer init'ed with start at %x/r/n"),
- pSerialHead->RxBufferInfo.RxCharBuffer));
- if ( pSerialHead->pHWObj->BindFlags & THREAD_AT_INIT ) {
-
- if ( ! StartDispatchThread( pSerialHead ) ) {
-
- COM_Deinit(pSerialHead);
- return(NULL);
- }
- }
-
-
- (void) pSerialHead->pHWObj->pFuncTbl->HWPostInit( pHWHead );
- DEBUGMSG (ZONE_INIT | ZONE_FUNCTION, (TEXT("-COM_Init/r/n")));
- return(pSerialHead);//返回结构体句柄,给设备管理器
- }
com_init这个函数参数是注册表的键值,并返回pSerialHead指向的结构体的指针。该函数最重要的是
pSerialHead指向的结构体,现在来看看
- typedef struct __HW_INDEP_INFO {
- CRITICAL_SECTION TransmitCritSec1;
- CRITICAL_SECTION ReceiveCritSec1;
- PHWOBJ pHWObj;
- PVOID pHWHead;
-
- HANDLE hSerialEvent;
- HANDLE hReadEvent;
- HANDLE hKillDispatchThread;
- HANDLE hTransmitEvent;
- HANDLE pDispatchThread;
- ULONG Priority256;
- ULONG DroppedBytesMDD;
- ULONG DroppedBytesPDD;
- ULONG RxBytes;
- ULONG TxBytes;
- ULONG TxBytesPending;
- ULONG TxBytesSent;
- DCB DCB;
- COMMTIMEOUTS CommTimeouts;
- DWORD OpenCnt;
- DWORD KillRxThread:1;
- DWORD XFlow:1;
- DWORD StopXmit:1;
- DWORD SentXoff:1;
- DWORD DtrFlow:1;
- DWORD RtsFlow:1;
- DWORD fAbortRead:1;
- DWORD fAbortTransmit:1;
- DWORD Reserved:24;
- ULONG fEventMask;
- RX_BUFFER_INFO RxBufferInfo;
- TX_BUFFER_INFO TxBufferInfo;
- LIST_ENTRY OpenList;
- CRITICAL_SECTION OpenCS;
- PHW_OPEN_INFO pAccessOwner;
- } HW_INDEP_INFO, *PHW_INDEP_INFO;
现在来逐一分析这个结构体的成员。
————————————————
PHWOBJ pHWObj;
- typedef struct __HWOBJ {
- ULONG BindFlags;
- DWORD dwIntID;
- PHW_VTBL pFuncTbl;
- } HWOBJ, *PHWOBJ;
在com_init函数中有pSerialHead->pHWObj = GetSerialObject( DevIndex );GetSerialObject函数参数其实是注册表中的
DeviceArrayIndex的值,这相当于串口的编号。现在来看看GetSerialObject的原型
- extern "C" PHWOBJ
- GetSerialObject(
- DWORD DeviceArrayIndex
- )
- {
- PHWOBJ pSerObj;
-
-
-
-
-
-
- pSerObj=(PHWOBJ)LocalAlloc( LPTR ,sizeof(HWOBJ) );
- if ( !pSerObj )
- return (NULL);
-
- //BindFlags 表示在哪里启动串口驱动的IST线程
- pSerObj->BindFlags = THREAD_IN_PDD;
- pSerObj>dwIntID = DeviceArrayIndex;
- pSerObj->pFuncTbl = (HW_VTBL *) &IoVTbl;
-
- return (pSerObj);
- }
GetSerialObject函数获得了串口的编号,以及负责和中间层联系。在4.2下增加串口是要修改这个函数的,现在
放在微软的源代码下是否不妥当?难道5.0另有安排?
pSerObj->pFuncTbl = (HW_VTBL *) &IoVTbl;
- 不知道怎么回事,为什么要实现那么多的层,这个IoVTbl初始化HW_VTBL 指向的结构体,何必多次一举,为什么还要改变个名字,用原来的名字不是更直观吗??不会是我C语言还没有达到水准吧?
- 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
- };
- 上面的函数表其实是实现了下面结构体内的函数(结构体赋值,相当巧妙),这个函数表只是实现,但是调用的却是下面的函数,刚开始还真反映不过来。为了分层,太TMD了,
- typedef struct __HW_VTBL {
- PVOID (*HWInit)(ULONG Identifier, PVOID pMDDContext);
- ULONG (*HWDeinit)(PVOID pHead);
- BOOL (*HWOpen)(PVOID pHead);
- ULONG (*HWClose)(PVOID pHead);
- ULONG (*HWGetBytes)(PVOID pHead, PUCHAR pTarget, PULONG pBytes);
- PVOID (*HWGetRxStart)(PVOID pHead);
- INTERRUPT_TYPE (*HWGetIntrType)(PVOID pHead);
- VOID (*HWOtherIntrHandler)(PVOID pHead);
- VOID (*HWLineIntrHandler)(PVOID pHead);
- ULONG (*HWGetRxBufferSize)(PVOID pHead);
- VOID (*HWTxIntrHandler)(PVOID pHead);
- ULONG (*HWPutBytes)(PVOID pHead, PUCHAR pSrc, ULONG NumBytes, PULONG pBytesSent);
- BOOL (*HWPowerOff)(PVOID pHead);
- BOOL (*HWPowerOn)(PVOID pHead);
- VOID (*HWClearDTR)(PVOID pHead);
- VOID (*HWSetDTR)(PVOID pHead);
- VOID (*HWClearRTS)(PVOID pHead);
- VOID (*HWSetRTS)(PVOID pHead);
- BOOL (*HWEnableIR)(PVOID pHead, ULONG BaudRate);
- BOOL (*HWDisableIR)(PVOID pHead);
- VOID (*HWClearBreak)(PVOID pHead);
- VOID (*HWSetBreak)(PVOID pHead);
- BOOL (*HWXmitComChar)(PVOID pHead, UCHAR ComChar);
- ULONG (*HWGetStatus)(PVOID pHead, LPCOMSTAT lpStat);
- VOID (*HWReset)(PVOID pHead);
- VOID (*HWGetModemStatus)(PVOID pHead, PULONG pModemStatus);
- VOID (*HWGetCommProperties)(PVOID pHead, LPCOMMPROP pCommProp);
- VOID (*HWPurgeComm)(PVOID pHead, DWORD fdwAction);
- BOOL (*HWSetDCB)(PVOID pHead, LPDCB pDCB);
- BOOL (*HWSetCommTimeouts)(PVOID pHead, LPCOMMTIMEOUTS lpCommTO);
- BOOL (*HWIoctl)(PVOID pHead, DWORD dwCode,PBYTE pBufIn,DWORD dwLenIn,
- PBYTE pBufOut,DWORD dwLenOut,PDWORD pdwActualOut);
- } HW_VTBL, *PHW_VTBL;
现在来看看在com_init中调用的HWInit——从后面的分析可以知道,其实调用的是SerInit函数
//--------------------------------------------------------------------------------------------------------
//这个函数在MDD和PDD之间按照微软的命名SerXXX,应该叫做服务层
// Converting C Style to C++ Serial Object.
/*
@doc OEM
@func PVOID | SerInit | Initializes device identified by argument.
* This routine sets information controlled by the user
* such as Line control and baud rate. It can also initialize events and
* interrupts, thereby indirectly managing initializing hardware buffers.
* Exported only to driver, called only once per process.
*
@rdesc The return value is a PVOID to be passed back into the HW
dependent layer when HW functions are called.
*/
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;
}
————————————————————————————————————————
//要增加串口的话,就要修改这个函数——增加几个case即可,哈哈,不用修改GetSerialObject了
//这个函数在BSP下的
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;
}
其实其他的串口接口函数com_Read等等,起实现方法都和com_init一致,都是调用上面结构体的函数实现了,
使用了从上到下的设计方法。在这里就不再分析了。现在开始看PDD层。
————————————————————————————————————————————————
转载请标明:作者wogoyixikexie@gliet.桂林电子科技大学一系科协,原文地址:http://blog.csdn.net/gooogleman——如有错误,希望能够留言指出;如果你有更加好的方法,也请在博客后面留言,我会感激你的批评和分享。