NT4/private/windows/media/dplay/serial/commcode.cpp

1441 lines
47 KiB
C++
Raw Normal View History

2001-01-01 00:00:00 +01:00
// 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;
}