/*-------------------------------------------------------------- * * 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 #include #include #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); }