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