Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

649 lines
16 KiB

/*==========================================================================
*
* Copyright (C) 1996-1998 Microsoft Corporation. All Rights Reserved.
*
* File: comport.c
* Content: Routines for COM port I/O
*@@BEGIN_MSINTERNAL
* History:
* Date By Reason
* ==== == ======
* 4/10/96 kipo created it
* 4/12/96 kipo use GlobalAllocPtr to create memory
* 4/15/96 kipo added msinternal
* 5/22/96 kipo added support for RTSDTR flow control
* 6/10/96 kipo added modem support
* 6/22/96 kipo added support for EnumConnectionData(); added methods
* to NewComPort().
* 7/13/96 kipo added GetComPortAddress()
* 8/15/96 kipo added CRC
* 8/16/96 kipo loop on WriteFile to send large buffers
* 8/19/96 kipo update thread interface
* 1/06/97 kipo updated for objects
* 2/18/97 kipo allow multiple instances of service provider
* 4/08/97 kipo added support for separate modem and serial baud rates
* 5/23/97 kipo added support return status codes
* 11/24/97 kipo better error messages
* 1/30/98 kipo added hTerminateThreadEvent to fix bugs #15220 & #15228
*@@END_MSINTERNAL
***************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include "comport.h"
#include "dpf.h"
#include "macros.h"
// constants
#define READTIMEOUT 5000 // ms to wait before read times out
#define WRITETIMEOUT 5000 // ms to wait before write times out
#define WRITETOTALTIMEOUT 5000 // total ms to wait before write times out
#define IOBUFFERSIZE 4096 // size of read/write buffers in bytes
// prototypes
static HRESULT SetupComPort(LPDPCOMPORT globals, HANDLE hCom);
static HRESULT ShutdownComPort(LPDPCOMPORT globals);
static DWORD ReadComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD nMaxLength);
static DWORD WriteComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD dwBytesToWrite, BOOLEAN bQueueOnReenter);
static HRESULT GetComPortBaudRate(LPDPCOMPORT globals, LPDWORD lpdwBaudRate);
static HANDLE GetComPortHandle(LPDPCOMPORT globals);
static DWORD WINAPI IOThread(LPVOID lpvParam1);
/*
* NewComPort
*
* Creates a com port object of the given size. The readRoutine is called whenever
* a byte is received in the input thread.
*/
HRESULT NewComPort(DWORD dwObjectSize,
LPDIRECTPLAYSP lpDPlay, LPREADROUTINE lpReadRoutine,
LPDPCOMPORT *lplpObject)
{
LPDPCOMPORT globals;
DWORD dwError;
// allocate space for base object and our globals
globals =(LPDPCOMPORT) SP_MemAlloc(dwObjectSize);
if (globals == NULL)
{
dwError = GetLastError();
return (HRESULT_FROM_WIN32(dwError));
}
// store read routine pointer and IDirectPlaySP pointer
globals->lpReadRoutine = lpReadRoutine;
globals->lpDPlay = lpDPlay;
// fill in base methods
globals->Dispose = NULL;
globals->Connect = NULL;
globals->Disconnect = NULL;
globals->Setup = SetupComPort;
globals->Shutdown = ShutdownComPort;
globals->Read = ReadComPort;
globals->Write = WriteComPort;
globals->GetBaudRate = GetComPortBaudRate;
globals->GetHandle = GetComPortHandle;
globals->GetAddress = NULL;
globals->GetAddressChoices = NULL;
// return base object
*lplpObject = globals;
return (DP_OK);
}
/*
* SetupComPort
*
* Sets up the COM port for overlapped I/O with a read thread.
*/
static HRESULT SetupComPort(LPDPCOMPORT globals, HANDLE hCom)
{
COMMTIMEOUTS timoutInfo;
DWORD dwError;
// store com port handle
globals->hCom = hCom;
// wake up read thread when a byte arrives
SetCommMask(globals->hCom, EV_RXCHAR);
// setup read/write buffer for I/O
SetupComm(globals->hCom, IOBUFFERSIZE, IOBUFFERSIZE);
// set time outs
timoutInfo.ReadIntervalTimeout = MAXDWORD;
timoutInfo.ReadTotalTimeoutMultiplier = 0;
timoutInfo.ReadTotalTimeoutConstant = 0;
timoutInfo.WriteTotalTimeoutMultiplier = 0;
timoutInfo.WriteTotalTimeoutConstant = WRITETOTALTIMEOUT;
if (!SetCommTimeouts(globals->hCom, &timoutInfo))
goto Failure;
// create I/O event used for overlapped read
ZeroMemory(&globals->readOverlapped, sizeof(OVERLAPPED));
globals->readOverlapped.hEvent = CreateEvent( NULL, // no security
TRUE, // explicit reset req
FALSE, // initial event reset
NULL ); // no name
if (globals->readOverlapped.hEvent == NULL)
goto Failure;
// create I/O event used for overlapped write
ZeroMemory(&globals->writeOverlapped, sizeof(OVERLAPPED));
globals->writeOverlapped.hEvent = CreateEvent( NULL, // no security
TRUE, // explicit reset req
FALSE, // initial event reset
NULL ); // no name
if (globals->writeOverlapped.hEvent == NULL)
goto Failure;
// create event used to signal I/O thread to exit
globals->hTerminateThreadEvent = CreateEvent( NULL, // no security
TRUE, // explicit reset req
FALSE, // initial event reset
NULL ); // no name
if (globals->hTerminateThreadEvent == NULL)
goto Failure;
// Init vars for pending queue
InitializeCriticalSection(&globals->csWriting);
InitBilink(&globals->PendingSends);
globals->bWriting=FALSE;
// create read thread
globals->hIOThread = CreateThread(
NULL, // default security
0, // default stack size
IOThread, // pointer to thread routine
globals, // argument for thread
0, // start it right away
&globals->IOThreadID);
if (globals->hIOThread == NULL)
goto Failure;
// adjust thread priority to be higher than normal or the serial port will
// back up and the game will slow down or lose messages.
SetThreadPriority(globals->hIOThread, THREAD_PRIORITY_ABOVE_NORMAL);
ResumeThread(globals->hIOThread);
// assert DTR
EscapeCommFunction(globals->hCom, SETDTR);
return (DP_OK);
Failure:
dwError = GetLastError();
ShutdownComPort(globals);
return (HRESULT_FROM_WIN32(dwError));
}
/*
* ShutdownComPort
*
* Stop's all I/O on COM port and releases allocated resources.
*/
static HRESULT ShutdownComPort(LPDPCOMPORT globals)
{
if (globals->hIOThread)
{
// the thread will wake up if we disable event notifications using
// SetCommMask. Need to set the hTerminateThread event before doing
// this so the thread will know to exit
SetEvent(globals->hTerminateThreadEvent);
SetCommMask(globals->hCom, 0);
WaitForSingleObject(globals->hIOThread, INFINITE);
CloseHandle (globals->hIOThread);
globals->hIOThread = NULL;
// purge any outstanding reads/writes
EscapeCommFunction(globals->hCom, CLRDTR);
PurgeComm(globals->hCom, PURGE_TXABORT | PURGE_RXABORT |
PURGE_TXCLEAR | PURGE_RXCLEAR );
}
if (globals->hTerminateThreadEvent)
{
CloseHandle(globals->hTerminateThreadEvent);
globals->hTerminateThreadEvent = NULL;
}
if (globals->readOverlapped.hEvent)
{
CloseHandle(globals->readOverlapped.hEvent);
globals->readOverlapped.hEvent = NULL;
}
if (globals->writeOverlapped.hEvent)
{
CloseHandle(globals->writeOverlapped.hEvent);
globals->writeOverlapped.hEvent = NULL;
}
// the com port is shut down
globals->hCom = NULL;
// Free resources for pending queue
DeleteCriticalSection(&globals->csWriting);
return (DP_OK);
}
/*
* ReadComPort
*
* Read bytes from COM port. Will block until all bytes have been read.
*/
static DWORD ReadComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD nMaxLength)
{
COMSTAT ComStat;
DWORD dwErrorFlags, dwLength, dwError;
ClearCommError(globals->hCom, &dwErrorFlags, &ComStat);
dwLength = min(nMaxLength, ComStat.cbInQue);
if (dwLength == 0)
return (0);
if (ReadFile(globals->hCom, lpvBuffer, dwLength, &dwLength, &globals->readOverlapped))
return (dwLength);
// deal with error
dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
DPF(0, "Error reading from com port: 0x%8X", dwError);
return (0);
}
// wait for this transmission to complete
if (WaitForSingleObject(globals->readOverlapped.hEvent, READTIMEOUT) != WAIT_OBJECT_0)
{
DPF(0, "Timed out reading com port after waiting %d ms", READTIMEOUT);
return (0);
}
GetOverlappedResult(globals->hCom, &globals->readOverlapped, &dwLength, FALSE);
globals->readOverlapped.Offset += dwLength;
return (dwLength);
}
/*
* WriteComPort
*
* Write bytes to COM port. Will block until all bytes have been written.
*/
static DWORD WriteComPort(LPDPCOMPORT globals, LPVOID lpvBuffer, DWORD dwBytesToWrite, BOOLEAN bQueueOnReenter)
{
DWORD dwLength;
DWORD dwBytesWritten;
LPBYTE lpData;
DWORD dwError;
EnterCriticalSection(&globals->csWriting);
if(!globals->bWriting || !bQueueOnReenter){
globals->bWriting=TRUE;
LeaveCriticalSection(&globals->csWriting);
lpData = lpvBuffer;
dwBytesWritten = 0;
while (dwBytesWritten < dwBytesToWrite)
{
dwLength = dwBytesToWrite - dwBytesWritten;
if (WriteFile(globals->hCom, lpData, dwLength, &dwLength, &globals->writeOverlapped))
{
dwBytesWritten += dwLength;
globals->bWriting = FALSE;
return (dwBytesWritten);
}
dwError = GetLastError();
if (dwError != ERROR_IO_PENDING)
{
DPF(0, "Error writing to com port: 0x%8X", dwError);
globals->bWriting = FALSE;
return (dwBytesWritten);
}
// wait for this transmission to complete
if (WaitForSingleObject(globals->writeOverlapped.hEvent, WRITETIMEOUT) != WAIT_OBJECT_0)
{
DPF(0, "Timed out writing to com port after waiting %d ms", WRITETIMEOUT);
globals->bWriting = FALSE;
return (dwBytesWritten);
}
if (GetOverlappedResult(globals->hCom, &globals->writeOverlapped, &dwLength, TRUE) == 0)
{
dwError = GetLastError();
DPF(0, "Error writing to com port: 0x%8X", dwError);
/*
// a-josbor: this probably should return, but I'm unwilling to make the change so close to ship...
globals->bWriting = FALSE;
return (dwBytesWritten);
*/
}
globals->writeOverlapped.Offset += dwLength;
lpData += dwLength;
dwBytesWritten += dwLength;
}
if(bQueueOnReenter){ // don't drain queue recurrsively.
// Drain any pending sends.
EnterCriticalSection(&globals->csWriting);
while(!EMPTY_BILINK(&globals->PendingSends)){
LPPENDING_SEND lpPendingSend;
lpPendingSend=CONTAINING_RECORD(globals->PendingSends.next,PENDING_SEND,Bilink);
Delete(&lpPendingSend->Bilink);
LeaveCriticalSection(&globals->csWriting);
WriteComPort(globals,lpPendingSend->Data,lpPendingSend->dwBytesToWrite,FALSE);
SP_MemFree(lpPendingSend);
EnterCriticalSection(&globals->csWriting);
}
globals->bWriting=FALSE;
LeaveCriticalSection(&globals->csWriting);
}
} else {
LPPENDING_SEND lpPendingSend;
// we are in the middle of writing, so copy this to the pending queue and it will get
// sent after the current write.
lpPendingSend = (LPPENDING_SEND) SP_MemAlloc(dwBytesToWrite+sizeof(PENDING_SEND));
if(lpPendingSend){
memcpy(lpPendingSend->Data,lpvBuffer,dwBytesToWrite);
lpPendingSend->dwBytesToWrite=dwBytesToWrite;
InsertBefore(&lpPendingSend->Bilink, &globals->PendingSends);
}
LeaveCriticalSection(&globals->csWriting);
dwBytesWritten=dwBytesToWrite;
}
return (dwBytesWritten);
}
/*
* GetComPortBaudRate
*
* Get baud rate of com port.
*/
static HRESULT GetComPortBaudRate(LPDPCOMPORT globals, LPDWORD lpdwBaudRate)
{
DCB dcb;
DWORD dwError;
ZeroMemory(&dcb, sizeof(DCB));
dcb.DCBlength = sizeof(DCB);
if (!GetCommState(globals->hCom, &dcb))
goto Failure;
*lpdwBaudRate = dcb.BaudRate;
return (DP_OK);
Failure:
dwError = GetLastError();
return (HRESULT_FROM_WIN32(dwError));
}
/*
* GetComPortHandle
*
* Get handle of com port.
*/
static HANDLE GetComPortHandle(LPDPCOMPORT globals)
{
return (globals->hCom);
}
/*
* IOThread
*
* Thread to wait for events from COM port. Will call the read routine if an byte
* is received.
*/
DWORD WINAPI IOThread(LPVOID lpvParam1)
{
LPDPCOMPORT globals = (LPDPCOMPORT) lpvParam1;
DWORD dwTransfer, dwEvtMask;
OVERLAPPED os;
HANDLE events[3];
DWORD dwResult;
// create I/O event used for overlapped read
ZeroMemory(&os, sizeof(OVERLAPPED));
os.hEvent = CreateEvent(NULL, // no security
TRUE, // explicit reset req
FALSE, // initial event reset
NULL ); // no name
if (os.hEvent == NULL)
goto CreateEventFailed;
if (!SetCommMask(globals->hCom, EV_RXCHAR))
goto SetComMaskFailed;
// events to use when waiting for overlapped I/O to complete
events[0] = globals->hTerminateThreadEvent;
events[1] = os.hEvent;
events[2] = (HANDLE) -1; // work around Win95 bugs in WaitForMultipleObjects
// spin until this event is signaled during Close.
while (WaitForSingleObject(globals->hTerminateThreadEvent, 0) == WAIT_TIMEOUT)
{
dwEvtMask = 0;
// wait for COM port event
if (!WaitCommEvent(globals->hCom, &dwEvtMask, &os))
{
if (GetLastError() == ERROR_IO_PENDING)
{
// wait for overlapped I/O to complete or the terminating event
// to be set. This lets us terminate this thread even if the I/O
// never completes, which fixes a bug on NT 4.0
dwResult = WaitForMultipleObjects(2, events, FALSE, INFINITE);
// terminating event was set
if (dwResult == WAIT_OBJECT_0)
{
break; // exit the thread
}
// I/O completed
else if (dwResult == (WAIT_OBJECT_0 + 1))
{
GetOverlappedResult(globals->hCom, &os, &dwTransfer, TRUE);
os.Offset += dwTransfer;
}
}
}
// was a read event
if (dwEvtMask & EV_RXCHAR)
{
if (globals->lpReadRoutine)
globals->lpReadRoutine(globals->lpDPlay); // call read routine
}
}
SetComMaskFailed:
CloseHandle(os.hEvent);
CreateEventFailed:
ExitThread(0);
return (0);
}
/*
Name : "CRC-32"
Width : 32
Poly : 04C11DB7
Init : FFFFFFFF
RefIn : True
RefOut : True
XorOut : FFFFFFFF
Check : CBF43926
This is supposedly what Ethernet uses
*/
#if 0
#define WIDTH 32
#define POLY 0x04C11DB7
#define INITVALUE 0xFFFFFFFF
#define REFIN TRUE
#define XOROUT 0xFFFFFFFF
#define CHECK 0xCBF43926
#define WIDMASK 0xFFFFFFFF // value is (2^WIDTH)-1
#endif
/*
Name : "CRC-16"
Width : 16
Poly : 8005
Init : 0000
RefIn : True
RefOut : True
XorOut : 0000
Check : BB3D
*/
#if 1
#define WIDTH 16
#define POLY 0x8005
#define INITVALUE 0
#define REFIN TRUE
#define XOROUT 0
#define CHECK 0xBB3D
#define WIDMASK 0x0000FFFF // value is (2^WIDTH)-1
#endif
#define BITMASK(X) (1L << (X))
DWORD crc_normal(LPBYTE blk_adr, DWORD blk_len, DWORD crctable[])
{
DWORD crc = INITVALUE;
while (blk_len--)
crc = crctable[((crc>>24) ^ *blk_adr++) & 0xFFL] ^ (crc << 8);
return (crc ^ XOROUT);
}
DWORD crc_reflected(LPBYTE blk_adr, DWORD blk_len, DWORD crctable[])
{
DWORD crc = INITVALUE;
while (blk_len--)
crc = crctable[(crc ^ *blk_adr++) & 0xFFL] ^ (crc >> 8);
return (crc ^ XOROUT);
}
DWORD reflect(DWORD v, int b)
/* Returns the value v with the bottom b [0,32] bits reflected. */
/* Example: reflect(0x3e23L,3) == 0x3e26 */
{
int i;
DWORD t = v;
for (i = 0; i < b; i++)
{
if (t & 1L)
v |= BITMASK((b-1)-i);
else
v &= ~BITMASK((b-1)-i);
t >>= 1;
}
return v;
}
DWORD cm_tab (int index)
{
int i;
DWORD r;
DWORD topbit = (DWORD) BITMASK(WIDTH-1);
DWORD inbyte = (DWORD) index;
if (REFIN)
inbyte = reflect(inbyte, 8);
r = inbyte << (WIDTH-8);
for (i = 0; i < 8; i++)
{
if (r & topbit)
r = (r << 1) ^ POLY;
else
r <<= 1;
}
if (REFIN)
r = reflect(r, WIDTH);
return (r & WIDMASK);
}
void generate_table(DWORD dwTable[])
{
int i;
for (i = 0; i < 256; i++)
{
dwTable[i] = cm_tab(i);
}
}
// todo - make this a static table
DWORD gCRCTable[256];
BOOL gTableCreated = FALSE;
DWORD GenerateCRC(LPVOID pBuffer, DWORD dwBufferSize)
{
if (!gTableCreated)
{
generate_table(gCRCTable);
gTableCreated = TRUE;
}
return (crc_reflected(pBuffer, dwBufferSize, gCRCTable));
}