1441 lines
47 KiB
C++
1441 lines
47 KiB
C++
// 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.
|
|
//
|
|
// Copyright (C) 1995 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// MODULE: CommCode.c
|
|
//
|
|
// PURPOSE: Handles all the COMM routines for TapiComm.
|
|
//
|
|
// EXPORTED FUNCTIONS: These functions are for use by other modules.
|
|
// StartComm - Start communications.
|
|
// StopComm - Stop Communications.
|
|
// WriteCommString - Write a string to the Comm port.
|
|
//
|
|
// INTERNAL FUNCTION: These functions are for this module only.
|
|
// CloseReadThread - Close the Read Thread.
|
|
// CloseWriteThread - Close the Write Thread.
|
|
//
|
|
// StartReadThreadProc - Starting function for the Read Thread.
|
|
// StartWriteThreadProc - Starting function for the Write Thread.
|
|
//
|
|
// - Write Thread helper function
|
|
// HandleWriteData - Actually does the work of writing a string to comm.
|
|
//
|
|
// - Read Thread helper functions
|
|
// SetupReadEvent - Sets up the overlapped ReadFile
|
|
// HandleReadEvent - Gets the results from the overlapped ReadFile
|
|
// HandleReadData - Handles data returned from the ReadFile
|
|
//
|
|
// HandleCommEvent - Sets up the CommEvent event.
|
|
// SetupCommEvent - Handles CommEvent events (if they occur).
|
|
//
|
|
|
|
|
|
#include <windows.h>
|
|
#include <string.h>
|
|
#include "TapiCode.h"
|
|
#include "CommCode.h"
|
|
#include "globals.h"
|
|
#include "TapiInfo.h"
|
|
#include "dpspimp.h"
|
|
#include "logit.h"
|
|
|
|
// This is the message posted to the WriteThread
|
|
// When we have something to write.
|
|
|
|
// Default size of the Input Buffer used by this code.
|
|
#define INPUTBUFFERSIZE 2048
|
|
|
|
//*****************************************
|
|
// Global variables.
|
|
//*****************************************
|
|
|
|
volatile BOOL g_bIgnoreReads = FALSE;
|
|
volatile BOOL g_bRecovery = FALSE;
|
|
|
|
HANDLE g_hCommFile = NULL;
|
|
|
|
DWORD g_dwIOThreadID = 0;
|
|
HANDLE g_hIOThread = NULL;
|
|
|
|
HANDLE g_hCloseEvent = NULL;
|
|
HANDLE g_hDummyEvent1 = NULL;
|
|
HANDLE g_hDummyEvent2 = NULL;
|
|
HANDLE g_hReadEvent = NULL;
|
|
HANDLE g_hWriteEvent1 = NULL;
|
|
HANDLE g_hWriteEvent0 = NULL;
|
|
HANDLE g_hCommEvent = NULL;
|
|
|
|
CImpIDP_SP *g_IDP = NULL;
|
|
|
|
DWORD dwMsgs = 0;
|
|
typedef struct
|
|
{
|
|
BOOL bValid;
|
|
LPVOID lpv;
|
|
DWORD dwSize;
|
|
} WRITE_WAIT;
|
|
|
|
WRITE_WAIT writewait[2];
|
|
|
|
MSG_BUILDER msg_Building;
|
|
|
|
//*****************************************
|
|
// CommCode internal Function Prototypes
|
|
//*****************************************
|
|
|
|
void CloseReadThread();
|
|
void CloseWriteThread();
|
|
|
|
DWORD WINAPI StartIOThreadProc(LPVOID lpvParam);
|
|
|
|
|
|
BOOL HandleWriteData(LPOVERLAPPED lpOverlappedWrite,
|
|
LPCSTR lpszStringToWrite, DWORD dwNumberOfBytesToWrite);
|
|
|
|
|
|
BOOL SetupReadEvent(LPOVERLAPPED lpOverlappedRead,
|
|
LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
|
|
LPDWORD lpnNumberOfBytesRead);
|
|
BOOL HandleReadEvent(LPOVERLAPPED lpOverlappedRead,
|
|
LPSTR lpszInputBuffer, DWORD dwSizeofBuffer,
|
|
LPDWORD lpnNumberOfBytesRead);
|
|
BOOL HandleReadData(LPCSTR lpszInputBuffer, DWORD dwSizeofBuffer);
|
|
|
|
|
|
BOOL HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
|
|
LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent);
|
|
BOOL SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
|
|
LPDWORD lpfdwEvtMask);
|
|
|
|
|
|
|
|
#define WAIT_OBJECT_1 (WAIT_OBJECT_0 + 1)
|
|
#define WAIT_OBJECT_2 (WAIT_OBJECT_0 + 2)
|
|
#define WAIT_OBJECT_3 (WAIT_OBJECT_0 + 3)
|
|
#define WAIT_OBJECT_4 (WAIT_OBJECT_0 + 4)
|
|
#define WAIT_OBJECT_5 (WAIT_OBJECT_0 + 5)
|
|
|
|
|
|
volatile BOOL g_bCommStarted = FALSE;
|
|
|
|
//*****************************************
|
|
// Functions exported for use by other modules
|
|
//*****************************************
|
|
|
|
|
|
//
|
|
// FUNCTION: StartComm(HANDLE)
|
|
//
|
|
// PURPOSE: Starts communications over the comm port.
|
|
//
|
|
// PARAMETERS:
|
|
// hNewCommFile - This is the COMM File handle to communicate with.
|
|
// This handle is obtained from TAPI.
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE if able to setup the communications.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// StartComm makes sure there isn't communication in progress already,
|
|
// the hNewCommFile is valid, and all the threads can be created. It
|
|
// also configures the hNewCommFile for the appropriate COMM settings.
|
|
//
|
|
// If StartComm fails for any reason, it's up to the calling application
|
|
// to close the Comm file handle.
|
|
//
|
|
//
|
|
|
|
extern BOOL CreateQueue(DWORD dwElements, DWORD dwmaxMsg, DWORD dwmaxPlayers);
|
|
extern BOOL DeleteQueue();
|
|
|
|
BOOL _cdecl StartComm(HANDLE hNewCommFile, HANDLE hEvent)
|
|
{
|
|
// Is this a valid comm handle?
|
|
if (GetFileType(hNewCommFile) != FILE_TYPE_CHAR)
|
|
{
|
|
TSHELL_INFO(TEXT("File handle is not a comm handle."));
|
|
return FALSE;
|
|
}
|
|
|
|
// Are we already doing comm?
|
|
if (g_hCommFile != NULL)
|
|
{
|
|
TSHELL_INFO(TEXT("Already have a comm file open"));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
// Its ok to continue.
|
|
|
|
g_hCommFile = hNewCommFile;
|
|
|
|
// Setting and querying the comm port configurations.
|
|
|
|
{ // Configure the comm settings.
|
|
COMMTIMEOUTS commtimeouts;
|
|
DCB dcb;
|
|
COMMPROP commprop;
|
|
DWORD fdwEvtMask;
|
|
|
|
// These are here just so you can set a breakpoint
|
|
// and see what the comm settings are. Most Comm settings
|
|
// are already set through TAPI.
|
|
GetCommState(hNewCommFile, &dcb);
|
|
GetCommProperties(hNewCommFile, &commprop);
|
|
GetCommMask(g_hCommFile, &fdwEvtMask);
|
|
GetCommTimeouts(g_hCommFile, &commtimeouts);
|
|
|
|
|
|
// The CommTimeout numbers will very likely change if you are
|
|
// coding to meet some kind of specification where
|
|
// you need to reply within a certain amount of time after
|
|
// recieving the last byte. However, If 1/4th of a second
|
|
// goes by between recieving two characters, its a good
|
|
// indication that the transmitting end has finished, even
|
|
// assuming a 1200 baud modem.
|
|
|
|
commtimeouts.ReadIntervalTimeout = 250;
|
|
commtimeouts.ReadTotalTimeoutMultiplier = 0;
|
|
commtimeouts.ReadTotalTimeoutConstant = 0;
|
|
commtimeouts.WriteTotalTimeoutMultiplier = 0;
|
|
commtimeouts.WriteTotalTimeoutConstant = 0;
|
|
|
|
SetCommTimeouts(g_hCommFile, &commtimeouts);
|
|
|
|
// fAbortOnError is the only DCB dependancy in TapiComm.
|
|
// Can't guarentee that the SP will set this to what we expect.
|
|
dcb.fAbortOnError = FALSE;
|
|
SetCommState(hNewCommFile, &dcb);
|
|
}
|
|
|
|
// Create the events we need.
|
|
g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
g_hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
g_hDummyEvent1 = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
g_hDummyEvent2 = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
g_hWriteEvent1 = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
g_hWriteEvent0 = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
g_hCommEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
if ( !g_hCloseEvent
|
|
|| !g_hDummyEvent1
|
|
|| !g_hReadEvent
|
|
|| !g_hDummyEvent2
|
|
|| !g_hWriteEvent1
|
|
|| !g_hWriteEvent0
|
|
|| !g_hCommEvent)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("Unable to CreateEvent: %d"), GetLastError()));
|
|
g_hCommFile = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Create the Read thread.
|
|
g_hIOThread =
|
|
CreateThread(NULL, 0, StartIOThreadProc, 0, 0, &g_dwIOThreadID);
|
|
|
|
if (g_hIOThread == NULL)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("Unable to create IO thread: %d"), GetLastError()));
|
|
|
|
g_dwIOThreadID = 0;
|
|
g_hCommFile = 0;
|
|
|
|
if (g_hCloseEvent ) CloseHandle(g_hCloseEvent );
|
|
if (g_hReadEvent ) CloseHandle(g_hReadEvent );
|
|
if (g_hDummyEvent1) CloseHandle(g_hDummyEvent1);
|
|
if (g_hDummyEvent2) CloseHandle(g_hDummyEvent2);
|
|
if (g_hWriteEvent1) CloseHandle(g_hWriteEvent1);
|
|
if (g_hWriteEvent0) CloseHandle(g_hWriteEvent0);
|
|
if (g_hCommEvent ) CloseHandle(g_hCommEvent );
|
|
|
|
g_hCloseEvent = NULL;
|
|
g_hReadEvent = NULL;
|
|
g_hDummyEvent1 = NULL;
|
|
g_hDummyEvent2 = NULL;
|
|
g_hWriteEvent1 = NULL;
|
|
g_hWriteEvent0 = NULL;
|
|
g_hCommEvent = NULL;
|
|
return FALSE;
|
|
}
|
|
|
|
// Comm threads should to have a higher base priority than the UI thread.
|
|
// If they don't, then any temporary priority boost the UI thread gains
|
|
// could cause the COMM threads to loose data.
|
|
SetThreadPriority(g_hIOThread, THREAD_PRIORITY_HIGHEST);
|
|
|
|
g_bCommStarted = TRUE;
|
|
// Everything was created ok. Ready to go!
|
|
if (hEvent)
|
|
SetEvent(hEvent);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: StopComm
|
|
//
|
|
// PURPOSE: Stop and end all communication threads.
|
|
//
|
|
// PARAMETERS:
|
|
// none
|
|
//
|
|
// RETURN VALUE:
|
|
// none
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// Tries to gracefully signal all communication threads to
|
|
// close, but terminates them if it has to.
|
|
//
|
|
//
|
|
|
|
void _cdecl StopComm(HANDLE hEvent)
|
|
{
|
|
g_bCommStarted = FALSE;
|
|
// No need to continue if we're not communicating.
|
|
if (g_hCommFile == NULL)
|
|
return;
|
|
|
|
TSHELL_INFO(TEXT("Stopping the Comm."));
|
|
|
|
if (g_hIOThread)
|
|
{
|
|
|
|
TSHELL_INFO(TEXT("Closing Read Thread"));
|
|
|
|
// Signal the event to close the worker threads.
|
|
SetEvent(g_hCloseEvent);
|
|
|
|
// Purge all outstanding reads
|
|
PurgeComm(g_hCommFile, PURGE_RXABORT | PURGE_RXCLEAR
|
|
| PURGE_TXABORT | PURGE_TXCLEAR);
|
|
|
|
// Wait 10 seconds for it to exit. Shouldn't happen.
|
|
if (WaitForSingleObject(g_hIOThread, 10000) == WAIT_TIMEOUT)
|
|
{
|
|
TSHELL_INFO(TEXT("IO thread not exiting. Terminating it."));
|
|
|
|
TerminateThread(g_hIOThread, 0);
|
|
|
|
// The ReadThread cleans up these itself if it terminates
|
|
// normally.
|
|
CloseHandle(g_hIOThread);
|
|
g_hIOThread = 0;
|
|
g_dwIOThreadID = 0;
|
|
}
|
|
|
|
}
|
|
|
|
CloseHandle(g_hCloseEvent );
|
|
CloseHandle(g_hDummyEvent1);
|
|
CloseHandle(g_hDummyEvent2);
|
|
CloseHandle(g_hWriteEvent1);
|
|
CloseHandle(g_hWriteEvent0);
|
|
CloseHandle(g_hCommEvent );
|
|
|
|
g_hCloseEvent = NULL;
|
|
g_hDummyEvent1 = NULL;
|
|
g_hDummyEvent2 = NULL;
|
|
g_hWriteEvent1 = NULL;
|
|
g_hWriteEvent0 = NULL;
|
|
g_hCommEvent = NULL;
|
|
|
|
// Now close the comm port handle.
|
|
CloseHandle(g_hCommFile);
|
|
g_hCommFile = NULL;
|
|
if (hEvent)
|
|
SetEvent(hEvent);
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: WriteCommString(LPCSTR, DWORD)
|
|
//
|
|
// PURPOSE: Send a String to the Write Thread to be written to the Comm.
|
|
//
|
|
// PARAMETERS:
|
|
// pszStringToWrite - String to Write to Comm port.
|
|
// nSizeofStringToWrite - length of pszStringToWrite.
|
|
//
|
|
// RETURN VALUE:
|
|
// Returns TRUE if the PostMessage is successful.
|
|
// Returns FALSE if PostMessage fails or Write thread doesn't exist.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This is a wrapper function so that other modules don't care that
|
|
// Comm writing is done via PostMessage to a Write thread. Note that
|
|
// using PostMessage speeds up response to the UI (very little delay to
|
|
// 'write' a string) and provides a natural buffer if the comm is slow
|
|
// (ie: the messages just pile up in the message queue).
|
|
//
|
|
// Note that it is assumed that pszStringToWrite is allocated with
|
|
// LocalAlloc, and that if WriteCommString succeeds, its the job of the
|
|
// Write thread to LocalFree it. If WriteCommString fails, then its
|
|
// the job of the calling function to free the string.
|
|
//
|
|
//
|
|
|
|
BOOL WriteCommString(LPVOID lpszStringToWrite, DWORD dwSizeofStringToWrite)
|
|
{
|
|
if (g_hIOThread)
|
|
{
|
|
if (PostThreadMessage(g_dwIOThreadID, PWM_COMMWRITE,
|
|
(WPARAM) dwSizeofStringToWrite, (LPARAM) lpszStringToWrite))
|
|
{
|
|
if (g_IDP)
|
|
InterlockedIncrement((LPLONG) &g_IDP->m_dwPendingWrites);
|
|
return TRUE;
|
|
}
|
|
else
|
|
TSHELL_INFO(TEXT("Failed to Post to Write thread."));
|
|
}
|
|
else
|
|
TSHELL_INFO(TEXT("Write thread not created."));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
//*****************************************
|
|
// The rest of the functions are intended for use
|
|
// only within the CommCode module.
|
|
//*****************************************
|
|
|
|
|
|
|
|
//
|
|
// FUNCTION: StartIOThreadProc(LPVOID)
|
|
//
|
|
// PURPOSE: The starting point for the Write thread.
|
|
//
|
|
// PARAMETERS:
|
|
// lpvParam - unused.
|
|
//
|
|
// RETURN VALUE:
|
|
// DWORD - unused.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// The Write thread uses a PeekMessage loop to wait for a string to write,
|
|
// and when it gets one, it writes it to the Comm port. If the CloseEvent
|
|
// object is signaled, then it exits. The use of messages to tell the
|
|
// Write thread what to write provides a natural desynchronization between
|
|
// the UI and the Write thread.
|
|
//
|
|
//
|
|
|
|
typedef enum
|
|
{
|
|
READ_HDR,
|
|
READ_MSG,
|
|
READ_RECOVER_K,
|
|
READ_RECOVER_J,
|
|
READ_RECOVER_y,
|
|
READ_RECOVER_o,
|
|
READ_RECOVER_r,
|
|
READ_RECOVER_h,
|
|
READ_RECOVER_a,
|
|
READ_RECOVER_n,
|
|
READ_RECOVER_H,
|
|
READ_RECOVER_a2,
|
|
READ_RECOVER_l1,
|
|
READ_RECOVER_l2,
|
|
READ_GOOD_CONNECT_1,
|
|
RG2,
|
|
RG3,
|
|
RG4,
|
|
RG5,
|
|
RG6,
|
|
RG7,
|
|
RG8,
|
|
RG9,
|
|
RG10,
|
|
RG11,
|
|
RG12,
|
|
RG13,
|
|
RG14,
|
|
RG15,
|
|
RG16
|
|
} READ_STATE;
|
|
|
|
typedef enum
|
|
{
|
|
ZERO_PENDING,
|
|
ONE_PENDING,
|
|
TWO_PENDING
|
|
} WRITE_STATE;
|
|
|
|
#ifdef DEBUG
|
|
#define WAIT_ZERO_PENDING INFINITE
|
|
#else
|
|
#define WAIT_ZERO_PENDING 5000
|
|
#endif
|
|
|
|
DWORD WINAPI StartIOThreadProc(LPVOID lpvParam)
|
|
{
|
|
MSG msg;
|
|
DWORD dwHandleSignaled;
|
|
READ_STATE rState = READ_HDR;
|
|
WRITE_STATE wState = ZERO_PENDING;
|
|
HANDLE HandlesToWaitFor[5];
|
|
OVERLAPPED overlappedWrite0 = {0, 0, 0, 0, NULL};
|
|
OVERLAPPED overlappedWrite1 = {0, 0, 0, 0, NULL};
|
|
OVERLAPPED overlappedCommEvent = {0, 0, 0, 0, NULL};
|
|
OVERLAPPED overlappedRead = {0, 0, 0, 0, NULL};
|
|
DWORD dwCount;
|
|
DWORD dwReadCountExpected;
|
|
DWORD fdwEvtMask;
|
|
BOOL bb;
|
|
BOOL bFirst = TRUE;
|
|
DWORD dwTimeBegin = 0;
|
|
DWORD dwBadBegin = 0;
|
|
|
|
HandlesToWaitFor[0] = g_hCloseEvent;
|
|
HandlesToWaitFor[1] = g_hCommEvent;
|
|
HandlesToWaitFor[2] = g_hReadEvent;
|
|
|
|
|
|
overlappedRead.hEvent = g_hReadEvent;
|
|
overlappedCommEvent.hEvent = g_hCommEvent;
|
|
|
|
overlappedWrite0.hEvent = g_hWriteEvent0;
|
|
overlappedWrite1.hEvent = g_hWriteEvent1;
|
|
writewait[0].bValid = FALSE;
|
|
writewait[0].lpv = NULL;
|
|
writewait[0].dwSize = 0;
|
|
writewait[1].bValid = FALSE;
|
|
writewait[1].lpv = NULL;
|
|
writewait[1].dwSize = 0;
|
|
|
|
// Setup CommEvent handling.
|
|
|
|
// Set the comm mask so we receive error signals.
|
|
if (!SetCommMask(g_hCommFile, EV_ERR))
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("Unable to SetCommMask: %d"), GetLastError()));
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
|
|
// Start waiting for CommEvents (Errors)
|
|
if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask))
|
|
{
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
|
|
|
|
dwCount = sizeof(DPHDR);
|
|
dwReadCountExpected = dwCount;
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
|
|
TSHELL_INFO(TEXT("Comm started so release waiting operations."));
|
|
g_IDP->SetBlock();
|
|
|
|
while (TRUE)
|
|
{
|
|
switch(wState)
|
|
{
|
|
case ZERO_PENDING:
|
|
{
|
|
HandlesToWaitFor[3] = g_hDummyEvent1;
|
|
HandlesToWaitFor[4] = g_hWriteEvent1;
|
|
|
|
// TSHELL_INFO(TEXT("Wait with 0 Pending."));
|
|
dwHandleSignaled =
|
|
MsgWaitForMultipleObjects(5, HandlesToWaitFor, FALSE,
|
|
WAIT_ZERO_PENDING, QS_ALLINPUT);
|
|
}
|
|
break;
|
|
case ONE_PENDING:
|
|
{
|
|
// TSHELL_INFO(TEXT("Wait with 1 Pending."));
|
|
if (writewait[0].bValid)
|
|
{
|
|
HandlesToWaitFor[3] = g_hWriteEvent0;
|
|
HandlesToWaitFor[4] = g_hDummyEvent2;
|
|
|
|
}
|
|
else
|
|
{
|
|
HandlesToWaitFor[3] = g_hDummyEvent1;
|
|
HandlesToWaitFor[4] = g_hWriteEvent1;
|
|
}
|
|
|
|
dwHandleSignaled =
|
|
MsgWaitForMultipleObjects(5, HandlesToWaitFor, FALSE,
|
|
INFINITE, QS_ALLINPUT);
|
|
|
|
}
|
|
break;
|
|
case TWO_PENDING:
|
|
{
|
|
// TSHELL_INFO(TEXT("Wait with 2 Pending."));
|
|
HandlesToWaitFor[3] = g_hWriteEvent0;
|
|
HandlesToWaitFor[4] = g_hWriteEvent1;
|
|
|
|
dwHandleSignaled =
|
|
WaitForMultipleObjects(5, HandlesToWaitFor, FALSE,
|
|
INFINITE);
|
|
}
|
|
break;
|
|
default:
|
|
TSHELL_INFO(TEXT("Invalid Write State"));
|
|
|
|
}
|
|
|
|
// DBG_INFO((DBGARG, TEXT("IO Thread Woken up. %8x"), dwHandleSignaled));
|
|
switch (dwHandleSignaled)
|
|
{
|
|
case WAIT_TIMEOUT:
|
|
//
|
|
// Do to what I believe are problems with MsgWait() we will
|
|
// time out MsgWait() at 1000msec if in the zero pending state.
|
|
//
|
|
break;
|
|
case WAIT_OBJECT_0: // CloseEvent
|
|
{
|
|
TSHELL_INFO(TEXT("Close Event recieved."));
|
|
//
|
|
// Cleanup and Exit
|
|
//
|
|
return(0);
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_1: // CommEvent
|
|
{
|
|
TSHELL_INFO(TEXT("CommEvent Recieved."));
|
|
// Handle the CommEvent.
|
|
if (!HandleCommEvent(&overlappedCommEvent, &fdwEvtMask, TRUE))
|
|
{
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
|
|
// Start waiting for the next CommEvent.
|
|
if (!SetupCommEvent(&overlappedCommEvent, &fdwEvtMask))
|
|
{
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_2: // ReadEvent
|
|
{
|
|
// TSHELL_INFO(TEXT("ReadEvent Recieved."));
|
|
|
|
bb = GetOverlappedResult(g_hCommFile, &overlappedRead, &dwCount, FALSE);
|
|
DBG_INFO((DBGARG, TEXT("Read expected %d and got %d on result %d"),
|
|
dwReadCountExpected, dwCount, bb));
|
|
if (dwCount == dwReadCountExpected)
|
|
{
|
|
if (rState == READ_HDR)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("Read Hdr to(%d) from(%d) count (%d) cookie(%d) All(%8x)"),
|
|
msg_Building.dpHdr.to,
|
|
msg_Building.dpHdr.from,
|
|
msg_Building.dpHdr.usCount,
|
|
msg_Building.dpHdr.usCookie,
|
|
msg_Building.dpHdr.dwConnect1));
|
|
|
|
if ( msg_Building.dpHdr.usCookie == SPSYS_USER
|
|
|| msg_Building.dpHdr.usCookie == SPSYS_SYS
|
|
|| msg_Building.dpHdr.usCookie == SPSYS_HIGH
|
|
|| msg_Building.dpHdr.usCookie == SPSYS_CONNECT)
|
|
{
|
|
// TSHELL_INFO(TEXT("Got a valid msg header, look for body."));
|
|
rState = READ_MSG;
|
|
dwReadCountExpected = msg_Building.dpHdr.usCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, msg_Building.chMsgCompose,
|
|
dwReadCountExpected, &dwCount, &overlappedRead);
|
|
}
|
|
else if ( msg_Building.dpHdr.dwConnect1 == DPSYS_JOHN)
|
|
{
|
|
SPMSG_CONNECT *pMsg;
|
|
|
|
TSHELL_INFO(TEXT("Other end needs recover ssync."));
|
|
pMsg = (SPMSG_CONNECT *) LocalAlloc(LMEM_FIXED, sizeof(SPMSG_CONNECT));
|
|
if (pMsg)
|
|
{
|
|
pMsg->dpHdr.to = 0;
|
|
pMsg->dpHdr.from = 0;
|
|
pMsg->dpHdr.usCount = sizeof(SPMSG_CONNECT) - sizeof(DPHDR);
|
|
pMsg->dpHdr.usCookie = SPSYS_CONNECT;
|
|
pMsg->usVerMajor = DPVERSION_MAJOR;
|
|
pMsg->usVerMinor = DPVERSION_MINOR;
|
|
pMsg->dwConnect1 = DPSYS_KYRA;
|
|
pMsg->dwConnect2 = DPSYS_HALL;
|
|
WriteCommString(pMsg, sizeof(SPMSG_CONNECT));
|
|
dwCount = sizeof(DPHDR);
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
}
|
|
else
|
|
{
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DPHDR *pRecover;
|
|
|
|
TSHELL_INFO(TEXT("Bad header type. Enter recover state."));
|
|
|
|
if (bFirst)
|
|
{
|
|
PurgeComm(g_hCommFile, PURGE_RXCLEAR);
|
|
rState = READ_GOOD_CONNECT_1;
|
|
dwCount = 1;
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
dwTimeBegin = GetTickCount();
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
}
|
|
else
|
|
{
|
|
pRecover = (DPHDR *) LocalAlloc(LMEM_FIXED, sizeof(DPHDR));
|
|
if (pRecover)
|
|
{
|
|
g_bRecovery = TRUE;
|
|
pRecover->dwConnect1 = DPSYS_JOHN;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Send John 1 %8x"), pRecover));
|
|
|
|
WriteCommString(pRecover, sizeof(DPHDR));
|
|
PurgeComm(g_hCommFile, PURGE_RXCLEAR);
|
|
rState = READ_RECOVER_K;
|
|
dwCount = 1;
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
}
|
|
else
|
|
{
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (rState == READ_MSG)
|
|
{
|
|
bFirst = FALSE;
|
|
g_IDP->HandleMessage((LPVOID) &msg_Building, dwReadCountExpected);
|
|
rState = READ_HDR;
|
|
dwCount = sizeof(DPHDR);
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
}
|
|
else if (rState >= READ_GOOD_CONNECT_1)
|
|
{
|
|
char ch = *((CHAR *) (&msg_Building.dpHdr));
|
|
BOOL bSuccess = FALSE;
|
|
|
|
DBG_INFO((DBGARG, TEXT("First Connect state loop. %x, State %d"), 0x000000ff & ch, rState));
|
|
|
|
switch( rState)
|
|
{
|
|
case READ_GOOD_CONNECT_1:
|
|
rState = (ch == 0x00) ? RG2 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG2:
|
|
rState = (ch == 0x00) ? RG3 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG3 :
|
|
if (ch == 0x0c)
|
|
rState = RG4;
|
|
else if (ch == 0x00)
|
|
rState = RG3;
|
|
else
|
|
rState = READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG4 :
|
|
rState = (ch == 0x3c) ? RG5 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG5 :
|
|
rState = (ch == 0x01) ? RG6 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG6 :
|
|
rState = (ch == 0x00) ? RG7 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG7 :
|
|
rState = (ch == 0x01) ? RG8 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG8 :
|
|
rState = (ch == 0x00) ? RG9 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG9 :
|
|
rState = (ch == 0x4b) ? RG10 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG10:
|
|
rState = (ch == 0x79) ? RG11 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG11:
|
|
rState = (ch == 0x72) ? RG12 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG12:
|
|
rState = (ch == 0x61) ? RG13 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG13:
|
|
rState = (ch == 0x48) ? RG14 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG14:
|
|
rState = (ch == 0x61) ? RG15 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG15:
|
|
rState = (ch == 0x6c) ? RG16 : READ_GOOD_CONNECT_1;
|
|
break;
|
|
case RG16:
|
|
rState = (ch == 0x6c) ? READ_HDR : READ_GOOD_CONNECT_1;
|
|
break;
|
|
default:
|
|
rState = READ_GOOD_CONNECT_1;
|
|
}
|
|
|
|
ResetEvent(g_hReadEvent);
|
|
if (rState == READ_HDR)
|
|
{
|
|
SPMSG_CONNECT *pConnect;
|
|
pConnect = (SPMSG_CONNECT *) &msg_Building;
|
|
|
|
pConnect->dpHdr.to = 0;
|
|
pConnect->dpHdr.from = 0;
|
|
pConnect->dpHdr.usCount = sizeof(SPMSG_CONNECT) - sizeof(DPHDR);
|
|
pConnect->dpHdr.usCookie = SPSYS_CONNECT;
|
|
pConnect->usVerMajor = DPVERSION_MAJOR;
|
|
pConnect->usVerMinor = DPVERSION_MINOR;
|
|
pConnect->dwConnect1 = DPSYS_KYRA;
|
|
pConnect->dwConnect2 = DPSYS_HALL;
|
|
|
|
g_IDP->HandleMessage((LPVOID) &msg_Building, sizeof(SPMSG_CONNECT));
|
|
dwCount = sizeof(READ_HDR);
|
|
dwReadCountExpected = dwCount;
|
|
|
|
TSHELL_INFO(TEXT("Sweet recovery, I hope."));
|
|
DBG_INFO(( DBGARG, TEXT("Garbage process %d ticks"),
|
|
GetTickCount() - dwTimeBegin));
|
|
}
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
}
|
|
else
|
|
{
|
|
char ch = *((CHAR *) (&msg_Building.dpHdr));
|
|
BOOL bSuccess = FALSE;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Recover state loop. %x"), 0x000000ff & ch));
|
|
|
|
switch (ch)
|
|
{
|
|
case 'K':
|
|
if (rState == READ_RECOVER_K)
|
|
rState = READ_RECOVER_y;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'y':
|
|
if (rState == READ_RECOVER_y)
|
|
rState = READ_RECOVER_r;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'r':
|
|
if (rState == READ_RECOVER_r)
|
|
rState = READ_RECOVER_a;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'a':
|
|
if (rState == READ_RECOVER_a)
|
|
rState = READ_RECOVER_H;
|
|
else if (rState == READ_RECOVER_a2)
|
|
rState = READ_RECOVER_l1;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'H':
|
|
if (rState == READ_RECOVER_H)
|
|
rState = READ_RECOVER_a2;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'l':
|
|
if (rState == READ_RECOVER_l1)
|
|
rState = READ_RECOVER_l2;
|
|
else if (rState == READ_RECOVER_l2)
|
|
{
|
|
SPMSG_CONNECT *pMsg;
|
|
|
|
pMsg = (SPMSG_CONNECT *) LocalAlloc(LMEM_FIXED, sizeof(SPMSG_CONNECT));
|
|
|
|
if (pMsg)
|
|
{
|
|
pMsg->dpHdr.to = 0;
|
|
pMsg->dpHdr.from = 0;
|
|
pMsg->dpHdr.usCount = sizeof(SPMSG_CONNECT) - sizeof(DPHDR);
|
|
pMsg->dpHdr.usCookie = SPSYS_CONNECT;
|
|
pMsg->usVerMajor = DPVERSION_MAJOR;
|
|
pMsg->usVerMinor = DPVERSION_MINOR;
|
|
pMsg->dwConnect1 = DPSYS_KYRA;
|
|
pMsg->dwConnect2 = DPSYS_HALL;
|
|
WriteCommString(pMsg, sizeof(SPMSG_CONNECT));
|
|
bSuccess = TRUE;
|
|
g_bRecovery = FALSE;
|
|
}
|
|
else
|
|
{
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
}
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'J':
|
|
if (rState == READ_RECOVER_J)
|
|
rState = READ_RECOVER_o;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'o':
|
|
if (rState == READ_RECOVER_o)
|
|
rState = READ_RECOVER_h;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'h':
|
|
if (rState == READ_RECOVER_h)
|
|
rState = READ_RECOVER_n;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
case 'n':
|
|
if (rState == READ_RECOVER_n)
|
|
bSuccess = TRUE;
|
|
else
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
|
|
default:
|
|
rState = READ_RECOVER_K;
|
|
break;
|
|
}
|
|
if (bSuccess)
|
|
{
|
|
rState = READ_HDR;
|
|
dwCount = sizeof(DPHDR);
|
|
}
|
|
else
|
|
{
|
|
dwCount = 1;
|
|
}
|
|
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPHDR *pRecover;
|
|
|
|
if (g_bIgnoreReads)
|
|
{
|
|
TSHELL_INFO(TEXT("Ignore extraneous reads before we send something."));
|
|
dwBadBegin++;
|
|
if (dwBadBegin % 10)
|
|
{
|
|
TSHELL_INFO(TEXT("Bad Byte again."));
|
|
}
|
|
|
|
dwCount = sizeof(DPHDR);
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
break;
|
|
}
|
|
|
|
if (bFirst)
|
|
{
|
|
TSHELL_INFO(TEXT("Got bad data before we read anything. Look for Good Connect."));
|
|
PurgeComm(g_hCommFile, PURGE_RXCLEAR);
|
|
rState = READ_GOOD_CONNECT_1;
|
|
dwTimeBegin = GetTickCount();
|
|
dwCount = 1;
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
break;
|
|
}
|
|
|
|
|
|
TSHELL_INFO(TEXT("Recover from bad read count."));
|
|
DBG_INFO((DBGARG, TEXT("Bad Packet Begins %8x, Read %d Expected %d State %d"),
|
|
&msg_Building, dwCount, dwReadCountExpected, rState));
|
|
|
|
DBG_INFO((DBGARG, TEXT("Read Bad Hdr to(%d) from(%d) count (%d) cookie(%d) All(%8x)"),
|
|
msg_Building.dpHdr.to,
|
|
msg_Building.dpHdr.from,
|
|
msg_Building.dpHdr.usCount,
|
|
msg_Building.dpHdr.usCookie,
|
|
msg_Building.dpHdr.dwConnect1));
|
|
|
|
pRecover = (DPHDR *) LocalAlloc(LMEM_FIXED, sizeof(DPHDR));
|
|
|
|
if (pRecover)
|
|
{
|
|
g_bRecovery = TRUE;
|
|
pRecover->dwConnect1 = DPSYS_JOHN;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Send John 1 %8x"), pRecover));
|
|
|
|
WriteCommString(pRecover, sizeof(DPHDR));
|
|
PurgeComm(g_hCommFile, PURGE_RXCLEAR);
|
|
rState = READ_RECOVER_K;
|
|
dwCount = 1;
|
|
dwReadCountExpected = dwCount;
|
|
ResetEvent(g_hReadEvent);
|
|
ReadFile(g_hCommFile, &msg_Building.dpHdr, dwCount, &dwCount, &overlappedRead);
|
|
}
|
|
else
|
|
{
|
|
PostHangupCall();
|
|
return(0);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_3: // Write Buffer 1 completion.
|
|
{
|
|
// TSHELL_INFO(TEXT("Write Buffer1 received."));
|
|
if (writewait[0].bValid == TRUE)
|
|
{
|
|
GetOverlappedResult(g_hCommFile, &overlappedWrite0, &dwCount, FALSE);
|
|
if (writewait[0].dwSize == dwCount)
|
|
{
|
|
//
|
|
// Success.
|
|
//
|
|
writewait[0].bValid = FALSE;
|
|
DBG_INFO((DBGARG, TEXT("Free 0 %8x"), writewait[0].lpv));
|
|
|
|
LocalFree((HLOCAL) writewait[0].lpv);
|
|
if (wState == TWO_PENDING)
|
|
{
|
|
wState = ONE_PENDING;
|
|
}
|
|
else if (wState == ONE_PENDING)
|
|
{
|
|
wState = ZERO_PENDING;
|
|
}
|
|
//
|
|
// state of zero pending would be a fatal error. BUGBUG;
|
|
//
|
|
}
|
|
else
|
|
{
|
|
writewait[0].bValid = FALSE;
|
|
writewait[0].bValid = FALSE;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Free Err 0 %8x"), writewait[0].lpv));
|
|
|
|
LocalFree((HLOCAL) writewait[0].lpv);
|
|
if (wState == TWO_PENDING)
|
|
{
|
|
wState = ONE_PENDING;
|
|
}
|
|
else if (wState == ONE_PENDING)
|
|
{
|
|
wState = ZERO_PENDING;
|
|
}
|
|
DBG_INFO((DBGARG, TEXT("Write Error Tried %d Wrote %d"),
|
|
writewait[0].dwSize, dwCount));
|
|
}
|
|
}
|
|
//
|
|
// if bValid not TRUE error. BUGBUG.
|
|
//
|
|
ResetEvent(g_hWriteEvent0);
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_4: // Write Buffer 2 completion.
|
|
{
|
|
// TSHELL_INFO(TEXT("Write Buffer2 received."));
|
|
if (writewait[1].bValid == TRUE)
|
|
{
|
|
GetOverlappedResult(g_hCommFile, &overlappedWrite1, &dwCount, FALSE);
|
|
if (writewait[1].dwSize == dwCount)
|
|
{
|
|
//
|
|
// Success.
|
|
//
|
|
writewait[1].bValid = FALSE;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Free 1 %8x"), writewait[1].lpv));
|
|
|
|
LocalFree((HLOCAL) writewait[1].lpv);
|
|
if (wState == TWO_PENDING)
|
|
{
|
|
wState = ONE_PENDING;
|
|
}
|
|
else if (wState == ONE_PENDING)
|
|
{
|
|
wState = ZERO_PENDING;
|
|
}
|
|
//
|
|
// state of zero pending would be a fatal error. BUGBUG;
|
|
//
|
|
}
|
|
else
|
|
{
|
|
writewait[1].bValid = FALSE;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Free Err 1 %8x"), writewait[1].lpv));
|
|
|
|
LocalFree((HLOCAL) writewait[1].lpv);
|
|
if (wState == TWO_PENDING)
|
|
{
|
|
wState = ONE_PENDING;
|
|
}
|
|
else if (wState == ONE_PENDING)
|
|
{
|
|
wState = ZERO_PENDING;
|
|
}
|
|
DBG_INFO((DBGARG, TEXT("Write Error Tried %d Wrote %d"),
|
|
writewait[0].dwSize, dwCount));
|
|
}
|
|
}
|
|
//
|
|
// if bValid not TRUE error. BUGBUG.
|
|
//
|
|
ResetEvent(g_hWriteEvent1);
|
|
}
|
|
break;
|
|
|
|
case WAIT_OBJECT_5: // More Write Requests.
|
|
//
|
|
// It turns out that out MsgWait function won't return
|
|
// if we have a current message in the loop, only if
|
|
// we get a new one.
|
|
//
|
|
// That has to be bullshit, since we have a window
|
|
// of opportunity after we do our peek's manually and
|
|
// before we do a MsgWait().
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Remember that loop conditional doesn't execute PeekMessage()
|
|
// if the state is already TWO_PENDING. Don't change it or
|
|
// you'll lose a message.
|
|
//
|
|
while (wState != TWO_PENDING && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
|
|
{
|
|
if (msg.hwnd != NULL || msg.message != PWM_COMMWRITE)
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
else
|
|
{
|
|
if (g_IDP)
|
|
InterlockedDecrement((LPLONG) &g_IDP->m_dwPendingWrites);
|
|
|
|
DBG_INFO((DBGARG, TEXT("Write Hdr to(%d) from(%d) count (%d) cookie(%d): %8x %d"),
|
|
((DPHDR *)msg.lParam)->to,
|
|
((DPHDR *)msg.lParam)->from,
|
|
((DPHDR *)msg.lParam)->usCount,
|
|
((DPHDR *)msg.lParam)->usCookie,
|
|
((DPHDR *)msg.lParam)->dwConnect1,
|
|
rState));
|
|
|
|
|
|
if (writewait[0].bValid == FALSE)
|
|
{
|
|
writewait[0].bValid = TRUE;
|
|
writewait[0].lpv = (LPVOID) msg.lParam;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Set 0 %8x"), writewait[0].lpv));
|
|
|
|
writewait[0].dwSize = msg.wParam;
|
|
WriteFile( g_hCommFile, (LPVOID) msg.lParam, msg.wParam,
|
|
&dwCount, &overlappedWrite0);
|
|
}
|
|
else
|
|
{
|
|
writewait[1].bValid = TRUE;
|
|
writewait[1].lpv = (LPVOID) msg.lParam;
|
|
|
|
DBG_INFO((DBGARG, TEXT("Set 1 %8x"), writewait[1].lpv));
|
|
|
|
writewait[1].dwSize = msg.wParam;
|
|
WriteFile( g_hCommFile, (LPVOID) msg.lParam, msg.wParam,
|
|
&dwCount, &overlappedWrite1);
|
|
}
|
|
}
|
|
if (wState == ZERO_PENDING)
|
|
{
|
|
wState = ONE_PENDING;
|
|
}
|
|
else if (wState == ONE_PENDING)
|
|
{
|
|
wState = TWO_PENDING;
|
|
}
|
|
//
|
|
// BUGBUG state two_pending is illegal.
|
|
//
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// FUNCTION: SetupCommEvent(LPOVERLAPPED, LPDWORD)
|
|
//
|
|
// PURPOSE: Sets up the overlapped WaitCommEvent call.
|
|
//
|
|
// PARAMETERS:
|
|
// lpOverlappedCommEvent - Pointer to the overlapped structure to use.
|
|
// lpfdwEvtMask - Pointer to DWORD to received Event data.
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE if able to successfully setup the WaitCommEvent.
|
|
// FALSE if unable to setup WaitCommEvent, unable to handle
|
|
// an existing outstanding event or if the CloseEvent has been signaled.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a helper function for the Read Thread that sets up
|
|
// the WaitCommEvent so we can deal with comm events (like Comm errors)
|
|
// if they occur.
|
|
//
|
|
//
|
|
|
|
BOOL SetupCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
|
|
LPDWORD lpfdwEvtMask)
|
|
{
|
|
DWORD dwLastError;
|
|
|
|
StartSetupCommEvent:
|
|
|
|
// Make sure the CloseEvent hasn't been signaled yet.
|
|
// Check is needed because this function is potentially recursive.
|
|
if (WAIT_TIMEOUT != WaitForSingleObject(g_hCloseEvent,0))
|
|
return FALSE;
|
|
|
|
// Start waiting for Comm Errors.
|
|
if (WaitCommEvent(g_hCommFile, lpfdwEvtMask, lpOverlappedCommEvent))
|
|
{
|
|
// This could happen if there was an error waiting on the
|
|
// comm port. Lets try and handle it.
|
|
|
|
TSHELL_INFO(TEXT("Event (Error) waiting before WaitCommEvent."));
|
|
|
|
if (!HandleCommEvent(NULL, lpfdwEvtMask, FALSE))
|
|
return FALSE;
|
|
|
|
// What could cause infinite recursion at this point?
|
|
goto StartSetupCommEvent;
|
|
}
|
|
|
|
// We expect ERROR_IO_PENDING returned from WaitCommEvent
|
|
// because we are waiting with an overlapped structure.
|
|
|
|
dwLastError = GetLastError();
|
|
|
|
// LastError was ERROR_IO_PENDING, as expected.
|
|
if (dwLastError == ERROR_IO_PENDING)
|
|
{
|
|
TSHELL_INFO(TEXT("Waiting for a CommEvent (Error) to occur."));
|
|
return TRUE;
|
|
}
|
|
|
|
// Its possible for this error to occur if the
|
|
// service provider has closed the port. Time to end.
|
|
if (dwLastError == ERROR_INVALID_HANDLE)
|
|
{
|
|
TSHELL_INFO(TEXT("ERROR_INVALID_HANDLE, Likely that the Service Provider has closed the port."));
|
|
return FALSE;
|
|
}
|
|
|
|
// Unexpected error. No idea what could cause this to happen.
|
|
TSHELL_INFO(TEXT("Unexpected WaitCommEvent error: "));
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// FUNCTION: HandleCommEvent(LPOVERLAPPED, LPDWORD, BOOL)
|
|
//
|
|
// PURPOSE: Handle an outstanding Comm Event.
|
|
//
|
|
// PARAMETERS:
|
|
// lpOverlappedCommEvent - Pointer to the overlapped structure to use.
|
|
// lpfdwEvtMask - Pointer to DWORD to received Event data.
|
|
// fRetrieveEvent - Flag to signal if the event needs to be
|
|
// retrieved, or has already been retrieved.
|
|
//
|
|
// RETURN VALUE:
|
|
// TRUE if able to handle a Comm Event.
|
|
// FALSE if unable to setup WaitCommEvent, unable to handle
|
|
// an existing outstanding event or if the CloseEvent has been signaled.
|
|
//
|
|
// COMMENTS:
|
|
//
|
|
// This function is a helper function for the Read Thread that (if
|
|
// fRetrieveEvent == TRUE) retrieves an outstanding CommEvent and
|
|
// deals with it. The only event that should occur is an EV_ERR event,
|
|
// signalling that there has been an error on the comm port.
|
|
//
|
|
// Normally, comm errors would not be put into the normal data stream
|
|
// as this sample is demonstrating. Putting it in a status bar would
|
|
// be more appropriate for a real application.
|
|
//
|
|
//
|
|
|
|
BOOL HandleCommEvent(LPOVERLAPPED lpOverlappedCommEvent,
|
|
LPDWORD lpfdwEvtMask, BOOL fRetrieveEvent)
|
|
{
|
|
DWORD dwDummy;
|
|
LPSTR lpszOutput;
|
|
char szError[128] = "";
|
|
DWORD dwErrors;
|
|
DWORD dwLastError;
|
|
|
|
|
|
lpszOutput = (char *) LocalAlloc(LPTR,256);
|
|
if (lpszOutput == NULL)
|
|
{
|
|
DBG_INFO((DBGARG, TEXT("LocalAlloc: %d"), GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
// If this fails, it could be because the file was closed (and I/O is
|
|
// finished) or because the overlapped I/O is still in progress. In
|
|
// either case (or any others) its a bug and return FALSE.
|
|
if (fRetrieveEvent)
|
|
if (!GetOverlappedResult(g_hCommFile,
|
|
lpOverlappedCommEvent, &dwDummy, FALSE))
|
|
{
|
|
dwLastError = GetLastError();
|
|
|
|
// Its possible for this error to occur if the
|
|
// service provider has closed the port. Time to end.
|
|
if (dwLastError == ERROR_INVALID_HANDLE)
|
|
{
|
|
TSHELL_INFO(TEXT("ERROR_INVALID_HANDLE, Likely that the Service Provider has closed the port."));
|
|
return FALSE;
|
|
}
|
|
|
|
DBG_INFO((DBGARG, TEXT("Unexpected GetOverlappedResult for WaitCommEvent: %x"),
|
|
dwLastError));
|
|
return FALSE;
|
|
}
|
|
|
|
// Was the event an error?
|
|
if (*lpfdwEvtMask & EV_ERR)
|
|
{
|
|
// Which error was it?
|
|
if (!ClearCommError(g_hCommFile, &dwErrors, NULL))
|
|
{
|
|
dwLastError = GetLastError();
|
|
|
|
// Its possible for this error to occur if the
|
|
// service provider has closed the port. Time to end.
|
|
if (dwLastError == ERROR_INVALID_HANDLE)
|
|
{
|
|
TSHELL_INFO(TEXT("ERROR_INVALID_HANDLE, Likely that the Service Provider has closed the port."));
|
|
return FALSE;
|
|
}
|
|
|
|
DBG_INFO((DBGARG, TEXT("ClearCommError: %x"), GetLastError()));
|
|
return FALSE;
|
|
}
|
|
|
|
// Its possible that multiple errors occured and were handled
|
|
// in the last ClearCommError. Because all errors were signaled
|
|
// individually, but cleared all at once, pending comm events
|
|
// can yield EV_ERR while dwErrors equals 0. Ignore this event.
|
|
if (dwErrors == 0)
|
|
{
|
|
lstrcat(szError, TEXT("NULL Error"));
|
|
}
|
|
|
|
if (dwErrors & CE_FRAME)
|
|
{
|
|
if (szError[0])
|
|
lstrcat(szError,TEXT(" and "));
|
|
|
|
lstrcat(szError,TEXT("CE_FRAME"));
|
|
}
|
|
|
|
if (dwErrors & CE_OVERRUN)
|
|
{
|
|
if (szError[0])
|
|
lstrcat(szError,TEXT(" and "));
|
|
|
|
lstrcat(szError,TEXT("CE_OVERRUN"));
|
|
}
|
|
|
|
if (dwErrors & CE_RXPARITY)
|
|
{
|
|
if (szError[0])
|
|
lstrcat(szError,TEXT(" and "));
|
|
|
|
lstrcat(szError,TEXT("CE_RXPARITY"));
|
|
}
|
|
|
|
if (dwErrors & ~ (CE_FRAME | CE_OVERRUN | CE_RXPARITY))
|
|
{
|
|
if (szError[0])
|
|
lstrcat(szError,TEXT(" and "));
|
|
|
|
lstrcat(szError,TEXT("EV_ERR Unknown EvtMask"));
|
|
}
|
|
|
|
|
|
DBG_INFO((DBGARG, TEXT("Comm Event: '%s', EvtMask = '%lx' %s %d"),
|
|
szError, dwErrors));
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
// Should not have gotten here. Only interested in ERR conditions.
|
|
|
|
DBG_INFO((DBGARG, TEXT("Unexpected comm event %lx"),*lpfdwEvtMask));
|
|
return FALSE;
|
|
}
|
|
|