1. PDD層代碼簡單分析
PDD層的主要包含了以下2個類:CSerialPDDPowerUpCallback, CSerialPDD, 下面簡單的分析這2個類的作用。
1.1 CSerialPDDPowerUpCallback
CSerialPDDPowerUpCallback 類用于串口電源上電時的處理。
在調用CSerialPDD::Init()后會創建一個CSerialPDDPowerUpCallback類型的對象
在CSerialPDD::PowerOn()函數中會調用此對象的SignalCallBack()函數, 這樣RunThread就開始運行, 進而通過調用CSerialPDD::NotifyPDDInterrupt()進行后續處理, 包括調用
CeEventHasOccurred (NOTIFICATION_EVENT_RS232_DETECTED, NULL) 通知系統有串口被探測到.
以及調用SerialEventHandler()進行MDD層上的處理.
更詳細的細節可以參考微軟的源代碼:
%WINCEROOT%\PUBLIC\COMMON\OAK\DRIVERS\SERIAL\SERPDDCM\cserpdd.cpp
%WINCEROOT%\PUBLIC\COMMON\OAK\INC\cserpdd.h
2.2 CSerialPDD
CSerialPDD 是串口PDD層的關鍵,實現對硬件的操作,開發者開發的驅動就是繼承這個類,并實現重載相關的函數來完成特定設備的PDD功能的。
這個類抽象了以下這些接口:
// Tx Function.
virtual BOOL InitXmit(BOOL bInit) = 0;
virtual void XmitInterruptHandler(PUCHAR pTxBuffer, ULONG *pBuffLen) = 0;
virtual void XmitComChar(UCHAR ComChar) = 0;
virtual BOOL EnableXmitInterrupt(BOOL bEnable)= 0;
virtual BOOL CancelXmit() = 0 ;
// Rx Function.
virtual BOOL InitReceive(BOOL bInit) = 0;
virtual ULONG ReceiveInterruptHandler(PUCHAR pRxBuffer,ULONG *pBufflen) = 0;
virtual ULONG CancelReceive() = 0;
// Modem
virtual BOOL InitModem(BOOL bInit) = 0;
virtual void ModemInterruptHandler()= 0; // This is Used to Indicate Modem Signal Changes.
virtual ULONG GetModemStatus() = 0;
virtual void SetDTR(BOOL bSet)= 0;
virtual void SetRTS(BOOL bSet)= 0;
virtual BOOL IsCTSOff() { return ((GetModemStatus() & MS_CTS_ON)==0) ; };
virtual BOOL IsDSROff() { return ((GetModemStatus() & MS_DSR_ON)==0) ; };
// Line Function
virtual BOOL InitLine(BOOL bInit) = 0;
virtual void LineInterruptHandler() = 0;
virtual void SetBreak(BOOL bSet) = 0 ;
virtual BOOL SetBaudRate(ULONG BaudRate,BOOL bIrModule) = 0;
virtual BOOL SetByteSize(ULONG ByteSize) = 0;
virtual BOOL SetParity(ULONG Parity)= 0;
virtual BOOL SetStopBits(ULONG StopBits)= 0;
以上這些接口都是純虛函數, 在實現PDD層時, 必須實現這些接口.
當然不需要的功能你可以簡單的用類似virtual BOOL func() {;} 的形式來實現。
其他的非純虛函數的虛函數也可以被用戶重載, 以實現自己特定的功能。
我們知道PDD層的函數為如下函數:
GetSerialObject
This function returns a pointer to a HWOBJ structure. The structure contains the function pointers and parameters for the hardware interface functions of the relevant lower layer.
HWClearBreak
This function clears an RS-232 line break condition.
HWClearDTR
This function clears the Data Terminal Ready (DTR) signal.
HWClearRTS
This function clears the Request to Send (RTS) signal.
HWClose
This function closes the device initialized by the HWInit function.
HWDeinit
This function is called by the upper layer to de-initialize the hardware when a device driver is unloaded.
HWDisableIR
This function disables the infrared (IR) serial interface.
HWEnableIR
This function enables the infrared (IR) serial interface.
HWGetCommProperties
This function retrieves the current properties of the communications device.
HWGetIntrType
This function returns the current interrupt type.
HWGetModemStatus
This function retrieves the modem status.
HWGetRxBufferSize
This function returns the maximum number of bytes that the hardware buffer can hold, not including the padding, stop, and start bits.
HWGetRxStart
This function returns the start of the hardware-receive buffer.
HWGetStatus
This function specifies the hardware status API.
HWInit
This function initializes a serial device.
HWIoctl
This function executes device I/O control (IOCTL) routines.
HWLineIntrHandler
This function handles line interrupts for serial port devices.
HWModemIntrHandler
This function handles the modem interrupt. In the serial port upper layer implementation available in Microsoft Windows CE 3.0 and later, this function replaces the HWOtherIntrHandler function.
HWOpen
This function is called by the upper layer to open the serial device.
HWOtherIntrHandler
In Windows CE 3.0 and later, this function has been replaced with the new function HWModemIntrHandler.
HWPostInit
This function performs necessary operations after it initializes all data structures and prepares the serial IST to begin handling interrupts. It is called by the upper layer.
HWPowerOff
This function notifies the platform-dependent driver that the hardware platform is about to enter suspend mode. It is called by the model device driver (MDD).
HWPowerOn
This function notifies the platform-dependent driver that the hardware platform is resuming from suspend mode. It is called by the MDD.
HWPurgeComm
This function purges the communications device.
HWPutBytes
This function writes bytes to hardware. The driver calls this function.
HWReset
This function resets the hardware API.
HWRxIntrHandler
This function handles serial port interrupts.
HWSetBreak
This function sets the line break condition on the transmit line.
HWSetCommTimeouts
This function sets the communications time-out events in response to a call to the SetCommTimeouts function.
HWSetDCB
This function sets the device control block.
HWSetDTR
This function sets the Data Terminal Ready (DTR) signal.
HWSetRTS
This function sets the Request to Send (RTS) signal.
HWTxIntrHandler
This function handles the transmit interrupt for serial port devices.
HWXmitComChar
This function transmits a single character.
更具體的細節可以參考:MSDN幫助 ms-help://MS.WindowsCE.500/wceddk5/html/wce50grfserialportdriverfunctions.htm
微軟用一堆SerXXXXX()的函數封裝了CSerialPdd相關的操作,來完成PDD功能。
2. PDD與MDD層的交互
2.1 PDD層的函數是如何能夠被MDD層所調用
1.2節介紹的那一堆SerXXXXX()函數會被記錄到一個函數指針表里,真是通過這個函數指針表來實現與MDD層的交互的。此函數指針表是HW_VTBL類型的, 在微軟的代碼里是命名為IoVTbl的函數指針表。
MDD層通過調用 GetSerialObject(DWORD DeviceArrayIndex)函數來獲取一個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;
其中pFuncTbl正是指向我們之前說到的 IoVTbl 函數指針結構的地址.
這樣MDD層就可以通過->pFuncTbl.DDSIFunction()來對特定的硬件做特定的操作了.
2.2 GetSerialObject函數
這個函數的原型是:PHWOBJ GetSerialObject(DWORD DeviceArrayIndex) ,它是連接MDD和PDD層的關鍵函數, 也是實現多端口驅動的關鍵, 這個函數允許一個MDD層連接多個PDD層。
在MDD層的COM_Init函數中,驅動會通過查詢DeviceArrayIndex鍵值, 得到我們需要打開的是普通串口或者是其他種類的串口設備。
而正是這個數值決定了GetSerialObject是返回哪個串口的PHWOBJ指針, PHWOBJ包含了pFuncTbl,這個正是前一節說到的PDD層函數指針表,通過這個函數表我們就可以實現對相應的串口設備進行操作。(串口有可能是標準的串口,也有可能是紅外設備)
2.3 CreateSerialObject 函數
如果開發者選擇直接繼承微軟CSerialPdd類來實現串口的PDD層,還要實現CreateSerialObject及DeleteSerialObject函數。
在MDD層的COM_Init函數被調用時,驅動會通過查詢注冊表來得到相關參數并調用PDD層的HWInit函數,HWInit函數又會調用CreateSerialObject來初始化串口設備,在關閉串口以后DeleteSerialObject函數會被調用。
3. 其他
3.1 COM_Init函數如何被調用
此函數通常是由Device.exe進程調用的, 而此函數的參數Identifier 是一個指向注冊表HKEY_LOCAL_MACHINE\Drivers\Active.下的一個鍵值,這也是決定驅動如何操作硬件的關鍵。
對于使用ActivateDeviceEx函數調用的情況,請參照MSND, http://msdn.microsoft.com/en-us/library/aa447677.aspx
3.2 相關代碼段:
view plaincopy to clipboardprint?
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);
/*
* INTERNAL: this routine initializes the hardware abstraction interface
* via HWInit(). It allocates a data structure representing this
* instantiation of the device. It also creates an event and initializes
* a critical section for receiving as well as registering the logical
* interrupt dwIntID with NK via InterruptInitialize. This call
* requires that the hardware dependent portion export apis that return
* the physical address of the receive buffer and the size of that buffer.
* Finally, it creates a buffer to act as an intermediate
* buffer when receiving.
*/
DEBUGMSG (ZONE_INIT | ZONE_FUNCTION, (TEXT("+COM_Init\r\n")));
// Allocate our control structure.
pSerialHead = (PHW_INDEP_INFO)LocalAlloc(LPTR, sizeof(HW_INDEP_INFO));
// Check that LocalAlloc did stuff ok too.
if ( !pSerialHead ) {
DEBUGMSG(ZONE_INIT | ZONE_ERROR,
(TEXT("Error allocating memory for pSerialHead, COM_Init failed\n\r")));
return(NULL);
}
// Initially, open list is empty.
InitializeListHead( &pSerialHead->OpenList );
InitializeCriticalSection(&(pSerialHead->OpenCS));
pSerialHead->pAccessOwner = NULL;
pSerialHead->fEventMask = 0;
// Init CommTimeouts.
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;
/* Create tx and rx events and stash in global struct field. Check return.
*/
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);
}
/* Initialize the critical sections that will guard the parts of
* the receive and transmit buffers.
*/
InitializeCriticalSection(&(pSerialHead->ReceiveCritSec1));
InitializeCriticalSection(&(pSerialHead->TransmitCritSec1));
/* Want to use the Identifier to do RegOpenKey and RegQueryValue (?)
* to get the index to be passed to GetHWObj.
* The HWObj will also have a flag denoting whether to start the
* listening thread or provide the callback.
*/
DEBUGMSG (ZONE_INIT,(TEXT("Try to open %s\r\n"), (LPCTSTR)Identifier));
hKey = OpenDeviceKey((LPCTSTR)Identifier);
// 通過調用OpenDeviceKey, 驅動可以通過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);
// 通過查詢Priority256鍵值, 我們可以知道IST的優先級, 在后面會通過這個值來改變IST的優先級.
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));
// Initialize hardware dependent data.
// GetSerialObject是連接MDD和PDD層的關鍵函數, 在這個函數里面決定了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));
// 這里調用了PDD層的HWInit
pHWHead = pSerialHead->pHWObj->pFuncTbl->HWInit(Identifier, pSerialHead, pSerialHead->pHWObj);
pSerialHead->pHWHead = pHWHead;
/* Check that HWInit did stuff ok. From here on out, call Deinit function
* when things fail.
*/
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")));
// Allocate at least twice the hardware buffer size so we have headroom
HWBufferSize = 2 * pSerialHead->pHWObj->pFuncTbl->HWGetRxBufferSize(pHWHead);
// Init rx buffer and buffer length here.
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 ) {
// Hook the interrupt and start the associated thread.
if ( ! StartDispatchThread( pSerialHead ) ) {
// Failed on InterruptInitialize or CreateThread. Bail.
COM_Deinit(pSerialHead);
return(NULL);
}
}
// OK, now that everything is ready on our end, give the PDD
// one last chance to init interrupts, etc.
(void) pSerialHead->pHWObj->pFuncTbl->HWPostInit( pHWHead );
DEBUGMSG (ZONE_INIT | ZONE_FUNCTION, (TEXT("-COM_Init\r\n")));
return(pSerialHead);
}
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);
/*
* INTERNAL: this routine initializes the hardware abstraction interface
* via HWInit(). It allocates a data structure representing this
* instantiation of the device. It also creates an event and initializes
* a critical section for receiving as well as registering the logical
* interrupt dwIntID with NK via InterruptInitialize. This call
* requires that the hardware dependent portion export apis that return
* the physical address of the receive buffer and the size of that buffer.
* Finally, it creates a buffer to act as an intermediate
* buffer when receiving.
*/
DEBUGMSG (ZONE_INIT | ZONE_FUNCTION, (TEXT("+COM_Init\r\n")));
// Allocate our control structure.
pSerialHead = (PHW_INDEP_INFO)LocalAlloc(LPTR, sizeof(HW_INDEP_INFO));
// Check that LocalAlloc did stuff ok too.
if ( !pSerialHead ) {
DEBUGMSG(ZONE_INIT | ZONE_ERROR,
(TEXT("Error allocating memory for pSerialHead, COM_Init failed\n\r")));
return(NULL);
}
// Initially, open list is empty.
InitializeListHead( &pSerialHead->OpenList );
InitializeCriticalSection(&(pSerialHead->OpenCS));
pSerialHead->pAccessOwner = NULL;
pSerialHead->fEventMask = 0;
// Init CommTimeouts.
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;
/* Create tx and rx events and stash in global struct field. Check return.
*/
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);
}
/* Initialize the critical sections that will guard the parts of
* the receive and transmit buffers.
*/
InitializeCriticalSection(&(pSerialHead->ReceiveCritSec1));
InitializeCriticalSection(&(pSerialHead->TransmitCritSec1));
/* Want to use the Identifier to do RegOpenKey and RegQueryValue (?)
* to get the index to be passed to GetHWObj.
* The HWObj will also have a flag denoting whether to start the
* listening thread or provide the callback.
*/
DEBUGMSG (ZONE_INIT,(TEXT("Try to open %s\r\n"), (LPCTSTR)Identifier));
hKey = OpenDeviceKey((LPCTSTR)Identifier);
// 通過調用OpenDeviceKey, 驅動可以通過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);
// 通過查詢Priority256鍵值, 我們可以知道IST的優先級, 在后面會通過這個值來改變IST的優先級.
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));
// Initialize hardware dependent data.
// GetSerialObject是連接MDD和PDD層的關鍵函數, 在這個函數里面決定了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));
// 這里調用了PDD層的HWInit
pHWHead = pSerialHead->pHWObj->pFuncTbl->HWInit(Identifier, pSerialHead, pSerialHead->pHWObj);
pSerialHead->pHWHead = pHWHead;
/* Check that HWInit did stuff ok. From here on out, call Deinit function
* when things fail.
*/
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")));
// Allocate at least twice the hardware buffer size so we have headroom
HWBufferSize = 2 * pSerialHead->pHWObj->pFuncTbl->HWGetRxBufferSize(pHWHead);
// Init rx buffer and buffer length here.
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 ) {
// Hook the interrupt and start the associated thread.
if ( ! StartDispatchThread( pSerialHead ) ) {
// Failed on InterruptInitialize or CreateThread. Bail.
COM_Deinit(pSerialHead);
return(NULL);
}
}
// OK, now that everything is ready on our end, give the PDD
// one last chance to init interrupts, etc.
(void) pSerialHead->pHWObj->pFuncTbl->HWPostInit( pHWHead );
DEBUGMSG (ZONE_INIT | ZONE_FUNCTION, (TEXT("-COM_Init\r\n")));
return(pSerialHead);
}