📄 mdd.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Module Name:
mdd.c
Abstract:
This file contains the serial mdd (model device driver) code. This is
intended to work with a series of serial pdds to implement serial
devices for the WinCE OS. Since most of the serial handling can be
done in a hardware independant way this should limit the amout of
individual code an OEM would need to develop.
Functions:
COM_Init
COM_Open
COM_Close
COM_Deinit
COM_Read
COM_Write
COM_Seek
COM_PowerUp
COM_PowerDown
COM_IOControl
SerialDllEntry
SerialEventHandler
SerialDispatchThread
ApplyDCB
SerialGetDroppedByteNumber
WaitCommEvent
EvaluateEventFlag
ProcessExiting
Notes:
--*/
#include <windows.h>
#include <types.h>
#include <memory.h>
#include <linklist.h>
#include <nkintr.h>
#include <serdbg.h>
#include <serpriv.h>
#include <hwcomapi.h>
#include <pegdser.h>
#include <devload.h>
#include <pm.h>
/* Debug Zones.
*/
#ifdef DEBUG
#define DBG_INIT 0x0001
#define DBG_OPEN 0x0002
#define DBG_READ 0x0004
#define DBG_WRITE 0x0008
#define DBG_CLOSE 0x0010
#define DBG_IOCTL 0x0020
#define DBG_THREAD 0x0040
#define DBG_EVENTS 0x0080
#define DBG_CRITSEC 0x0100
#define DBG_FLOW 0x0200
#define DBG_IR 0x0400
#define DBG_NOTHING 0x0800
#define DBG_ALLOC 0x1000
#define DBG_FUNCTION 0x2000
#define DBG_WARNING 0x4000
#define DBG_ERROR 0x8000
#define ZONE_WRITE 1
#define ZONE_FUNCTION 1
DBGPARAM dpCurSettings = {
TEXT("Serial"), {
TEXT("Init"),TEXT("Open"),TEXT("Read"),TEXT("Write"),
TEXT("Close"),TEXT("Ioctl"),TEXT("Thread"),TEXT("Events"),
TEXT("CritSec"),TEXT("FlowCtrl"),TEXT("Infrared"),TEXT("User Read"),
TEXT("Alloc"),TEXT("Function"),TEXT("Warning"),TEXT("Error")},
0
};
#endif
#ifndef MIN
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#endif
//
// Macros to maintain a usage cout for a particular serial structure,
// so that we know when it is safe to deallocate it.
//
#define COM_INC_USAGE_CNT(pOpenHead) \
InterlockedIncrement(&pOpenHead->StructUsers)
#define COM_DEC_USAGE_CNT(pOpenHead) \
InterlockedDecrement(&pOpenHead->StructUsers)
// Define some internally used functions
BOOL COM_Close(PHW_OPEN_INFO pOpenHead);
BOOL COM_Deinit(PHW_INDEP_INFO pSerialHead);
VOID EvaluateEventFlag(PVOID pHead, ULONG fdwEventMask);
/*
@doc INTERNAL
@func BOOL | SerialDllEntry | Process attach/detach api.
*
@rdesc The return is a BOOL, representing success (TRUE) or failure (FALSE).
*/
BOOL
SerialDllEntry(
HINSTANCE hinstDll, /*@parm Instance pointer. */
DWORD dwReason, /*@parm Reason routine is called. */
LPVOID lpReserved /*@parm system parameter. */
)
{
if ( dwReason == DLL_PROCESS_ATTACH ) {
DEBUGREGISTER(hinstDll);
DEBUGMSG (ZONE_INIT, (TEXT("serial port process attach\r\n")));
DisableThreadLibraryCalls((HMODULE) hinstDll);
}
if ( dwReason == DLL_PROCESS_DETACH ) {
DEBUGMSG (ZONE_INIT, (TEXT("process detach called\r\n")));
}
return (TRUE);
}
VOID
SerialEventHandler(PHW_INDEP_INFO pSerialHead)
{
PHW_VTBL pFuncTbl = pSerialHead->pHWObj->pFuncTbl;
PVOID pHWHead = pSerialHead->pHWHead;
ULONG CharIndex;
ULONG RoomLeft = 0;
ULONG TotalLeft = 0;
INTERRUPT_TYPE it = INTR_NONE;
BOOL RxDataAvail = FALSE;
DEBUGMSG (ZONE_THREAD, (TEXT("+SerialEventHandler, pHead 0x%X\r\n"),
pSerialHead));
if ( pSerialHead->KillRxThread ||
!pSerialHead->hSerialEvent ) {
DEBUGMSG (ZONE_THREAD, (TEXT("Exitting thread\r\n")));
SetEvent(pSerialHead->hKillDispatchThread);
ExitThread(0);
}
// NOTE - This one is a little tricky. If the only owner is a monitoring task
// then I don't have an owner for read/write, yet I might be in this routine due
// due to a change in line status. Lets just do the best we can and increment
// the count for the access owner if available.
if ( pSerialHead->pAccessOwner )
COM_INC_USAGE_CNT(pSerialHead->pAccessOwner);
while ( it = pFuncTbl->HWGetIntrType(pHWHead) ) {
DEBUGMSG (ZONE_THREAD,
(TEXT("SerialEventHandler, Interrupts 0x%X\r\n"), it));
if ( it & INTR_RX ) {
// It's read data event. Optimize the read by reading chunks
// if the user has not specified using xflow control
// or event/error/eof characters. Ack the receive,
// unmask the interrupt, get the current data pointer
// Note: We have to copy RxRead and RxWrite index to local in order to make it atomic.
register DWORD RxWIndex=RxWrite(pSerialHead), RxRIndex=RxRead(pSerialHead);
DEBUGMSG (ZONE_THREAD|ZONE_READ , (TEXT("Rx Event\r\n")));
RxEnterCS(pSerialHead);
if ( RxRIndex == 0 ) {
// have to leave one byte free.
RoomLeft = RxLength(pSerialHead) - RxWIndex - 1;
} else {
RoomLeft = RxLength(pSerialHead) - RxWIndex;
}
if ( RxRIndex > RxWIndex ) {
RoomLeft = RxRIndex - RxWIndex - 1;
}
if ( RoomLeft ) {
pSerialHead->DroppedBytesPDD +=
pFuncTbl->HWGetBytes(pHWHead,
RxBuffWrite(pSerialHead),
&RoomLeft);
} else {
BYTE TempBuf[16];
RoomLeft = 16;
pFuncTbl->HWGetBytes(pHWHead,
TempBuf,
&RoomLeft);
pSerialHead->DroppedBytesMDD += RoomLeft;
DEBUGMSG (ZONE_WARN|ZONE_READ, (TEXT("Tossed %d bytes\r\n"),
RoomLeft));
RoomLeft = 0;
}
DEBUGMSG (ZONE_READ ,
(TEXT("After HWGetBytes, Fifo(R=%d,W=%d,BA=%d,L=%d) ByteRead=%d\r\n"),
RxRead(pSerialHead), RxWrite(pSerialHead),
RxBytesAvail(pSerialHead), RxLength(pSerialHead),
RoomLeft));
// If flow control enabled then we need to scan for XON/XOFF
// characters
if ( pSerialHead->XFlow ) {
for ( CharIndex=0; CharIndex < RoomLeft; ) {
if ( RxBuffWrite(pSerialHead)[CharIndex] ==
pSerialHead->DCB.XoffChar ) {
DEBUGMSG (ZONE_FLOW, (TEXT("Received XOFF\r\n")));
pSerialHead->StopXmit = 1;
memmove (RxBuffWrite(pSerialHead)+CharIndex,
RxBuffWrite(pSerialHead)+CharIndex+1,
RoomLeft - CharIndex);
RoomLeft--;
continue;
} else if ( RxBuffWrite(pSerialHead)[CharIndex] ==
pSerialHead->DCB.XonChar ) {
pSerialHead->StopXmit = 0;
DEBUGMSG (ZONE_FLOW, (TEXT("Received XON\r\n")));
memmove (RxBuffWrite(pSerialHead)+CharIndex,
RxBuffWrite(pSerialHead)+CharIndex+1,
RoomLeft - CharIndex);
RoomLeft--;
continue;
}
CharIndex++;
}
}
pSerialHead->RxBytes += RoomLeft;
RxWrite(pSerialHead) =
(RxWrite(pSerialHead)+RoomLeft<RxLength(pSerialHead)? RxWrite(pSerialHead)+RoomLeft: RxWrite(pSerialHead)+RoomLeft-RxLength(pSerialHead));
RxLeaveCS(pSerialHead);
if ( RoomLeft ) {
RxDataAvail = TRUE;
}
/* Support DTR_CONTROL_HANDSHAKE/RTS_CONTROL_HANDSHAKE
* signal is cleared when the input buffer is more than 3/4 full.
*/
if ( (pSerialHead->DCB.fDtrControl == DTR_CONTROL_HANDSHAKE) &&
(!pSerialHead->DtrFlow) &&
(4*RxBytesAvail(pSerialHead) > (3*RxLength(pSerialHead))) ) {
DEBUGMSG (ZONE_READ|ZONE_FLOW,
(TEXT("DTR_CONTROL_HANDSHAKE Clearing DTR\r\n")));
pSerialHead->DtrFlow = 1;
pFuncTbl->HWClearDTR(pHWHead);
}
if ( (pSerialHead->DCB.fRtsControl == RTS_CONTROL_HANDSHAKE) &&
(!pSerialHead->RtsFlow) &&
(4*RxBytesAvail(pSerialHead) > (3*RxLength(pSerialHead))) ) {
DEBUGMSG (ZONE_READ|ZONE_FLOW,
(TEXT("RTS_CONTROL_HANDSHAKE Clearing RTS\r\n")));
pSerialHead->RtsFlow = 1;
pFuncTbl->HWClearRTS(pHWHead);
}
/* If Xon/Xoff flow control is desired. check the limit against
* the remaining room and act accordingly.
*/
if ( pSerialHead->DCB.fInX && !(pSerialHead->SentXoff) &&
( pSerialHead->DCB.XoffLim >=
(RxLength(pSerialHead) - RxBytesAvail(pSerialHead))) ) {
DEBUGMSG (ZONE_FLOW, (TEXT("Sending XOFF\r\n")));
pFuncTbl->HWXmitComChar(pHWHead, pSerialHead->DCB.XoffChar);
pSerialHead->SentXoff = 1;
if ( !pSerialHead->DCB.fTXContinueOnXoff ) {
pSerialHead->StopXmit = 1;
}
}
}
if ( it & INTR_TX ) {
/* If the transmit interrupt bit is set in the serial CSR, then
* it must be a transmit interrupt, so call the HW tx handler,
* unmask the interrupt and then notify SerialTransmit() that if
* it has a PutBytes pending, it can do that now.
*/
DEBUGMSG (ZONE_THREAD, (TEXT("Transmit Event\r\n")));
/* Call the hardware Tx interrupt handler since the hardware
* has interrupted.
*/
pFuncTbl->HWTxIntrHandler(pHWHead);
SetEvent(pSerialHead->hTransmitEvent);
}
if ( (it & INTR_MODEM) ) {
DEBUGMSG (ZONE_THREAD, (TEXT("Other Event, it:%x\r\n"), it));
/* Call low level status clean up code.
*/
pFuncTbl->HWOtherIntrHandler(pHWHead);
}
if ( it & INTR_LINE ) {
DEBUGMSG (ZONE_THREAD, (TEXT("Line Event, it:%x\r\n"), it));
/* Call low level line status clean up code.
* Then unmask the interrupt
*/
pFuncTbl->HWLineIntrHandler(pHWHead);
}
}
// We kept this till the end to optimize the above loop
if ( RxDataAvail ) {
// Signal COM_Read that bytes are available.
SetEvent(pSerialHead->hReadEvent);
EvaluateEventFlag(pSerialHead, EV_RXCHAR);
}
DEBUGMSG (ZONE_THREAD ,
(TEXT("-SerialEventHandler, Fifo(R=%d,W=%d,L=%d)\r\n"),
RxRead(pSerialHead), RxWrite(pSerialHead),
RxLength(pSerialHead)));
if ( pSerialHead->pAccessOwner )
COM_DEC_USAGE_CNT(pSerialHead->pAccessOwner);
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -