1440 lines
47 KiB
C++
1440 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 wrong, 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 = (DWORD)msg.wParam;
|
||
WriteFile( g_hCommFile, (LPVOID) msg.lParam, (DWORD)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 = (DWORD)msg.wParam;
|
||
WriteFile( g_hCommFile, (LPVOID) msg.lParam, (DWORD)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;
|
||
}
|
||
|