📄 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 amount 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
DllEntry
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>
#ifdef TARGET_NT
#include <devemul.h>
#include <nteser.h>
#endif
/* 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
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 count 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 | DllEntry | Process attach/detach api.
*
@rdesc The return is a BOOL, representing success (TRUE) or failure (FALSE).
*/
BOOL
DllEntry(
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);
}
/*
@doc INTERNAL
@func VOID | DoTxData | Sends next available chunk of TX data.
*
*/
VOID
DoTxData( PHW_INDEP_INFO pSerialHead )
{
PHW_VTBL pFuncTbl = pSerialHead->pHWObj->pFuncTbl;
PVOID pHWHead = pSerialHead->pHWHead;
ULONG Len;
DEBUGMSG (ZONE_WRITE, (TEXT("DoPutBytes wait for CritSec %x.\r\n"),
&(pSerialHead->TxBufferInfo.CS)));
TxEnterCS(pSerialHead);
DEBUGMSG (ZONE_WRITE, (TEXT("DoPutBytes got CritSec %x.\r\n"),
&(pSerialHead->TxBufferInfo.CS)));
// If device was closed from under us, stop transmitting
if ( !pSerialHead->OpenCnt ) {
DEBUGMSG (ZONE_THREAD|ZONE_WRITE , (TEXT("Device closed! Quit transmission!\r\n")));
DEBUGMSG (ZONE_WRITE,
(TEXT("SerialEventHandler: %d sent up-to-now.\n\r"),pSerialHead->TxBytesSent));
pSerialHead->TxBufferInfo.Permissions = 0;
pSerialHead->TxBufferInfo.TxCharBuffer = NULL;
pSerialHead->TxBufferInfo.Length = 0;
TxRead(pSerialHead) = 0;
}
// Check the flow control status, and if not flowed off, call the
// hw TX routine to actually transmit some data.
if ( pSerialHead->TxBufferInfo.TxCharBuffer && TxBytesAvail(pSerialHead) ) {
DWORD oldPerm = SetProcPermissions(pSerialHead->TxBufferInfo.Permissions);
if ( pSerialHead->DCB.fRtsControl == RTS_CONTROL_TOGGLE ) {
DEBUGMSG (ZONE_THREAD|ZONE_WRITE , (TEXT("RTS set.\r\n")));
pFuncTbl->HWSetRTS(pHWHead);
}
// Don't transmit anything if we are flowed off.
if ( pSerialHead->StopXmit ) {
// But we still need to call TxIntrHandler so that the interrupt
// gets cleared.
DEBUGMSG (ZONE_FLOW|ZONE_WRITE , (TEXT("XOFF'ed, send nothing.\r\n")));
Len = 0;
} else {
DEBUGMSG (ZONE_WRITE,
(TEXT("TxRead = %d, TxLength = %d, TxBytesAvail = %d.\r\n"),
TxRead(pSerialHead), TxLength(pSerialHead),
TxBytesAvail(pSerialHead)));
Len = TxBytesAvail(pSerialHead);
}
DEBUGMSG (ZONE_WRITE, (TEXT("About to copy %d bytes\r\n"), Len));
pFuncTbl->HWTxIntrHandler(pHWHead,
TxBuffRead(pSerialHead),
&Len);
DEBUGMSG (ZONE_WRITE, (TEXT("%d bytes actually copied.\r\n"), Len));
// Update Fifo info
pSerialHead->TxBytes += Len;
pSerialHead->TxBytesSent += Len;
TxRead(pSerialHead) += Len;
// Even if everything was Tx'ed, don't signal TX complete until
// we get transmit interrupt indicating that the data has
// actually been sent. Since few/no UARTS have a way to tell
// how much data remains, we don't bother trying to adjust the
// return length to account for partially completed hardware buffer TX
SetProcPermissions(oldPerm);
} else {
// Even if there is nothing left to send, we need to call
// the interrupt handler so that it can clear the
// transmit interrupt
Len = 0;
pFuncTbl->HWTxIntrHandler(pHWHead,
NULL,
&Len);
DEBUGMSG (ZONE_WRITE, (TEXT("Transmission complete, %d bytes sent\r\n"), Len));
pSerialHead->TxBufferInfo.Permissions = 0;
pSerialHead->TxBufferInfo.TxCharBuffer = NULL;
pSerialHead->TxBufferInfo.Length = 0;
TxRead(pSerialHead) = 0;
SetEvent(pSerialHead->hTransmitEvent);
}
TxLeaveCS(pSerialHead);
DEBUGMSG (ZONE_WRITE|ZONE_FUNCTION,
(TEXT("DoPutBytes released CritSec: %x.\r\n"),
&(pSerialHead->TxBufferInfo.CS)));
}
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 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 ( 1 ) {
if ( !(it = pFuncTbl->HWGetIntrType(pHWHead)) ) {
DEBUGMSG (ZONE_THREAD,
(TEXT("SerialEventHandler, No Interrupt.\r\n")));
break;
}
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
// and see if data is available.
DEBUGMSG (ZONE_THREAD|ZONE_READ , (TEXT("Rx Event\r\n")));
if ( RxRead(pSerialHead) == 0 ) {
// have to leave one byte free.
RoomLeft = RxLength(pSerialHead) - RxWrite(pSerialHead) - 1;
} else {
RoomLeft = RxLength(pSerialHead) - RxWrite(pSerialHead);
}
if ( RxRead(pSerialHead) > RxWrite(pSerialHead) ) {
RoomLeft = RxRead(pSerialHead) - RxWrite(pSerialHead) - 1;
}
if ( RoomLeft ) {
pSerialHead->DroppedBytesPDD +=
pFuncTbl->HWRxIntrHandler(pHWHead,
RxBuffWrite(pSerialHead),
&RoomLeft);
} else {
BYTE TempBuf[16];
RoomLeft = 16;
pFuncTbl->HWRxIntrHandler(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")));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -