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.
 
 
 
 
 
 

781 lines
19 KiB

/*--------------------------------------------------------------
*
* FILE: SK_COMM.C
*
* PURPOSE: The file contains the Functions responsible for
* managing the COMM ports
*
* CREATION: June 1994
*
* COPYRIGHT: Black Diamond Software (C) 1994
*
* AUTHOR: Ronald Moak
*
* NOTES:
*
* This file, and all others associated with it contains trade secrets
* and information that is proprietary to Black Diamond Software.
* It may not be copied copied or distributed to any person or firm
* without the express written permission of Black Diamond Software.
* This permission is available only in the form of a Software Source
* License Agreement.
*
* $Header: %Z% %F% %H% %T% %I%
*
*--- Includes ---------------------------------------------------------*/
//#define WINVER 0x0300
// added to be compatible with new windows.h (12/91) and wintric.h
#define USECOMM
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#include "windows.h"
//#include "winstric.h" // added for win 3.1 compatibility 1/92
#include "gide.h" // Serial Keys Function Proto
#include "initgide.h" // Serial Keys Function Proto
#include "w95trace.h"
#include "sk_defs.h"
#include "sk_comm.h"
#include "drivers.h"
#include "sk_ex.h"
#define COMMTERMINATE 0xFFFFFFFF // this 'character' indicates a request to terminate
// Local Function ProtoTypes --------------------------------
static BOOL OpenComm();
static void __cdecl ProcessComm(VOID *notUsed);
static int ReadComm();
// Local Variables ---------------------------------------------------
static DCB s_dcbCommNew; // New DCB for comm port
static DCB s_dcbCommOld; // Origional DCB for comm port
static OVERLAPPED s_oRead; // Overlapped structure for reading.
static HANDLE s_hFileComm;
static HANDLE s_hThreadComm = NULL;
static HDESK s_hdeskUser = NULL;
static DWORD s_NullTimer;
static int s_NullCount=0;
static HANDLE s_ahEvents[2] = {NULL, NULL};
#define iEventComm 0
#define iEventExit 1
/*---------------------------------------------------------------
*
* Global Functions -
*
*---------------------------------------------------------------*/
/*---------------------------------------------------------------
*
* FUNCTION void InitComm()
*
* TYPE Global
*
* PURPOSE
*
* INPUTS None
*
* RETURNS None
*
*---------------------------------------------------------------*/
BOOL InitComm()
{
BOOL fOk = TRUE;
DBPRINTF(TEXT("InitComm()\r\n"));
// Create Event for Overlap File Read
s_ahEvents[iEventComm] = CreateEvent(NULL, TRUE, FALSE, NULL);
fOk = (NULL != s_ahEvents[iEventComm]);
if (fOk)
{
s_ahEvents[iEventExit] = CreateEvent(NULL, TRUE, FALSE, NULL);
fOk = (NULL != s_ahEvents[iEventExit]);
}
if (!fOk)
{
TerminateComm();
}
return(fOk);
}
/*---------------------------------------------------------------
*
* FUNCTION void TerminateComm()
*
* TYPE Global
*
* PURPOSE The function is called for the final shutdown of
* the comm port.
*
* INPUTS None
*
* RETURNS TRUE - Start Successful
* FALSE - Start Failed
*
*---------------------------------------------------------------*/
void TerminateComm()
{
BOOL fOk;
int i;
DBPRINTF(TEXT("TerminateComm()\r\n"));
StopComm();
for (i = 0; i < ARRAY_SIZE(s_ahEvents); ++i)
{
if (NULL != s_ahEvents[i])
{
fOk = CloseHandle(s_ahEvents[i]);
DBPRINTF_IF(fOk, TEXT("Unable to Close Event\r\n"));
s_ahEvents[i] = NULL;
}
}
return;
}
/*---------------------------------------------------------------
*
* FUNCTION BOOL StartComm()
*
* TYPE Global
*
* PURPOSE The function is call to start the thread to
* read and process data coming from the comm port.
* It will create a thread and an event. This function
* assumes that the comm port is already opened.
*
* INPUTS None
*
* RETURNS TRUE - Start Successful
* FALSE - Start Failed
*
*---------------------------------------------------------------*/
BOOL StartComm()
{
BOOL fOk = TRUE;
DWORD Id;
DBPRINTF(TEXT("StartComm()\r\n"));
// ----------------------------------------------------------
// Note: Comm Threads are started and stopped whenever
// the com port is changed. The User logs in or out
// or the comm configuration is changed.
// ----------------------------------------------------------
if (NULL == s_hFileComm && // no port currently in use
(skNewKey.dwFlags & SERKF_AVAILABLE) &&
(skNewKey.dwFlags & SERKF_SERIALKEYSON))
{
if (NULL != s_hThreadComm)
{
// This is an unexpected situation. We have the comm thread
// running with no open comm port. The thread must be hung.
// Let's close the open handle and forget about it.
DBPRINTF(TEXT("StartComm() unexpected (NULL != s_hThreadComm)\r\n"));
WaitForSingleObject(s_hThreadComm, 5 * 1000);
if (NULL != s_hThreadComm)
{
DBPRINTF(TEXT("StartComm() s_hThreadComm abandoned\r\n"));
CloseHandle(s_hThreadComm);
s_hThreadComm = NULL;
}
}
// skNewKey is used by OpenComm. We're setting skCurKey to default
// values in case OpenComm fails.
skCurKey.iBaudRate = 300; // No - Reset To Default Values
skCurKey.iPortState= 0;
skCurKey.dwFlags = 0;
lstrcpy(skCurKey.lpszActivePort, TEXT("COM1"));
lstrcpy(skCurKey.lpszPort, TEXT("COM1"));
if (!OpenComm()) // Did Comm Open Ok?
{
skNewKey.iBaudRate = 300; // No - Reset To Default Values
skNewKey.iPortState= 0;
skNewKey.dwFlags = 0;
lstrcpy(skNewKey.lpszActivePort, TEXT("COM1"));
lstrcpy(skNewKey.lpszPort, TEXT("COM1"));
fOk = FALSE;
}
else
{
// ensure we start with clean events
ResetEvent(s_ahEvents[iEventComm]);
ResetEvent(s_ahEvents[iEventExit]);
memset(&s_oRead, 0, sizeof(OVERLAPPED)); // Init Struct
s_oRead.hEvent = s_ahEvents[iEventComm]; // Store Event
// Create thread to handle Processing Comm Port
s_hThreadComm = (HANDLE)CreateThread( // Start Service Thread
0, 0,
(LPTHREAD_START_ROUTINE) ProcessComm,
0, 0,&Id); // argument to thread
if (NULL == s_hThreadComm)// Is Thread Handle Valid?
{
// Close out the Comm Port
SetCommState(s_hFileComm, &s_dcbCommOld); // Restore Comm State
CloseHandle(s_hFileComm);
s_hFileComm = NULL;
skCurKey.iPortState = 0;
fOk = FALSE;
}
else
{
// Comm Thread Successfully Started Set The Current Values
skCurKey.iBaudRate = skNewKey.iBaudRate;
skCurKey.iPortState = 2;
skCurKey.dwFlags = SERKF_SERIALKEYSON
| SERKF_AVAILABLE
| SERKF_ACTIVE;
lstrcpy(skCurKey.lpszActivePort, skNewKey.lpszActivePort);
lstrcpy(skCurKey.lpszPort, skNewKey.lpszActivePort);
DBPRINTF(TEXT("---- Comm Started\r\n"));
}
}
}
return(fOk);
}
/*---------------------------------------------------------------
*
* FUNCTION void SuspendComm()
*
* TYPE Global
*
* PURPOSE The function is called to Pause the thread
* reading and processing data coming from the comm port.
*
* INPUTS None
*
* RETURNS None
*
*---------------------------------------------------------------*/
void SuspendComm()
{
DBPRINTF(TEXT("SuspendComm()\r\n"));
if (NULL != s_hThreadComm)
{
SuspendThread(s_hThreadComm);
}
}
/*---------------------------------------------------------------
*
* FUNCTION void ResumeComm()
*
* TYPE Global
*
* PURPOSE The function is called to resume the Paused thread.
*
* INPUTS None
*
* RETURNS None
*
*---------------------------------------------------------------*/
void ResumeComm()
{
if (s_hThreadComm != NULL)
ResumeThread(s_hThreadComm);
}
/*---------------------------------------------------------------
*
* FUNCTION void StopComm()
*
* TYPE Global
*
* PURPOSE The function is called to stop the thread
* reading and processing data coming from the comm port.
*
* INPUTS None
*
* RETURNS TRUE - Start Successful
* FALSE - Start Failed
*
*---------------------------------------------------------------*/
void StopComm()
{
DBPRINTF(TEXT("StopComm()\r\n"));
if (NULL != s_hFileComm)
{
skCurKey.dwFlags = SERKF_AVAILABLE;
SetEvent(s_ahEvents[iEventExit]);
if (NULL != s_hThreadComm)
{
DWORD dwRet;
BOOL fOk;
dwRet = WaitForSingleObject(s_hThreadComm, 5 * 1000);
DBPRINTF_IF(WAIT_OBJECT_0 == dwRet, TEXT("StopComm() Comm Thread may be hung.\r\n"));
CloseHandle(s_hThreadComm);
s_hThreadComm = NULL;
SetCommState(s_hFileComm, &s_dcbCommOld); // Restore Comm State
fOk = CloseHandle(s_hFileComm); // Close the Comm Port
DBPRINTF_IF(fOk, TEXT("Unable to Close Comm File\r\n"));
s_hFileComm = NULL;
skCurKey.iPortState = 0;
}
}
}
/*---------------------------------------------------------------
*
* FUNCTION void SetCommBaud(int Baud)
*
* TYPE Global
*
* PURPOSE
*
*
* INPUTS None
*
* RETURNS TRUE - Start Successful
* FALSE - Start Failed
*
*---------------------------------------------------------------*/
void SetCommBaud(int Baud)
{
DBPRINTF(TEXT("SetCommBaud(%d)\r\n"), Baud);
switch (Baud) // Check for Valid Baud Rates
{
case 300:
case 600:
case 1200:
case 2400:
case 4800:
case 9600:
case 19200:
case 110:
case 14400:
case 38400:
case 56000:
case 57600:
case 115200:
break; // Baud Ok
default:
return; // Baud Invalid
}
skNewKey.iBaudRate = Baud; // Save Baud
if (NULL != s_hFileComm) // Is Comm Port Open?
{
s_dcbCommNew.BaudRate = skNewKey.iBaudRate; // Set new DCB Params
if (SetCommState(s_hFileComm, &s_dcbCommNew)) // State Change Ok?
{
skCurKey.iBaudRate = skNewKey.iBaudRate; // Save New Baud Rate
} else
{
DBPRINTF(TEXT("SetCommState(%d) FAILED!\r\n"), Baud);
// failed to set baud rate; try to revert it
s_dcbCommNew.BaudRate = skCurKey.iBaudRate; // reset DCB Params
if (!SetCommState(s_hFileComm, &s_dcbCommNew))
DBPRINTF(TEXT("SetCommState(%d) FAILED!\r\n"), skCurKey.iBaudRate);
}
}
}
/*---------------------------------------------------------------
*
* Local Functions
*
/*---------------------------------------------------------------
/*---------------------------------------------------------------
*
* FUNCTION void _CRTAPI1 ProcessComm()
*
* TYPE Local
*
* PURPOSE The function is the thread the cycles thru reading
* processing data coming from the comm port.
*
* INPUTS None
*
* RETURNS None
*
*---------------------------------------------------------------*/
static void __cdecl ProcessComm(VOID *notUsed)
{
int c;
HWINSTA hwinstaSave;
HWINSTA hwinstaUser;
HDESK hdeskSave;
DWORD dwThreadId;
BOOL fCont;
//------------------------------------------------------
//
// Note:
// The following code set the input focus to the current
// desktop. It is needed to insure that keyboard and mouse
// events will be passed to the current desktop.
//
//------------------------------------------------------
hwinstaSave = GetProcessWindowStation();
dwThreadId = GetCurrentThreadId();
hdeskSave = GetThreadDesktop(dwThreadId);
hwinstaUser = OpenWindowStation(TEXT("WinSta0"), FALSE, MAXIMUM_ALLOWED);
SetProcessWindowStation(hwinstaUser);
serialKeysStartUpInit(); // Initialize the Serial Keys
fCont = TRUE;
while (fCont)
{
c = ReadComm(); // Read Char from Com Port
switch (c)
{
case 0:
// Is Character a Null
// Is Null Timer > 30 Seconds
if ((GetTickCount() - s_NullTimer) > 30000)
{
s_NullTimer = GetTickCount(); // Yes - Reset Timer
s_NullCount = 1; // Reset Null Count
} else {
s_NullCount++; // No - Inc Null Count
if (s_NullCount == 3) // Have we had 3 Null in 30 Sec.?
{
// the user is requesting us to reset
SetCommBaud(300);
// DeskSwitch should be unnessary, but if it gets out of sync,
// this is where we resync
s_NullCount = 0; // Reset Null Counter
}
}
break;
case COMMTERMINATE:
fCont = FALSE;
break;
default:
DeskSwitchToInput();
serialKeysBegin((UCHAR)c); // Process Char
break;
}
}
SetThreadDesktop(hdeskSave);
SetProcessWindowStation(hwinstaSave);
CloseDesktop(s_hdeskUser);
s_hdeskUser = NULL;
CloseWindowStation(hwinstaUser);
ExitThread(0); // Close Thread
}
/*---------------------------------------------------------------
*
* BOOL IsCommPortName()
*
* Determines whether a given filename is a valid COM port name.
* Used by OpenComm so that it doesn't open a remote file or named
* pipe instead.
*
*---------------------------------------------------------------*/
static BOOL IsCommPortName( LPCTSTR pszFilename )
{
// Ensure that filename has form:
// COMn[n]\0
LPCTSTR pScan = pszFilename;
// Must start with COMn, where COM can be any case,
// and n is any 0..9 digit.
if( *pScan != 'C' && *pScan != 'c' )
return FALSE;
pScan++;
if( *pScan != 'O' && *pScan != 'o' )
return FALSE;
pScan++;
if( *pScan != 'M' && *pScan != 'm' )
return FALSE;
pScan++;
if( *pScan < '0' || *pScan > '9' )
return FALSE;
pScan++;
/*
// TODO: are COM54 really allowed?
// Optional second digit
if( *pScan >= '0' && *pScan <= '9' )
pScan++;
*/
// Manditory terminating nul
if( *pScan != '\0' )
return FALSE;
return TRUE;
}
/*---------------------------------------------------------------
*
* FUNCTION BOOL OpenComm()
*
* TYPE Local
*
* PURPOSE This Function opens the comm port and sets the new
* sets the Device Control Block.
*
* INPUTS None
*
* RETURNS TRUE - Open Ok / FALSE - Open Failed
*
*---------------------------------------------------------------*/
static BOOL OpenComm()
{
BOOL fOk = FALSE;
COMMTIMEOUTS ctmo;
// Check that the path we're given looks like a COM port.
// (Not, eg, a remote file or named pipe.)
if( ! IsCommPortName( skNewKey.lpszActivePort ) )
{
DBPRINTF(TEXT("- Not a COMn port\r\n"));
s_hFileComm = NULL;
return FALSE;
}
// The Security flags ensure that if we are duped into opening
// a named pipe, we'll do so anonymously, so that we can't be
// impersonated.
s_hFileComm = CreateFile(
skNewKey.lpszActivePort,// FileName (Com Port)
GENERIC_READ , // Access Mode
0, // Share Mode
NULL, // Address of Security Descriptor
OPEN_EXISTING, // How to Create
FILE_ATTRIBUTE_NORMAL // File Attributes
| FILE_FLAG_OVERLAPPED // Set for Async File Reads
| SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, // see above comment
NULL); // Templet File.
if (INVALID_HANDLE_VALUE == s_hFileComm) // File Ok?
{
DBPRINTF(TEXT("- Invalid File\r\n"));
s_hFileComm = NULL;
}
else
{
BOOL fRet;
COMMPROP cmmp;
SetupComm(
s_hFileComm,
1024, // size of input buffer
1024); // size of output buffer
memset(&s_dcbCommOld, 0, sizeof(s_dcbCommOld));
s_dcbCommOld.DCBlength = sizeof(s_dcbCommOld);
GetCommState(s_hFileComm, &s_dcbCommOld); // Save Old DCB for restore
s_dcbCommNew = s_dcbCommOld; // Copy to New
// set XoffLim and XonLim based on actual buffer size
fRet = GetCommProperties(s_hFileComm, &cmmp);
if (fRet)
{
s_dcbCommNew.XoffLim = (WORD)(cmmp.dwCurrentRxQueue / 4);
s_dcbCommNew.XonLim = (WORD)(cmmp.dwCurrentRxQueue / 4);
}
s_dcbCommNew.BaudRate = skNewKey.iBaudRate; // Set new DCB Params
s_dcbCommNew.ByteSize = 8;
s_dcbCommNew.Parity = NOPARITY;
s_dcbCommNew.StopBits = ONESTOPBIT;
s_dcbCommNew.fOutX = FALSE; // XOn/XOff used during transmission
s_dcbCommNew.fInX = TRUE; // XOn/XOff used during reception
s_dcbCommNew.fNull = FALSE; // tell windows not to strip nulls
s_dcbCommNew.fBinary = TRUE;
s_dcbCommNew.fOutxCtsFlow = FALSE;
s_dcbCommNew.fOutxDsrFlow = FALSE;
s_dcbCommNew.fDtrControl = DTR_CONTROL_ENABLE;
s_dcbCommNew.fDsrSensitivity = FALSE;
s_dcbCommNew.fErrorChar = TRUE;
s_dcbCommNew.fRtsControl = RTS_CONTROL_DISABLE;
s_dcbCommNew.fAbortOnError = FALSE;
s_dcbCommNew.XonChar = (char)0x11;
s_dcbCommNew.XoffChar = (char)0x13;
s_dcbCommNew.ErrorChar = '\0';
fOk = SetCommState(s_hFileComm, &s_dcbCommNew);
memset(&ctmo, 0, sizeof(ctmo));
SetCommTimeouts(s_hFileComm, &ctmo);
}
if (!fOk && NULL != s_hFileComm)
{
CloseHandle(s_hFileComm);
s_hFileComm = NULL;
}
return(fOk);
}
/*---------------------------------------------------------------
*
* FUNCTION int ReadComm()
*
* TYPE Local
*
* PURPOSE This Function reads a character from the comm port.
* If no character is present it wait on the HEV_COMM
* Event untill a character is present
*
* INPUTS None
*
* RETURNS int - Character read (-1 = Error Read)
*
*---------------------------------------------------------------*/
static int ReadComm()
{
int nRet;
DWORD cbRead = 0;
DWORD lastError, ComError;
DWORD dwRetWait;
BOOL fOk;
BOOL fExit;
BOOL fExitLoop = FALSE; // Boolean Flag to exit loop.
UCHAR uchBuff;
COMSTAT ComStat;
fExit = (WAIT_OBJECT_0 == WaitForSingleObject(s_ahEvents[iEventExit], 0));
if (!fExit)
{
fOk = ReadFile(s_hFileComm, &uchBuff, 1, &cbRead, &s_oRead);
if (!fOk) // Was there a Read Error?
{
lastError = GetLastError(); // This var can be useful for debugging
switch (lastError)
{
// If Error = IO_PENDING, wait til
// the event hadle signals success,
case ERROR_IO_PENDING:
dwRetWait = WaitForMultipleObjects(
ARRAY_SIZE(s_ahEvents), s_ahEvents, FALSE, INFINITE);
switch (dwRetWait - WAIT_OBJECT_0)
{
case iEventComm:
// this is the expected event
GetOverlappedResult(s_hFileComm, &s_oRead, &cbRead, FALSE);
if (cbRead < 1) // Did we read bytes;
{
// There was some error, return null
nRet = 0;
}
else
{
nRet = uchBuff;
}
break;
case iEventExit:
fExit = TRUE;
// fall through
default:
// this indicates and error and we exit to prevent loop
nRet = COMMTERMINATE;
break;
}
break;
default:
fOk = ClearCommError(s_hFileComm, &ComError,&ComStat);
if (fOk)
{
nRet = 0; // return a null
}
else
{
nRet = COMMTERMINATE; // terminate
}
break;
}
}
else
{
if (cbRead < 1) // Did we read bytes;
{
// There was some error, return null
nRet = 0;
}
else
{
nRet = uchBuff;
}
}
}
if (fExit)
{
ResetEvent(s_ahEvents[iEventExit]);
nRet = COMMTERMINATE;
}
return(nRet);
}