|
|
/* File: D:\WACKER\comstd\comstd.c (Created: 08-Dec-1993)
* * Copyright 1994 by Hilgraeve Inc. -- Monroe, MI * All rights reserved * * $Revision: 35 $ * $Date: 7/12/02 8:06a $ */ #define TAPI_CURRENT_VERSION 0x00010004 // cab:11/14/96 - required!
#include <windows.h>
#include <tapi.h>
#include <unimodem.h>
#include <time.h>
#pragma hdrstop
//#define DEBUGSTR
//#define DEBUG_CHARDUMP
#include <tdll\stdtyp.h>
#include <tdll\session.h>
#include <tdll\mc.h>
#include <tdll\sf.h>
#include <tdll\timers.h>
#include <tdll\com.h>
#include <tdll\comdev.h>
#include "comstd.hh"
#if defined(INCL_WINSOCK)
#include <comwsock\comwsock.hh>
#endif // defined(INCL_WINSOCK)
#include <tdll\assert.h>
#include <tdll\statusbr.h>
#include <tdll\com.hh>
#include "rc_id.h"
#include <tdll\misc.h> // IsNT()
#include <tdll\htchar.h>
#include <tdll\cnct.h>
#include <tdll\cnct.hh>
#include <cncttapi\cncttapi.h>
#include <cncttapi\cncttapi.hh>
#if defined(DEBUG_CHARDUMP)
#include <stdio.h>
FILE *pfDbgR = NULL; FILE *pfDbgC = NULL; #endif
BOOL WINAPI ComStdEntry(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpReserved); BOOL WINAPI _CRT_INIT(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpReserved); static void DeviceBreakTimerProc(void *pvData, long ulSince); //mrw:6/15/95
HINSTANCE hinstDLL = (HINSTANCE)0;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * * DESCRIPTION: * very temporary - mrw * * ARGUMENTS: * * RETURNS: * */ int GetAutoDetect(ST_STDCOM *pstPrivate) { return pstPrivate->stWorkSettings.fAutoDetect; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * ComStdEntry * * DESCRIPTION: * Currently, just initializes the C-Runtime library but may be used * for other things later. * * ARGUMENTS: * hInstDll - Instance of this DLL * fdwReason - Why this entry point is called * lpReserved - reserved * * RETURNS: * BOOL * */ BOOL WINAPI ComStdEntry(HINSTANCE hInst, DWORD fdwReason, LPVOID lpReserved) { hinstDLL = hInst; return _CRT_INIT(hInst, fdwReason, lpReserved); }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceInitialize * * DESCRIPTION: * Called whenever the driver is being loaded * * ARGUMENTS: * hCom -- A copy of the com handle. Can be used in the * driver code to call com services * usInterfaceVersion -- A version number identifying the version of the * driver interface * ppvDriverData -- A place to put the pointer to our private data. * This value will be passed back to us in all * subsequent calls. * * RETURNS: * COM_OK if all is hunky dory * COM_DEVICE_VERSION_ERROR if HA/Win expects a different interface version. * COM_NOT_ENOUGH_MEMORY * COM_DEVICE_ERROR if anything else goes wrong */ int WINAPI DeviceInitialize(HCOM hCom, unsigned nInterfaceVersion, void **ppvDriverData) { int iRetVal = COM_OK; int ix; ST_STDCOM *pstPrivate = NULL;
// Check version number and compatibility
if (nInterfaceVersion != COM_VERSION) { // This error is reported by Com Routines. We cannot report errors
// until after DeviceInitialize has completed.
return COM_DEVICE_VERSION_ERROR; }
if (*ppvDriverData) { pstPrivate = *ppvDriverData; } else { // Allocate our private storage structure
if ((pstPrivate = malloc(sizeof(*pstPrivate))) == NULL) { return COM_NOT_ENOUGH_MEMORY; }
*ppvDriverData = pstPrivate;
// These members are common to both com drivers
//
pstPrivate->hCom = hCom; pstPrivate->fNotifyRcv = TRUE; pstPrivate->dwEventMask = 0; pstPrivate->fSending = FALSE; pstPrivate->lSndTimer = 0L; pstPrivate->lSndLimit = 0L; pstPrivate->lSndStuck = 0L; pstPrivate->hwndEvents = (HWND)0; pstPrivate->nRBufrSize = SIZE_INQ; pstPrivate->pbBufrStart = NULL; pstPrivate->fHaltThread = TRUE;
InitializeCriticalSection(&pstPrivate->csect); for (ix = 0; ix < EVENT_COUNT; ++ix) { pstPrivate->ahEvent[ix] = CreateEvent(NULL, TRUE, // must be manually reset
FALSE, // create unsignalled
NULL); // unnamed
if (pstPrivate->ahEvent[ix] == NULL) { iRetVal = COM_FAILED; //
// Make sure to initialize the rest of the event handles to NULL;
//
for (++ix; ix < EVENT_COUNT; ++ix) { pstPrivate->ahEvent[ix] = NULL; } } } }
// These members are specific to the stdcom driver
//
pstPrivate->bLastMdmStat = 0; pstPrivate->pbSndBufr = NULL; pstPrivate->nParityErrors = 0; pstPrivate->nFramingErrors = 0; pstPrivate->nOverrunErrors = 0; pstPrivate->nOverflowErrors = 0;
pstPrivate->hWinComm = INVALID_HANDLE_VALUE; pstPrivate->fBreakSignalOn = FALSE; // Setup up reasonable default device values in case this type of
// device has not been used in a session before
pstPrivate->stWorkSettings.lBaud = 2400L; //pstPrivate->stWorkSettings.lBaud = 9600L;
pstPrivate->stWorkSettings.nDataBits = 8; pstPrivate->stWorkSettings.nStopBits = ONESTOPBIT; pstPrivate->stWorkSettings.nParity = NOPARITY; pstPrivate->stWorkSettings.afHandshake = HANDSHAKE_RCV_RTS | HANDSHAKE_SND_CTS; pstPrivate->stWorkSettings.chXON = 0x11; pstPrivate->stWorkSettings.chXOFF = 0x13; pstPrivate->stWorkSettings.nBreakDuration = 750; pstPrivate->stWorkSettings.fAutoDetect = TRUE; pstPrivate->stFileSettings = pstPrivate->stWorkSettings;
pstPrivate->fADRunning = FALSE; pstPrivate->nADTotal = 0; pstPrivate->nADMix = 0; pstPrivate->nAD7o1 = 0; pstPrivate->nADHighBits = 0; pstPrivate->nADBestGuess = AD_DONT_KNOW; pstPrivate->chADLastChar = '\0'; pstPrivate->fADToggleParity = FALSE; pstPrivate->fADReconfigure = FALSE;
pstPrivate->hComstdThread = NULL;
if (iRetVal != COM_OK) { if (pstPrivate) { free(pstPrivate); pstPrivate = NULL; } }
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceClose * * DESCRIPTION: * Called when HA/Win is done with this driver and is about to release .DLL * * ARGUMENTS: * pstPrivate -- Pointer to our private data structure * * RETURNS: * COM_OK */ int WINAPI DeviceClose(ST_STDCOM *pstPrivate) { int ix;
// Driver is about to be let go, do any cleanup
// Port should have been deactivated before we are called, but
// check anyway.
PortDeactivate(pstPrivate);
for (ix = 0; ix < EVENT_COUNT; ++ix) { if (pstPrivate->ahEvent[ix]) { ResetEvent(pstPrivate->ahEvent[ix]); CloseHandle(pstPrivate->ahEvent[ix]); pstPrivate->ahEvent[ix] = NULL; } }
DeleteCriticalSection(&pstPrivate->csect); // Free our private data area
free(pstPrivate); pstPrivate = NULL;
return COM_OK; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * ComLoadStdcomDriver * * DESCRIPTION: * Loads the COM handle with pointers to the stdcom driver functions * * ARGUMENTS: * * RETURNS: * COM_OK if successful * COM_FAILED otherwise * * AUTHOR: * mcc 12/26/95 */ int ComLoadStdcomDriver(HCOM pstCom) { int iRetVal = COM_OK;
if ( !pstCom ) return COM_FAILED;
pstCom->pfPortActivate = PortActivate; pstCom->pfPortDeactivate = PortDeactivate; pstCom->pfPortConnected = PortConnected; pstCom->pfRcvRefill = RcvRefill; pstCom->pfRcvClear = RcvClear; pstCom->pfSndBufrSend = SndBufrSend; pstCom->pfSndBufrIsBusy = SndBufrIsBusy; pstCom->pfSndBufrClear = SndBufrClear; pstCom->pfSndBufrQuery = SndBufrQuery; pstCom->pfDeviceSpecial = DeviceSpecial; pstCom->pfPortConfigure = PortConfigure; pstCom->pfDeviceDialog = DeviceDialog;
return iRetVal; }
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceDialog * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ /*lint ARGSUSED*/ int WINAPI DeviceDialog(ST_STDCOM *pstPrivate, HWND hwndParent) { int iRetValue = COM_OK; COMMCONFIG stCC; TCHAR szPortName[COM_MAX_PORT_NAME];
memset(&stCC, 0, sizeof(stCC)); stCC.dwSize = sizeof(stCC); stCC.wVersion = 1; stCC.dwProviderSubType = PST_RS232; ComGetPortName(pstPrivate->hCom, szPortName, COM_MAX_PORT_NAME); ComstdSettingsToDCB(&pstPrivate->stWorkSettings, &stCC.dcb);
if (CommConfigDialog(szPortName, hwndParent, &stCC)) { ComstdDCBToSettings(&stCC.dcb, &pstPrivate->stWorkSettings); } else { iRetValue = COM_CANCELLED; //DbgShowLastError();
}
return iRetValue; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceGetCommon * * DESCRIPTION: * Gets user settings common to all drivers * * ARGUMENTS: * pstPrivate -- Our private data structure * pstcommon -- Pointer to structure of type ST_COMMON to be filled in * with the desired settings * * RETURNS: * Always returns COM_OK */ int WINAPI DeviceGetCommon(ST_STDCOM *pstPrivate, ST_COMMON *pstCommon) { pstCommon->afItem = (COM_BAUD | COM_DATABITS | COM_STOPBITS | COM_PARITY | COM_AUTO); pstCommon->lBaud = pstPrivate->stWorkSettings.lBaud; pstCommon->nDataBits = pstPrivate->stWorkSettings.nDataBits; pstCommon->nStopBits = pstPrivate->stWorkSettings.nStopBits; pstCommon->nParity = pstPrivate->stWorkSettings.nParity; pstCommon->fAutoDetect = pstPrivate->stWorkSettings.fAutoDetect;
return COM_OK; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceSetCommon * * DESCRIPTION: * Passes common user settings to driver for use and storage. * * ARGUMENTS: * pstPrivate * pstCommon -- Structure containing common user settings to be used * by driver * * RETURNS: * Always returns COM_OK */ int WINAPI DeviceSetCommon(ST_STDCOM *pstPrivate, ST_COMMON *pstCommon) { if (bittest(pstCommon->afItem, COM_BAUD)) pstPrivate->stWorkSettings.lBaud = pstCommon->lBaud; if (bittest(pstCommon->afItem, COM_DATABITS)) pstPrivate->stWorkSettings.nDataBits = pstCommon->nDataBits; if (bittest(pstCommon->afItem, COM_STOPBITS)) pstPrivate->stWorkSettings.nStopBits = pstCommon->nStopBits; if (bittest(pstCommon->afItem, COM_PARITY)) pstPrivate->stWorkSettings.nParity = pstCommon->nParity; if (bittest(pstCommon->afItem, COM_AUTO)) pstPrivate->stWorkSettings.fAutoDetect = pstCommon->fAutoDetect;
return COM_OK; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceSpecial * * DESCRIPTION: * The means for others to control any special features in this driver * that are not supported by all drivers. * * ARGUMENTS: * * * RETURNS: * COM_NOT_SUPPORTED if the instruction string was not recognized * otherwise depends on instruction string */ /*ARGSUSED*/ int WINAPI DeviceSpecial(ST_STDCOM *pstPrivate, const TCHAR *pszInstructions, TCHAR *pszResult, int nBufrSize) {
int iRetVal = COM_NOT_SUPPORTED; HSESSION hSession; #if 0 //* do port of this later
unsigned usMask = 0; unsigned long ulSetVal; TCHAR *pszEnd; //
// MAX_IP_ADDR_LEN+11+1 = buffer size of pstPrivate->szRemoteAddr +
// settings string "SET IPADDR=" + 1 for the terminating NULL
// character. REV 09/20/2000
//
TCHAR achInstructions[MAX_IP_ADDR_LEN+11+1]; // John: decide how you want to handle
TCHAR *pszToken = achInstructions; int iIndex; TCHAR szResult[MAX_IP_ADDR_LEN+11+1];
static TCHAR *apszItems[] = { "HANDSHAKE_RCV_X", "HANDSHAKE_RCV_DTR", "HANDSHAKE_RCV_RTS", "HANDSHAKE_SND_X", "HANDSHAKE_SND_CTS", "HANDSHAKE_SND_DSR", "HANDSHAKE_SND_DCD", "XON_CHAR", "XOFF_CHAR", "BREAK_DURATION", "CTS_STATUS", "DSR_STATUS", "DCD_STATUS", "DTR_STATE", "RTS_STATE", "MODIFIED", // remove when real temporary settings are in
NULL };
// supported instruction strings:
// "Set xxx=vv"
// "Query xxx"
if (!pszInstructions || !*pszInstructions) return COM_FAILED; if (sizeof(achInstructions) < (size_t)(StrCharGetStrLength(pszInstructions) + 1)) return COM_NOT_SUPPORTED;
strcpy(achInstructions, pszInstructions);
if (pszResult) *pszResult = TEXT('\0');
pszToken = strtok(achInstructions, " "); if (!pszToken) return COM_NOT_SUPPORTED;
if (StrCharCmpi(pszToken, "SET") == 0) { iRetVal = COM_OK; pszToken = strtok(NULL, " ="); if (!pszToken) pszToken = TEXT('\0');
// Look up the item to set
for (iIndex = 0; apszItems[iIndex]; ++iIndex) if (StrCharCmpi(pszToken, apszItems[iIndex]) == 0) break;
// Isolate the new value to be set
pszToken = strtok(NULL, "\n");
if (pszToken && *pszToken) { // Several items take numeric values
ulSetVal = strtoul(pszToken, &pszEnd, 0);
switch(iIndex) { case 0: // RCV_X
usMask = HANDSHAKE_RCV_X; break;
case 1: // RCV_DTR
usMask = HANDSHAKE_RCV_DTR; break;
case 2: // RCV_RTS
usMask = HANDSHAKE_RCV_RTS; break;
case 3: // SND_X
usMask = HANDSHAKE_SND_X; break;
case 4: // SND_CTS
usMask = HANDSHAKE_SND_CTS; break;
case 5: // SND_DSR
usMask = HANDSHAKE_SND_DSR; break;
case 6: // SND_DCD
usMask = HANDSHAKE_SND_DCD; break;
case 7: // XON_CHAR
if (!*pszEnd && ulSetVal <= UCHAR_MAX) pstPrivate->stWorkSettings.chXON = (TCHAR)ulSetVal; else iRetVal = COM_FAILED; break;
case 8: // XOFF_CHAR
if (!*pszEnd && ulSetVal <= UCHAR_MAX) pstPrivate->stWorkSettings.chXOFF = (TCHAR)ulSetVal; else iRetVal = COM_FAILED; break;
case 9: // BREAK_DURATION
if (!*pszEnd && ulSetVal <= USHRT_MAX) pstPrivate->stWorkSettings.nBreakDuration = (USHORT)ulSetVal; else iRetVal = COM_FAILED; break;
case 13: // DTR_STATE
if (pstPrivate->hWinComm != INVALID_HANDLE_VALUE) { switch (ulSetVal) { case 0: EscapeCommFunction(pstPrivate->hWinComm, CLRDTR); break;
case 1: EscapeCommFunction(pstPrivate->hWinComm, SETDTR); break;
default: iRetVal = COM_FAILED; break; } } else iRetVal = COM_PORT_NOT_OPEN;
break;
case 14: // RTS_STATE
if (pstPrivate->hWinComm != INVALID_HANDLE_VALUE) { switch (ulSetVal) { case 0: EscapeCommFunction(pstPrivate->hWinComm, CLRRTS); break;
case 1: EscapeCommFunction(pstPrivate->hWinComm, SETRTS); break;
default: iRetVal = COM_FAILED; break; } } else iRetVal = COM_PORT_NOT_OPEN;
break;
// TODO: remove when real temp settings are implemented
case 15: // MODIFIED
break;
default: // Who was that masked man?
iRetVal = COM_FAILED; break; }
if (usMask != 0) { // Must have been a handshake setting
if (strcmp(pszToken, "1") == 0) bitset(pstPrivate->stWorkSettings.afHandshake, usMask); else if (strcmp(pszToken, "0") == 0) bitclear(pstPrivate->stWorkSettings.afHandshake,usMask); else { iRetVal = COM_FAILED; } } } else // if (pszToken && *pszToken)
{ iRetVal = COM_NOT_SUPPORTED; } } else if (StrCharCmpi(pszToken, "QUERY") == 0) { iRetVal = COM_OK; pszToken = strtok(NULL, "\n"); szResult[0] = TEXT('\0');
// Look up the item to query
for (iIndex = 0; apszItems[iIndex]; ++iIndex) if (StrCharCmpi(pszToken, apszItems[iIndex]) == 0) break;
if (*pszToken) { switch(iIndex) { case 0: // RCV_X
usMask = HANDSHAKE_RCV_X; break;
case 1: // RCV_DTR
usMask = HANDSHAKE_RCV_DTR; break;
case 2: // RCV_RTS
usMask = HANDSHAKE_RCV_RTS; break;
case 3: // SND_X
usMask = HANDSHAKE_SND_X; break;
case 4: // SND_CTS
usMask = HANDSHAKE_SND_CTS; break;
case 5: // SND_DSR
usMask = HANDSHAKE_SND_DSR; break;
case 6: // SND_DCD
usMask = HANDSHAKE_SND_DCD; break;
case 7: // XON_CHAR
wsprintf(szResult, "%u", pstPrivate->stWorkSettings.chXON); break;
case 8: // XOFF_CHAR
wsprintf(szResult, "%u", pstPrivate->stWorkSettings.chXOFF); break;
case 9: // BREAK_DURATION
wsprintf(szResult, "%u", pstPrivate->stWorkSettings.nBreakDuration); break;
case 10: // CTS_STATUS
strcpy(szResult, bittest(*pbMdmStat, MDMSTAT_CTS) ? "1" : "0"); break;
case 11: // DSR_STATUS
strcpy(szResult, bittest(*pbMdmStat, MDMSTAT_DSR) ? "1" : "0"); break;
case 12: // DCD_STATUS
strcpy(szResult, bittest(*pbMdmStat, MDMSTAT_DCD) ? "1" : "0"); break;
case 15: // MODIFIED
strcpy(szResult, "0"); break;
default: // Who was that masked man?
iRetVal = COM_FAILED; break; }
if (usMask != 0) { // Must have been a handshake setting
strcpy(szResult, bittest(pstPrivate->stWorkSettings.afHandshake, usMask) ? "1" : "0"); }
if (szResult[0]) { if (!pszResult || strlen(szResult) > uiBufrSize) iRetVal = COM_FAILED; else strcpy(pszResult, szResult); } } } else if (StrCharCmpi(pszToken, "SEND") == 0) { pszToken = strtok(NULL, "\n"); if (StrCharCmpi(pszToken, "BREAK") == 0) { if (pstPrivate->hWinComm != INVALID_HANDLE_VALUE && !pstPrivate->fBreakSignalOn) { SndBufrClear(pstPrivate); SetCommBreak(pstPrivate->hWinComm); ComGetSession(pstPrivate->hCom, &hSession);
if (TimerCreate(hSession, &pstPrivate->hTmrBreak, pstPrivate->stWorkSettings.nBreakDuration, MakeProcInstance((FARPROC)DeviceBreakTimerProc, hinstDLL), (DWORD)pstPrivate) != TIMER_OK) { //* DeviceReportError(pstPrivate, SID_ERR_NOTIMER, 0, TRUE);
iRetVal = COM_DEVICE_ERROR; }
pstPrivate->fBreakSignalOn = TRUE; iRetVal = COM_OK; } } } #else
if (pszResult && nBufrSize > 0) { *pszResult = TEXT('\0'); } #endif
// Implement only the Break function. All other comm functions handled
// through TAPI. - mrw:6/15/95
//
if (StrCharCmpi(pszInstructions, "Send Break") == 0) { if (pstPrivate->hWinComm != INVALID_HANDLE_VALUE && !pstPrivate->fBreakSignalOn) { SndBufrClear(pstPrivate); SetCommBreak(pstPrivate->hWinComm); ComGetSession(pstPrivate->hCom, &hSession);
if (TimerCreate(hSession, &pstPrivate->hTmrBreak, pstPrivate->stWorkSettings.nBreakDuration, DeviceBreakTimerProc, pstPrivate) != TIMER_OK) { //* DeviceReportError(pstPrivate, SID_ERR_NOTIMER, 0, TRUE);
iRetVal = COM_DEVICE_ERROR; }
pstPrivate->fBreakSignalOn = TRUE; iRetVal = COM_OK; } } //
// This is necessary to detect loss of carrier on COM ports. REV: 08/22/2001
//
else if (StrCharCmpi(pszInstructions, "Query DCD_STATUS") == 0) { iRetVal = COM_OK; if (pszResult && nBufrSize > 0) { _itoa(PortConnected(pstPrivate), pszResult, 10); } } //
// This is necessary to get the default settings on COM ports. REV: 08/22/2001
//
else if (StrCharCmpi(pszInstructions, "GET Defaults") == 0) { iRetVal = PortDefaultSettings(pstPrivate); }
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * DeviceLoadHdl * * DESCRIPTION: * * * ARGUMENTS: * pstPrivate -- driver data structure * * RETURNS: * */ int WINAPI DeviceLoadHdl(ST_STDCOM *pstPrivate, SF_HANDLE sfHdl) { unsigned long ul;
// Load comm settings from the session file. If we connect via TAPI,
// several of these settings will be inherited from TAPI and these
// values will not be used.
ul = sizeof(pstPrivate->stWorkSettings.lBaud); sfGetSessionItem(sfHdl, SFID_COMSTD_BAUD, &ul, &pstPrivate->stWorkSettings.lBaud);
ul = sizeof(pstPrivate->stWorkSettings.nDataBits); sfGetSessionItem(sfHdl, SFID_COMSTD_DATABITS, &ul, &pstPrivate->stWorkSettings.nDataBits);
ul = sizeof(pstPrivate->stWorkSettings.nStopBits); sfGetSessionItem(sfHdl, SFID_COMSTD_STOPBITS, &ul, &pstPrivate->stWorkSettings.nStopBits);
ul = sizeof(pstPrivate->stWorkSettings.nParity); sfGetSessionItem(sfHdl, SFID_COMSTD_PARITY, &ul, &pstPrivate->stWorkSettings.nParity);
ul = sizeof(pstPrivate->stWorkSettings.afHandshake); sfGetSessionItem(sfHdl, SFID_COMSTD_HANDSHAKING, &ul, &pstPrivate->stWorkSettings.afHandshake);
ul = sizeof(pstPrivate->stWorkSettings.fAutoDetect); sfGetSessionItem(sfHdl, SFID_COMSTD_AUTODETECT, &ul, &pstPrivate->stWorkSettings.fAutoDetect);
return SF_OK; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * DeviceSaveHdl * * DESCRIPTION: * * * ARGUMENTS: * pstPrivate -- driver data structure * * RETURNS: * */ int WINAPI DeviceSaveHdl(ST_STDCOM *pstPrivate, SF_HANDLE sfHdl) { // Save settings in session file space. Many of these values may be
// overwritten by TAPI settings but are used by direct connect.
sfPutSessionItem(sfHdl, SFID_COMSTD_BAUD, sizeof(pstPrivate->stWorkSettings.lBaud), &pstPrivate->stWorkSettings.lBaud);
sfPutSessionItem(sfHdl, SFID_COMSTD_DATABITS, sizeof(pstPrivate->stWorkSettings.nDataBits), &pstPrivate->stWorkSettings.nDataBits);
sfPutSessionItem(sfHdl, SFID_COMSTD_STOPBITS, sizeof(pstPrivate->stWorkSettings.nStopBits), &pstPrivate->stWorkSettings.nStopBits);
sfPutSessionItem(sfHdl, SFID_COMSTD_PARITY, sizeof(pstPrivate->stWorkSettings.nParity), &pstPrivate->stWorkSettings.nParity);
sfPutSessionItem(sfHdl, SFID_COMSTD_HANDSHAKING, sizeof(pstPrivate->stWorkSettings.afHandshake), &pstPrivate->stWorkSettings.afHandshake);
sfPutSessionItem(sfHdl, SFID_COMSTD_AUTODETECT, sizeof(pstPrivate->stWorkSettings.fAutoDetect), &pstPrivate->stWorkSettings.fAutoDetect);
return SF_OK; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: PortActivate * * DESCRIPTION: * Called to activate the port and open it for use * * ARGUMENTS: * pstPrivate -- driver data structure * pszPortName -- the name of the port to activate * * RETURNS: * COM_OK if port is successfully activated * COM_NOT_ENOUGH_MEMORY if there in insufficient memory for data storage * COM_NOT_FOUND if named port cannot be opened * COM_DEVICE_ERROR if API errors are encountered */ int WINAPI PortActivate(ST_STDCOM *pstPrivate, TCHAR *pszPortName, DWORD_PTR dwMediaHdl) { TCHAR szFullPortName[MAX_PATH]; int iRetVal = COM_OK; ST_COM_CONTROL *pstComCntrl; DWORD dwThreadID;
//
// Free the send bufers prior to setting to malloc so we don't
// have a memory leak. REV: 02/27/2001.
//
if (pstPrivate->pbBufrStart) { free(pstPrivate->pbBufrStart); pstPrivate->pbBufrStart = NULL; }
// Make sure we can get enough memory for buffers before opening device
pstPrivate->pbBufrStart = malloc((size_t)pstPrivate->nRBufrSize);
if (pstPrivate->pbBufrStart == NULL) { iRetVal = COM_NOT_ENOUGH_MEMORY; //* DeviceReportError(pstPrivate, SID_ERR_NOMEM, 0, TRUE);
goto checkout; }
//
// Free the send bufers prior to setting to malloc so we don't
// have a memory leak. REV: 02/27/2001.
//
if (pstPrivate->pbSndBufr) { free(pstPrivate->pbSndBufr); pstPrivate->pbSndBufr = NULL; }
pstPrivate->pbSndBufr = malloc((size_t) SIZE_OUTQ); if (pstPrivate->pbSndBufr == 0) { iRetVal = COM_NOT_ENOUGH_MEMORY; free(pstPrivate->pbBufrStart); pstPrivate->pbBufrStart = NULL; goto checkout; }
pstPrivate->pbBufrEnd = pstPrivate->pbBufrStart + pstPrivate->nRBufrSize; pstPrivate->pbReadEnd = pstPrivate->pbBufrStart; pstPrivate->pbComStart = pstPrivate->pbComEnd = pstPrivate->pbBufrStart; pstPrivate->fBufrEmpty = TRUE;
#if defined(DEBUG_CHARDUMP)
if (!pfDbgR) pfDbgR = fopen("comreads.dbg", "wt"); fprintf(pfDbgR, "Port opened, internal buffer = 0x%p to 0x%p\n", pstPrivate->pbBufrStart, pstPrivate->pbBufrEnd - 1); if (!pfDbgC) pfDbgC = fopen("comused.dbg", "wt"); fprintf(pfDbgC, "Port opened, internal buffer = 0x%p to 0x%p\n", pstPrivate->pbBufrStart, pstPrivate->pbBufrEnd - 1); #endif
pstPrivate->dwSndOffset = 0; pstPrivate->dwBytesToSend = 0;
if (dwMediaHdl) { pstPrivate->hWinComm = (HANDLE)dwMediaHdl;
if (PortExtractSettings(pstPrivate) != COM_OK) iRetVal = COM_DEVICE_ERROR; }
else { // Win32 internally maps ports COM1 to COM9 to
// \\.\COMx. We need to add this for ports COMxx,
// and for special com devices in the registry.
//
StrCharCopyN(szFullPortName, TEXT("\\\\.\\"), sizeof(szFullPortName) / sizeof(TCHAR)); StrCharCat(szFullPortName, pszPortName); pstPrivate->hWinComm = CreateFile(szFullPortName, GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, (HANDLE)NULL);
if (pstPrivate->hWinComm == INVALID_HANDLE_VALUE) { //* Figure out which errors to report specifically
DWORD dwError = GetLastError();
if( dwError == ERROR_NOT_ENOUGH_MEMORY || dwError == ERROR_OUTOFMEMORY || dwError == ERROR_OUT_OF_STRUCTURES || dwError == ERROR_INSUFFICIENT_BUFFER || dwError == ERROR_COMMITMENT_LIMIT || dwError == ERROR_NOT_ENOUGH_QUOTA) { iRetVal = COM_NOT_ENOUGH_MEMORY; } else if(dwError == ERROR_ACCESS_DENIED || dwError == ERROR_SHARING_VIOLATION || dwError == ERROR_LOCK_VIOLATION || dwError == ERROR_OPEN_FAILED || dwError == ERROR_IRQ_BUSY || dwError == ERROR_DEVICE_IN_USE) { iRetVal = COM_PORT_IN_USE; } else { iRetVal = COM_NOT_FOUND; } } }
if (iRetVal == COM_OK) { // Major bug in Win95 - If you call SetupComm() for a standard
// comm handle (not one given to us by TAPI) the WriteFile
// call fails and locks the system. - mrw:2/29/96
//
if (IsNT() && SetupComm(pstPrivate->hWinComm, 8192, 8192) == FALSE) { assert(0); } }
if (iRetVal == COM_OK) { iRetVal = PortConfigure(pstPrivate); }
if (iRetVal == COM_OK) { pstComCntrl = (ST_COM_CONTROL *)pstPrivate->hCom; pstComCntrl->puchRBData = pstPrivate->pbBufrStart; pstComCntrl->puchRBDataLimit = pstPrivate->pbBufrStart;
pstPrivate->dwEventMask = EV_ERR | EV_RLSD; pstPrivate->fNotifyRcv = TRUE; pstPrivate->fBufrEmpty = TRUE;
if (!SetCommMask(pstPrivate->hWinComm, pstPrivate->dwEventMask)) iRetVal = COM_DEVICE_ERROR;
// Clear error counts on new connection
pstPrivate->nParityErrors = 0; pstPrivate->nFramingErrors = 0; pstPrivate->nOverrunErrors = 0; pstPrivate->nOverflowErrors = 0;
// Start thread to handle Reading, Writing (& 'rithmetic) & events
pstPrivate->fHaltThread = FALSE; DBG_THREAD("DBG_THREAD: Calling CreateThread\r\n",0,0,0,0,0); pstPrivate->hComstdThread = CreateThread((LPSECURITY_ATTRIBUTES)0, 2000, ComstdThread, pstPrivate, 0, &dwThreadID);
if (pstPrivate->hComstdThread) { SetThreadPriority(pstPrivate->hComstdThread, THREAD_PRIORITY_ABOVE_NORMAL); //THREAD_PRIORITY_TIME_CRITICAL); // - mrw:7/8/96
}
DBG_THREAD("DBG_THREAD: CreateThread returned %08X\r\n", pstPrivate->hComstdThread,0,0,0,0); }
checkout: if (iRetVal != COM_OK) PortDeactivate(pstPrivate);
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: PortDeactivate * * DESCRIPTION: * Deactivates and closes an open port * * ARGUMENTS: * pstPrivate -- Driver data structure * * RETURNS: * COM_OK */ int WINAPI PortDeactivate(ST_STDCOM *pstPrivate) { int iRetVal = COM_OK;
if (pstPrivate->hComstdThread) { DWORD dwResult = 0L;
// Halt the thread by setting a flag for the thread to detect and then
// forcing WaitCommEvent to return by changing the event mask
DBG_THREAD("DBG_THREAD: Shutting down comstd thread\r\n", 0,0,0,0,0); pstPrivate->fHaltThread = TRUE; SetCommMask(pstPrivate->hWinComm, pstPrivate->dwEventMask); PurgeComm(pstPrivate->hWinComm, PURGE_TXABORT | PURGE_RXABORT); // Abort any calls in progress
// thread should exit now, it's handle will signal when it has exited
if (pstPrivate->hComstdThread) { dwResult = WaitForSingleObject(pstPrivate->hComstdThread, 5000);
if (dwResult != WAIT_OBJECT_0) { if (dwResult == WAIT_FAILED) { dwResult = GetLastError(); } assert(FALSE); } }
if (pstPrivate->hComstdThread) { CloseHandle(pstPrivate->hComstdThread); pstPrivate->hComstdThread = NULL; DBG_THREAD("DBG_THREAD: Comstd thread has shut down\r\n", 0,0,0,0,0); } }
if (pstPrivate->pbBufrStart) { free(pstPrivate->pbBufrStart); pstPrivate->pbBufrStart = NULL; }
if (pstPrivate->pbSndBufr) { free(pstPrivate->pbSndBufr); pstPrivate->pbSndBufr = 0; }
if (pstPrivate->hWinComm != INVALID_HANDLE_VALUE) { //* As of 2/9/94, this PurgeComm call caused the program to hang
// or reboot
// PurgeComm(pstPrivate->hWinComm,
// PURGE_TXABORT | PURGE_RXABORT); // Flush transmit queue
CloseHandle(pstPrivate->hWinComm); }
pstPrivate->hWinComm = INVALID_HANDLE_VALUE;
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: PortConfigure * * DESCRIPTION: * Configures an open port with the current set of user settings * * ARGUMENTS: * pstPrivate -- The driver data structure * * RETURNS: * COM_OK if port is configured successfully * COM_DEVICE_ERROR if API errors are encountered * COM_DEVICE_INVALID_SETTING if some user settings are not valid */ int WINAPI PortConfigure(ST_STDCOM *pstPrivate) { int iRetVal = COM_OK; unsigned uOverrides = 0; DWORD dwError; DWORD dwStructSize; DCB *pstDcb; COMMCONFIG stCommConfig; COMMTIMEOUTS stCT;
dwStructSize = sizeof(stCommConfig); stCommConfig.dwSize = sizeof(stCommConfig);
if (!GetCommConfig(pstPrivate->hWinComm, &stCommConfig, &dwStructSize)) { //* DeviceReportError(pstPrivate, SID_ERR_WINDRIVER, 0, TRUE);
iRetVal = COM_DEVICE_ERROR; } else { pstDcb = &stCommConfig.dcb; ComstdSettingsToDCB(&pstPrivate->stWorkSettings, pstDcb);
// Check for overrides
ComQueryOverride(pstPrivate->hCom, &uOverrides);
if (bittest(uOverrides, COM_OVERRIDE_8BIT)) { pstDcb->ByteSize = 8; pstDcb->Parity = NOPARITY; }
// If we need to receive all 256 chars., we need to override
// XON/XOFF during sending since it will strip XON & XOFF from
// the incoming stream if enabled
if (bittest(uOverrides, COM_OVERRIDE_RCVALL)) pstDcb->fOutX = 0;
stCommConfig.dwSize = sizeof(stCommConfig);
if (!SetCommConfig(pstPrivate->hWinComm, &stCommConfig, dwStructSize)) { dwError = GetLastError();
//* Use GetLastError to figure out what went wrong, but
//* docs don't specify which error to check for.
// At this point SOME setting in the DCB is bad but there is
// no way to find out which. Since the baud rate is a likely
// candidate. Try reissuing the command with a common baud
// rate to see if the problem goes away
//
pstDcb->BaudRate = 1200;
if (!SetCommConfig(pstPrivate->hWinComm, &stCommConfig, sizeof(stCommConfig))) { // If its still no good them some other setting is bad
//* DeviceReportError(pstPrivate, SID_ERR_BADSETTING, 0, TRUE);
} else { // Changing baud rate to 1200 worked, so the user's baud
// rate must be what the driver is refusing
//* DeviceReportError(pstPrivate, SID_ERR_BADBAUD, 0, TRUE);
} iRetVal = COM_DEVICE_INVALID_SETTING; } else { stCT.ReadIntervalTimeout = 10; stCT.ReadTotalTimeoutMultiplier = 0; stCT.ReadTotalTimeoutConstant = 0; stCT.WriteTotalTimeoutMultiplier = 0; stCT.WriteTotalTimeoutConstant = 5000; if (!SetCommTimeouts(pstPrivate->hWinComm, &stCT)) { assert(FALSE); iRetVal = COM_DEVICE_INVALID_SETTING; } } } return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * PortExtactSettings * * DESCRIPTION: * Extracts current Com settings from the Windows Com driver. This is * needed when we are passed an existing Com handle by something like TAPI * * ARGUMENTS: * pstPrivate -- The driver data structure * * RETURNS: * COM_OK if port is configured successfully * COM_DEVICE_ERROR if API errors are encountered */ int PortExtractSettings(ST_STDCOM *pstPrivate) { int iRetVal; DWORD dwError; DWORD dwSize; COMMCONFIG stCommConfig;
dwSize = sizeof(stCommConfig); if (!GetCommConfig(pstPrivate->hWinComm, &stCommConfig, &dwSize)) { dwError = GetLastError(); //* DeviceReportError(pstPrivate, SID_ERR_WINDRIVER, 0, TRUE);
iRetVal = COM_DEVICE_ERROR; } else { // Unload appropriate values from DCB to our settings structure
ComstdDCBToSettings(&stCommConfig.dcb, &pstPrivate->stWorkSettings);
// Don't leave autodetect on if user has already set something
// other than 8N1
DBG_AD("DBG_AD: fAutoDetect = %d\r\n", pstPrivate->stWorkSettings.fAutoDetect, 0,0,0,0); if (pstPrivate->stWorkSettings.fAutoDetect) { if (pstPrivate->stWorkSettings.nDataBits != 8 || pstPrivate->stWorkSettings.nParity != NOPARITY || pstPrivate->stWorkSettings.nStopBits != ONESTOPBIT) { DBG_AD("DBG_AD: Turning fAutoDetect off due to non 8N1\r\n", 0,0,0,0,0); pstPrivate->stWorkSettings.fAutoDetect = FALSE; } }
iRetVal = COM_OK; }
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * PortDefaultSettings * * DESCRIPTION: * Extracts current Com settings from the Windows Com driver. This is * needed when we are passed an existing Com handle by something like TAPI * * ARGUMENTS: * pstPrivate -- The driver data structure * * RETURNS: * COM_OK if port is configured successfully * COM_DEVICE_ERROR if API errors are encountered */ int PortDefaultSettings(ST_STDCOM *pstPrivate) { int iRetVal; DWORD dwError; COMMCONFIG stCommConfig; DWORD dwSize = sizeof(stCommConfig); TCHAR szPortName[COM_MAX_PORT_NAME];
if (pstPrivate == NULL || pstPrivate->hCom == NULL) { iRetVal = COM_INVALID_HANDLE; } else { ComGetPortName(pstPrivate->hCom, szPortName, COM_MAX_PORT_NAME);
if (StrCharGetStrLength(szPortName) == 0 || StrCharCmp(szPortName, "\0") == 0) { iRetVal = COM_DEVICE_ERROR; } else { if (!GetDefaultCommConfig(szPortName, &stCommConfig, &dwSize)) { dwError = GetLastError(); //* DeviceReportError(pstPrivate, SID_ERR_WINDRIVER, 0, TRUE);
iRetVal = COM_DEVICE_ERROR; } else { // Unload appropriate values from DCB to our settings structure
ComstdDCBToSettings(&stCommConfig.dcb, &pstPrivate->stWorkSettings);
// Don't leave autodetect on if user has already set something
// other than 8N1
DBG_AD("DBG_AD: fAutoDetect = %d\r\n", pstPrivate->stWorkSettings.fAutoDetect, 0,0,0,0); if (pstPrivate->stWorkSettings.fAutoDetect) { if (pstPrivate->stWorkSettings.nDataBits != 8 || pstPrivate->stWorkSettings.nParity != NOPARITY || pstPrivate->stWorkSettings.nStopBits != ONESTOPBIT) { DBG_AD("DBG_AD: Turning fAutoDetect off due to non 8N1\r\n", 0,0,0,0,0); pstPrivate->stWorkSettings.fAutoDetect = FALSE; } } } }
iRetVal = COM_OK; }
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: PortConnected * * DESCRIPTION: * Determines whether the driver is currently connected to a host system. * In the case of this driver, the presence of the carrier signal determines * when we are connected. * * ARGUMENTS: * pstPrivate -- Our private data structure * * RETURNS: * TRUE if carrier is present * FALSE if carrier is off */ int WINAPI PortConnected(ST_STDCOM *pstPrivate) { int iRetVal = COM_PORT_NOT_OPEN; DWORD dwModemStat;
if (GetCommModemStatus(pstPrivate->hWinComm, &dwModemStat)) { if (bittest(dwModemStat, MS_RLSD_ON)) { iRetVal = COM_PORT_OPEN; } }
if (iRetVal == COM_PORT_NOT_OPEN) { const HCOM hCom = pstPrivate->hCom;
if (hCom != NULL && ComValidHandle(hCom)) { const HHCNCT hhCnct = (HHCNCT)sessQueryCnctHdl(hCom->hSession); if (hhCnct != NULL) { const HHDRIVER hhDriver = (HHDRIVER)hhCnct->hDriver;
if (hhDriver != NULL) { if (!hhDriver->fCarrierDetect || bittest(hhDriver->stCallPar.dwBearerMode, LINEBEARERMODE_PASSTHROUGH)) { iRetVal = COM_PORT_OPEN; } } } } }
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: RcvRefill * * DESCRIPTION: * Called when the receive buffer is empty to refill it. This routine * should attempt to refill the buffer and return the first character. * It is important that this function be implemented efficiently. * * ARGUMENTS: * pstPrivate -- the driver data structure * * RETURNS: * TRUE if data is put in the receive buffer * FALSE if there is no new incoming data */ int WINAPI RcvRefill(ST_STDCOM *pstPrivate) { int fRetVal = FALSE; ST_COM_CONTROL *pstComCntrl; HCOM hCom;
EnterCriticalSection(&pstPrivate->csect);
pstPrivate->pbComStart = (pstPrivate->pbComEnd == pstPrivate->pbBufrEnd) ? pstPrivate->pbBufrStart : pstPrivate->pbComEnd; pstPrivate->pbComEnd = (pstPrivate->pbReadEnd >= pstPrivate->pbComStart) ? pstPrivate->pbReadEnd : pstPrivate->pbBufrEnd; DBG_READ("DBG_READ: Refill ComStart==%x, ComEnd==%x (ReadEnd==%x)\r\n", pstPrivate->pbComStart, pstPrivate->pbComEnd, pstPrivate->pbReadEnd, 0,0); if (pstPrivate->fBufrFull) { DBG_READ("DBG_READ: Refill Signalling EVENT_READ\r\n", 0,0,0,0,0); SetEvent(pstPrivate->ahEvent[EVENT_READ]); } if (pstPrivate->pbComStart == pstPrivate->pbComEnd) { DBG_READ("DBG_READ: Refill setting fBufrEmpty = TRUE\r\n", 0,0,0,0,0); pstPrivate->fBufrEmpty = TRUE;
hCom = pstPrivate->hCom; LeaveCriticalSection(&pstPrivate->csect); ComNotify(hCom, NODATA); EnterCriticalSection(&pstPrivate->csect); } else { pstComCntrl = (ST_COM_CONTROL *)pstPrivate->hCom; pstComCntrl->puchRBData = pstPrivate->pbComStart; pstComCntrl->puchRBDataLimit = pstPrivate->pbComEnd;
#if defined(DEBUG_CHARDUMP)
{ int iAvail; int iCnt;
iAvail = (int) pstComCntrl->puchRBDataLimit - pstComCntrl->puchRBData; fprintf(pfDbgC, "Consumed -- %d bytes 0x%p to 0x%p:", iAvail, pstComCntrl->puchRBData, pstComCntrl->puchRBDataLimit - 1); for (iCnt = 0; iCnt < iAvail; ++iCnt) { if ((iCnt % 16) == 0) fputs("\n", pfDbgC); fprintf(pfDbgC, "%02X ", pstComCntrl->puchRBData[iCnt]); } fputs("\n", pfDbgC); } #endif
// If this com driver were being used to make the connection, we
// would have to check here to see whether we were connected before
// we called AutoDetect. Since TAPI takes care of making the
// connection for this app, we can just start auto detecting
// whenever we get control
if (pstPrivate->stWorkSettings.fAutoDetect) { AutoDetectAnalyze(pstPrivate, (int)(pstPrivate->pbComEnd - pstPrivate->pbComStart), pstPrivate->pbComStart); } fRetVal = TRUE; }
LeaveCriticalSection(&pstPrivate->csect); return fRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: RcvClear * * DESCRIPTION: * Clears the receiver of all received data. * * ARGUMENTS: * hCom -- a comm handle returned by an earlier call to ComCreateHandle * * RETURNS: * COM_OK if data is cleared * COM_DEVICE_ERROR if Windows com device driver returns an error */ int WINAPI RcvClear(ST_STDCOM *pstPrivate) { int iRetVal = COM_OK; ST_COM_CONTROL *pstComCntrl;
EnterCriticalSection(&pstPrivate->csect);
pstComCntrl = (ST_COM_CONTROL *)pstPrivate->hCom;
// Set buffer pointers to clear out any data we might have queued
pstComCntrl->puchRBData = pstPrivate->pbBufrStart; pstComCntrl->puchRBDataLimit = pstPrivate->pbBufrStart; pstPrivate->pbReadEnd = pstPrivate->pbBufrStart; pstPrivate->pbComStart = pstPrivate->pbBufrStart; pstPrivate->pbComEnd = pstPrivate->pbBufrStart;
if (!PurgeComm(pstPrivate->hWinComm, PURGE_RXCLEAR | PURGE_RXABORT)) iRetVal = COM_DEVICE_ERROR;
LeaveCriticalSection(&pstPrivate->csect); return iRetVal; }
// Buffered send routines
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: SndBufrSend * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ int WINAPI SndBufrSend(ST_STDCOM *pstPrivate, void *pvBufr, int nSize) { int iRetVal = COM_OK; DWORD dwBytesWritten; DWORD dwError; HCOM hCom;
assert(pvBufr != (void *)0); assert(nSize <= SIZE_OUTQ);
if (nSize > 0) { EnterCriticalSection(&pstPrivate->csect);
// If Auto Detection is on, we may need to manually alter the
// parity of the output
if (pstPrivate->stWorkSettings.fAutoDetect) { AutoDetectOutput(pstPrivate, pvBufr, nSize); }
hCom = pstPrivate->hCom; LeaveCriticalSection(&pstPrivate->csect); ComNotify(hCom, SEND_STARTED);
#if defined(DEADWOOD)
// We had a bug that caused the session to stop displaying new characters when
// you used auto-connect to start typing to a modem on Win 95. I tracked it down
// to this point in the code by moving a debug trace statement around. Put it
// just before this EnterCriticalSection and the bug goes away. Put it just after
// and the bug comes back. I discovered that replacing the DbgOutStr with a Sleep(0)
// had the same effect. This is a cheap fix but seems to work. We may want to
// spend the time to figure out exactly what is going on sometime in the future.
//jkh 9/9/98
Sleep(0);
//
// JohnFu 2/13/02, the EnterCriticalSection was just below this Sleep(0) now
// moved to top of the if statement. It was possible for other threads to
// modify the members of pstPrivate anytime between the above three statements.
// That may be the original cause of the bug described above. The reason the Sleep(0)
// fixed that bug further proves that possibility. There should be no need to
// place the Sleep here any longer.
// I added the sleep back in to see if the file transfer issues we
// were seeing with large file transfers would be corrected by adding
// the sleep back in. REV: 4/8/2002
//
// Made this Sleep(0) as deadwood since this may cause the deadlock
// to reappear. This did not correct the large file transfer problem
// we were seeing. REV: 7/11/2002
//
#endif // defined(DEADWOOD)
EnterCriticalSection(&pstPrivate->csect); assert(pstPrivate->dwBytesToSend == 0); assert(pstPrivate->dwSndOffset == 0); assert((pstPrivate->dwSndOffset + nSize) <= SIZE_OUTQ);
#if defined(TODO)
//
// TODO:REV 7/11/2002 this is where the Ymodem-G is having problems and we are
// getting retries in file transfers. When the pstPrivate->dwBytesToSend is != 0,
// or the pstPrivate->dwSndOffset != 0, then we are overwriting the character
// that is existing in the buffer. We need to make sure we don't overwrite the
// buffer and/or clobber existing data in the buffer.
//
MemCopy(&pstPrivate->pbSndBufr[pstPrivate->dwSndOffset], (BYTE*) pvBufr, nSize); pstPrivate->dwBytesToSend += nSize; //pstPrivate->dwSndOffset = 0;
#else // defined(TODO)
MemCopy(pstPrivate->pbSndBufr, (BYTE*) pvBufr, nSize); pstPrivate->dwBytesToSend = nSize; pstPrivate->dwSndOffset = 0; #endif // defined(TODO)
pstPrivate->stWriteOv.Offset = pstPrivate->stWriteOv.OffsetHigh = 0; pstPrivate->stWriteOv.hEvent = pstPrivate->ahEvent[EVENT_WRITE];
DBG_WRITE("DBG_WRITE: %d WriteFile nSize==%d 0x%x\r\n", GetTickCount(),nSize,pstPrivate->hWinComm,0,0); // jmh:01-12-96 When the OVERLAPPED structure is passed to WriteFile,
// there is character loss. Thorough investigation indicates a problem
// within Win32 comm. Documentation says behavior is undefined when
// this structure is not passed, but it works.
if (WriteFile(pstPrivate->hWinComm, pstPrivate->pbSndBufr, (DWORD)nSize, &dwBytesWritten, &pstPrivate->stWriteOv)) // mrw:12/6/95 restored stWriteOv
{ assert(dwBytesWritten == (DWORD)nSize); DBG_WRITE("DBG_WRITE: %d WriteFile completed synchronously\r\n",GetTickCount(),0,0,0,0);
hCom = pstPrivate->hCom; LeaveCriticalSection(&pstPrivate->csect); ComNotify(hCom, SEND_DONE); EnterCriticalSection(&pstPrivate->csect); pstPrivate->dwBytesToSend = 0; } else { dwError = GetLastError(); if (dwError == ERROR_IO_PENDING) { pstPrivate->fSending = TRUE; } else { iRetVal = COM_FAILED; DBG_WRITE("DBG_WRITE: %d WriteFile failed %d 0x%x\r\n", GetTickCount(),dwError,pstPrivate->hWinComm,0,0); } }
LeaveCriticalSection(&pstPrivate->csect); }
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: SndBufrIsBusy * * DESCRIPTION: * Determines whether the driver is available to transmit a buffer of * data or not. * * ARGUMENTS: * pstPrivate -- address of com driver's data structure * * RETURNS: * COM_OK if data can be transmitted * COM_BUSY if driver is still working on a previous buffer */ int WINAPI SndBufrIsBusy(ST_STDCOM *pstPrivate) { int iRetVal = COM_OK;
EnterCriticalSection(&pstPrivate->csect);
if (pstPrivate->fBreakSignalOn || pstPrivate->fSending) { iRetVal = COM_BUSY; }
LeaveCriticalSection(&pstPrivate->csect);
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: SndBufrQuery * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ int WINAPI SndBufrQuery(ST_STDCOM *pstPrivate, unsigned *pafStatus, long *plHandshakeDelay) { int iRetVal = COM_OK; DWORD dwErrors; COMSTAT stComStat;
assert(pafStatus != NULL);
*pafStatus = 0;
//* temporary
if (!SndBufrIsBusy(pstPrivate)) { // If no send is in progress, return clear status
*pafStatus = 0; if (plHandshakeDelay) *plHandshakeDelay = 0L; } else { if (ClearCommError(pstPrivate->hWinComm, &dwErrors, &stComStat)) { if (stComStat.fXoffHold) bitset(*pafStatus, COMSB_WAIT_XON); if (stComStat.fCtsHold) bitset(*pafStatus, COMSB_WAIT_CTS); if (stComStat.fDsrHold) bitset(*pafStatus, COMSB_WAIT_DSR); if (stComStat.fRlsdHold) bitset(*pafStatus, COMSB_WAIT_DCD); if (stComStat.fXoffSent) bitset(*pafStatus, COMSB_WAIT_BUSY);
if (*pafStatus && pstPrivate->lSndStuck == -1L) pstPrivate->lSndStuck = (long)startinterval();
if (plHandshakeDelay) *plHandshakeDelay = (pstPrivate->lSndStuck == -1L ? 0L : (long)interval(pstPrivate->lSndStuck)); } }
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: SndBufrClear * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ int WINAPI SndBufrClear(ST_STDCOM *pstPrivate) { int iRetVal = COM_OK;
EnterCriticalSection(&pstPrivate->csect); if (SndBufrIsBusy(pstPrivate)) { if (!PurgeComm(pstPrivate->hWinComm, PURGE_TXCLEAR | PURGE_TXABORT)) iRetVal = COM_DEVICE_ERROR; } LeaveCriticalSection(&pstPrivate->csect);
return iRetVal; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: ComstdThread * * DESCRIPTION: * This thread services three events, reads, writes and rithmatic, uh * no, I mean comm events. It uses overlapped I/O to accomplish this * task which simplifies the task since thread contention between the * different events is eliminated. * * ARGUMENTS: * pvData - pointer to private comm handle * * RETURNS: * Eventually * */ DWORD WINAPI ComstdThread(void *pvData) { ST_STDCOM *pstPrivate = (ST_STDCOM *)pvData; DWORD dwResult = WAIT_OBJECT_0; DWORD dwError; DWORD dwBytes; DWORD dwComEvent = 0; long lBytesRead; long lReadSize; BYTE *pbReadFrom; OVERLAPPED stReadOv; OVERLAPPED stEventOv; COMSTAT stComStat; HANDLE *pEvents; HCOM hCom; #if defined(DEBUG_CHARDUMP)
int iCnt; #endif
DWORD dwEvents;
DBG_THREAD("DBG_THREAD: ComstdThread starting\r\n",0,0,0,0,0); EnterCriticalSection(&pstPrivate->csect);
// Set Read event to signaled to get the first Read operation going
//
pstPrivate->fBufrFull = TRUE; SetEvent(pstPrivate->ahEvent[EVENT_READ]);
// Set ComEvent event to signaled to to get the first WaitCommEvent
// started
//
SetEvent(pstPrivate->ahEvent[EVENT_COMEVENT]);
// Clear any set state left by a previous connection.
//
ResetEvent(pstPrivate->ahEvent[EVENT_WRITE]);
for ( ; ; ) { dwEvents = DIM(pstPrivate->ahEvent); pEvents = pstPrivate->ahEvent; LeaveCriticalSection(&pstPrivate->csect); DBG_THREAD("DBG_THREAD: Waiting\r\n", 0,0,0,0,0);
dwResult = WaitForMultipleObjects(dwEvents, pEvents, FALSE, INFINITE);
DBG_THREAD("DBG_THREAD: WaitForMultipleObjects returned %d\r\n", dwResult,0,0,0,0);
EnterCriticalSection(&pstPrivate->csect);
// To get this thread to exit, the deactivate routine forces a
// fake com event by calling SetCommMask
//
if (pstPrivate->fHaltThread) { LeaveCriticalSection(&pstPrivate->csect); DBG_THREAD("DBG_THREAD: ComStd exiting thread\r\n",0,0,0,0,0); ExitThread(0); }
switch (dwResult) { case WAIT_OBJECT_0 + EVENT_READ: if (pstPrivate->fBufrFull) { DBG_READ("DBG_READ: Thread -- fBufrFull = FALSE\r\n", 0,0,0,0,0);
pstPrivate->fBufrFull = FALSE; } else { if (GetOverlappedResult(pstPrivate->hWinComm, &stReadOv, (DWORD *)&lBytesRead, FALSE)) {
pstPrivate->pbReadEnd += lBytesRead; #if defined(DEBUG_CHARDUMP)
if (lBytesRead > 0) { fprintf(pfDbgR, "Overlapped Read -- %d bytes 0x%p to 0x%p:", lBytesRead, pbReadFrom, pstPrivate->pbReadEnd - 1); for (iCnt = 0; iCnt < lBytesRead; ++iCnt) { if ((iCnt % 16) == 0) fputs("\n", pfDbgR); fprintf(pfDbgR, "%02X ", pbReadFrom[iCnt]); } fputs("\n", pfDbgR); } #endif
if (pstPrivate->pbReadEnd >= pstPrivate->pbBufrEnd) { pstPrivate->pbReadEnd = pstPrivate->pbBufrStart; }
DBG_READ("DBG_READ: Thread -- got %ld, ReadEnd==%x\r\n", lBytesRead, pstPrivate->pbReadEnd,0,0,0);
if (pstPrivate->fBufrEmpty) { DBG_READ("DBG_READ: Thread -- fBufrEmpty = FALSE\r\n", 0,0,0,0,0);
pstPrivate->fBufrEmpty = FALSE;
hCom = pstPrivate->hCom; LeaveCriticalSection(&pstPrivate->csect); ComNotify(hCom, DATA_RECEIVED); EnterCriticalSection(&pstPrivate->csect); } } else { switch (GetLastError()) { case ERROR_OPERATION_ABORTED: // Operations can be aborted by calls to PurgeComm()
// Allow setup for another read request.
// mrw:12/14/95
//
break;
default: // Com is failing for some reason. Exit thread
// so that we don't tie-up resources.
//
DBG_EVENTS("DBG_EVENTS: GetOverlappedResult " "failed!\r\n",0,0,0,0,0);
LeaveCriticalSection(&pstPrivate->csect); ExitThread(0); } } }
// Do reads until we fill the buffer or we get an overlapped read
//
for ( ; ; ) { // Check for wrap around in circular buffer
//
pbReadFrom = (pstPrivate->pbReadEnd >= pstPrivate->pbBufrEnd) ? pstPrivate->pbBufrStart : pstPrivate->pbReadEnd;
#if 0 // mrw:10/7/96 - enabled shiva fix for NT 4.0 Service Pack
// Enabled for NT 4.0 release. Per Microsoft, leave this bug
// in, so US and international versions are identical. It was
// found between US and international releases.
//
// This was causing bad packets in Zmodem transfers when
// using Shiva's LanRover, which appeared at baud rates
// of 57600 or higher, and using TCP/IP to connect to the
// LanRover. lReadSize in this code would not leave an
// unused byte at the end of the buffer if pbComStart was
// pointing to the beginning of the buffer.
// - jmh 07-31-96
lReadSize = (pbReadFrom < pstPrivate->pbComStart) ? (pstPrivate->pbComStart - pbReadFrom - 1) : (pstPrivate->pbBufrEnd - pbReadFrom); #else
// Determine the extent to which we're allowed to fill the
// buffer. pbComStart points to the start of where the buffer
// is "reserved", waiting to be emptied from. We make sure we
// leave the byte before pbComStart empty. - jmh 07-31-96
//
if (pbReadFrom < pstPrivate->pbComStart) { lReadSize = (long)(pstPrivate->pbComStart - pbReadFrom - 1); } else { lReadSize = (long)(pstPrivate->pbBufrEnd - pbReadFrom); // The circular buffer code was written so that the address
// pointed to by pbBufrEnd is equated with pbBufrStart. We
// also need to make sure that if we've just calculated
// that we can read to the end of the buffer (aka the
// *start* of the buffer), and pbComStart is pointing to
// the start of the buffer, there's still an empty byte
// before pbComStart. - jmh 07-31-96
//
if (pstPrivate->pbComStart == pstPrivate->pbBufrStart) lReadSize -= 1; } #endif
if (lReadSize > MAX_READSIZE) { lReadSize = MAX_READSIZE; }
if (lReadSize == 0) { DBG_READ("DBG_READ: Thread -- fBufrFull = TRUE, " "unsignalling EVENT_READ\r\n",0,0,0,0,0);
pstPrivate->fBufrFull = TRUE; ResetEvent(pstPrivate->ahEvent[EVENT_READ]); break; } else { // Set up to do an overlapped read. From what I can make
// of the documenation, this may or may not complete
// immediately. So, to be safe, I will code it to expect
// either result.
//
stReadOv.Offset = stReadOv.OffsetHigh = 0; stReadOv.hEvent = pstPrivate->ahEvent[EVENT_READ];
DBG_READ("DBG_READ: Thread -- ReadFile started, " "ReadFrom==%x, ReadSize==%ld\r\n", pbReadFrom, lReadSize, 0,0,0);
// ReadFile resets the read event semaphore
//
if (ReadFile(pstPrivate->hWinComm, pbReadFrom, (DWORD)lReadSize, (DWORD *)&lBytesRead, &stReadOv)) { pstPrivate->pbReadEnd += lBytesRead;
#if defined(DEBUG_CHARDUMP)
fprintf(pfDbgR, "Overlapped Read -- %d bytes 0x%p to 0x%p:", lBytesRead, pbReadFrom, pstPrivate->pbReadEnd - 1); for (iCnt = 0; iCnt < lBytesRead; ++iCnt) { if ((iCnt % 16) == 0) fputs("\n", pfDbgR); fprintf(pfDbgR, "%02X ", pbReadFrom[iCnt]); } fputs("\n", pfDbgR); #endif
if (pstPrivate->pbReadEnd >= pstPrivate->pbBufrEnd) pstPrivate->pbReadEnd = pstPrivate->pbBufrStart;
DBG_READ("DBG_READ: Thread -- ReadFile completed " "synchronously, lBytesRead==%ld, ReadEnd==%x\r\n", lBytesRead, pstPrivate->pbReadEnd,0,0,0);
if (pstPrivate->fBufrEmpty) { DBG_READ("DBG_READ: Thread -- fBufrEmpty = " "FALSE\r\n", 0,0,0,0,0);
pstPrivate->fBufrEmpty = FALSE;
hCom = pstPrivate->hCom; LeaveCriticalSection(&pstPrivate->csect); ComNotify(hCom, DATA_RECEIVED); EnterCriticalSection(&pstPrivate->csect); } } else { switch (GetLastError()) { case ERROR_IO_PENDING: break;
case ERROR_OPERATION_ABORTED: // PurgeComm can do this. Setup for another read.
// mrw:12/14/95
// But clear errors or the read may fail from
// now to eternity! We're in an infinite for-loop,
// after all. jmh:06-12-96
ClearCommError(pstPrivate->hWinComm, &dwError, &stComStat); continue;
default: // Com is failing for some reason. Exit thread
// so that we don't tie-up resources.
//
DBG_READ("DBG_READ: ReadFile failed!\r\n", 0,0,0,0,0);
LeaveCriticalSection(&pstPrivate->csect); ExitThread(0); }
break; // Come back when event signals
} } } break;
case WAIT_OBJECT_0 + EVENT_WRITE: if (GetOverlappedResult(pstPrivate->hWinComm, &pstPrivate->stWriteOv, &dwBytes, FALSE) == FALSE) { dwError = GetLastError(); DBG_WRITE("DBG_WRITE: %d Overlapped WriteFile failed: errno=%d\n", GetTickCount(), dwError, 0, 0, 0); } else if (dwBytes < pstPrivate->dwBytesToSend && dwBytes != 0) { // ResetEvent(pstPrivate->ahEvent[EVENT_WRITE]);
DBG_WRITE("DBG_WRITE: %d Write result -- dwBytes==%d\r\n", GetTickCount(),dwBytes,0,0,0);
// There's more to write. Seems kinda silly, but WriteFile
// will return a success code, and dwBytes will show
// there's still stuff to write. So we make another call
// to WriteFile for what's remaining. Perhaps the write
// timeout is too short. This happens for slower baud rates
//
pstPrivate->dwBytesToSend -= dwBytes; pstPrivate->dwSndOffset += dwBytes;
pstPrivate->stWriteOv.Offset = pstPrivate->stWriteOv.OffsetHigh = 0; pstPrivate->stWriteOv.hEvent = pstPrivate->ahEvent[EVENT_WRITE];
DBG_WRITE("DBG_WRITE: %d WriteFile(2) nSize==%d 0x%x\r\n", GetTickCount(), pstPrivate->dwBytesToSend, pstPrivate->hWinComm, 0, 0); if (WriteFile(pstPrivate->hWinComm, &pstPrivate->pbSndBufr[pstPrivate->dwSndOffset], pstPrivate->dwBytesToSend, &dwBytes, &pstPrivate->stWriteOv)) { assert(dwBytes == pstPrivate->dwBytesToSend); DBG_WRITE("DBG_WRITE: %d WriteFile(2) completed synchronously\r\n", GetTickCount(),0,0,0,0); } else { dwError = GetLastError(); if (dwError == ERROR_IO_PENDING) { break; // This is what we expect
} else { DBG_WRITE("DBG_WRITE: %d WriteFile(2) failed %d 0x%x\r\n", GetTickCount(),dwError,pstPrivate->hWinComm,0,0); } } } else { // The write semaphore must be reset after the call to
// GetOverlappedResult, because it checks the semaphore
// to see if there's an outstanding write call. jmh 01-10-96
//
ResetEvent(pstPrivate->ahEvent[EVENT_WRITE]); }
DBG_WRITE("DBG_WRITE: %d Write result -- dwBytes==%d\r\n", GetTickCount(),dwBytes,0,0,0);
pstPrivate->dwBytesToSend = 0; pstPrivate->dwSndOffset = 0;
pstPrivate->fSending = FALSE;
hCom = pstPrivate->hCom; LeaveCriticalSection(&pstPrivate->csect); ComNotify(hCom, SEND_DONE); EnterCriticalSection(&pstPrivate->csect); break;
case WAIT_OBJECT_0 + EVENT_COMEVENT: // WaitCommEvent is returning an event flag
//
ResetEvent(pstPrivate->ahEvent[EVENT_COMEVENT]);
switch (dwComEvent) { case EV_ERR: ClearCommError(pstPrivate->hWinComm, &dwError, &stComStat);
DBG_EVENTS("DBG_EVENTS: EV_ERR dwError==%x\r\n", dwError,0,0,0,0);
//* need code here to record errors, handle HHS stuck etc.
break;
case EV_RLSD: // receive-line-signal-detect changed state.
hCom = pstPrivate->hCom; LeaveCriticalSection(&pstPrivate->csect); ComNotify(hCom, CONNECT); EnterCriticalSection(&pstPrivate->csect); DBG_EVENTS("DBG_EVENTS: EV_RLSD\r\n", 0,0,0,0,0); break;
default: DBG_EVENTS("DBG_EVENTS: EV_??? (dwComEvent==%x)\r\n", dwComEvent,0,0,0,0); break; }
// Start up another overlapped WaitCommEvent to get the
// next event
//
stEventOv.Offset = stEventOv.OffsetHigh = (DWORD)0; stEventOv.hEvent = pstPrivate->ahEvent[EVENT_COMEVENT];
if (WaitCommEvent(pstPrivate->hWinComm, &dwComEvent, &stEventOv)) { // Call completed synchronously, re-signal our event object
//
DBG_EVENTS("DBG_EVENTS: WaitCommEvent completed " "synchronously\r\n",0,0,0,0,0);
SetEvent(pstPrivate->ahEvent[EVENT_COMEVENT]); }
else { switch (GetLastError()) { case ERROR_IO_PENDING: break;
case ERROR_OPERATION_ABORTED: // Not sure this can happen but we'll code it like
// the read. - mrw:12/14/95
//
DBG_EVENTS("DBG_EVENTS: WaitCommEvent - " "ERROR_OPERATION_ABORTED\r\n",0,0,0,0,0);
SetEvent(pstPrivate->ahEvent[EVENT_COMEVENT]); break;
default: // Com is failing for some reason. Exit thread
// so that we don't tie-up resources.
//
DBG_EVENTS("DBG_EVENTS: WaitCommEvent failed!\r\n", 0,0,0,0,0);
LeaveCriticalSection(&pstPrivate->csect); ExitThread(0); } } break;
default: break; } }
LeaveCriticalSection(&pstPrivate->csect); DBG_THREAD("DBG_THREAD: ComstdThread exiting\r\n",0,0,0,0,0);
return (DWORD)0; }
/* --- AUTO DETECT ROUTINES --- */
static int Nibble[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0}; // 1=odd, 0=even
#define OddBits(b) (Nibble[(b) / 16] ^ Nibble[(b) % 16])
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * AutoDetectAnalyze * * DESCRIPTION: * Analyzes incoming data to determine the char size, parity type and * stop bits * * ARGUMENTS: * * * RETURNS: * */ void AutoDetectAnalyze(ST_STDCOM *pstPrivate, int nBytes, char *pchBufr) { char *pchScan = pchBufr; char *pszMsg; int fForceTo7Bits = FALSE; int iCnt = nBytes;
if (!pstPrivate->fADRunning) { AutoDetectStart(pstPrivate);
// This was a temporary fix I used while debugging a 7E1 problem that I decided
// to leave in because it may help in some situations and shouldn't hurt.
// In my case, when a GVC Fax 11400 V.42bis/MNP5 modem was installed as
// "Standard Modem", an initial 8N1 CRLF from the modem negotiation got through
// even when connecting to a 7E1 host. This made auto detect decide the whole
// connection was 8N1. I fixed it with this little patch. Once I reinstalled the
// modem as itself, it worked without the patch. This is not a rigourous fix
// because there is no guarantee that there will be only two extraneous characters
// or that they will be read all by themselves -- but this will fix the problems
// in some typical cases and will do no harm. jkh 9/9/98
if (iCnt <= 2) return; }
if (pstPrivate->nFramingErrors > 0) { DBG_AD("DBG_AD: Got Framing Errors: shutting down\r\n", 0,0,0,0,0); AutoDetectStop(pstPrivate); // MessageBox(NULL,
// "Would use Wizard code here. Either wrong baud rate "
// "is set or unusual settings have been encountered. "
// "Finished code may be able to handle some cases included here.",
// "Auto Detection Wizard", MB_OK);
return; }
pstPrivate->nADTotal += iCnt;
// for each byte, determine whether the lower 7 bits contain an odd
// number of 1 bits, then determine whether the byte would be a valid
// 7e1 character.
while (iCnt--) { if (OddBits(*pchScan & 0x7F)) ++pstPrivate->nADMix; if (OddBits(*pchScan)) ++pstPrivate->nAD7o1; if (*pchScan & 0x80) ++pstPrivate->nADHighBits; ++pchScan; }
// See whether we can make any decision with what we've got
if (pstPrivate->nADMix > 0 && pstPrivate->nADMix < pstPrivate->nADTotal) { // We now have both kinds of characters: those with an even and
// an odd number of bits in the lower 7 bits - so we can make
// a guess.
if (pstPrivate->nAD7o1 == pstPrivate->nADTotal) pstPrivate->nADBestGuess = AD_7O1; else if (pstPrivate->nAD7o1 == 0) pstPrivate->nADBestGuess = AD_7E1; else pstPrivate->nADBestGuess = AD_8N1; }
DBG_AD("DBG_AD: Cnt=%3d, Mix=%3d, 7o1=%3d, HB=%3d BG=%d\r\n", pstPrivate->nADTotal, pstPrivate->nADMix, pstPrivate->nAD7o1, pstPrivate->nADHighBits, pstPrivate->nADBestGuess);
// See whether we've checked a sufficient sample to determine settings
if (pstPrivate->nADBestGuess != AD_8N1 && (pstPrivate->nADTotal < MIN_AD_TOTAL || pstPrivate->nADMix < MIN_AD_MIX || (pstPrivate->nADTotal - pstPrivate->nADMix) < MIN_AD_MIX)) { // Data sample is insufficient to draw a conclusion.
// For now, let the data display as 7-bit data and wait for more
fForceTo7Bits = TRUE; } else { // We have enough data to make a decision
if (pstPrivate->nAD7o1 == 0) { // Data is 7-even-1
pstPrivate->stWorkSettings.nDataBits = 7; pstPrivate->stWorkSettings.nParity = EVENPARITY; fForceTo7Bits = TRUE; pstPrivate->fADReconfigure = TRUE; pszMsg = "Establishing settings of 7-Even-1"; } else if (pstPrivate->nAD7o1 == pstPrivate->nADTotal) { // Data is 7-odd-1
pstPrivate->stWorkSettings.nDataBits = 7; pstPrivate->stWorkSettings.nParity = ODDPARITY; fForceTo7Bits = TRUE; pstPrivate->fADReconfigure = TRUE; pszMsg = "Establishing settings of 7-Odd-1"; } else { // Data is most likely 8-none-1. But if the high bit was
// set on all the received data, it may have been 7-mark-1 or
// some other odd setting
pstPrivate->stWorkSettings.nDataBits = 8; pstPrivate->stWorkSettings.nParity = NOPARITY; if (pstPrivate->nADHighBits == pstPrivate->nADTotal) pszMsg = "Settings are either 8-none-1 or something quite " "odd like 7-mark-one. A wizard would pop up here" "asking the user if the data looked correct and" "offering suggestions if it did not."; else pszMsg = "Establishing settings of 8-none-1"; }
// Decision has been made, so turn auto detect off
DBG_AD("DBG_AD: %s\r\n", pszMsg, 0,0,0,0); AutoDetectStop(pstPrivate); if (pstPrivate->fADReconfigure) { DBG_AD("DBG_AD: Reconfiguring port\r\n", 0,0,0,0,0); PortConfigure(pstPrivate); } // MessageBox(NULL, pszMsg, "Auto Detection Done", MB_OK);
}
if (fForceTo7Bits) { while (nBytes--) { *pchBufr = (char)(*pchBufr & 0x7F); ++pchBufr; } } }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * AutoDetectOutput * * DESCRIPTION: * Checks state of auto detection and alters outgoing characters to * reflect the best guess of their parity status. * * ARGUMENTS: * * * RETURNS: * */ void AutoDetectOutput(ST_STDCOM *pstPrivate, void *pvBufr, int nSize) { char *pch = (char *)pvBufr;
if (!pstPrivate->fADRunning) AutoDetectStart(pstPrivate);
switch (pstPrivate->nADBestGuess) { case AD_8N1: // Do nothing
break;
case AD_7E1: // Make output look like 7e1
DBG_AD("DBG_AD: Converting %d output char(s) to 7E1\r\n", nSize, 0,0,0,0); while (nSize--) { if (OddBits(*pch & 0x7F)) *pch |= 0x80; ++pch; } break;
case AD_7O1: // Make output look like 7o1
DBG_AD("DBG_AD: Converting %d output char(s) to 7O1\r\n", nSize, 0,0,0,0); while (nSize--) { if (!OddBits(*pch & 0x7F)) *pch |= 0x80; ++pch; } break;
case AD_DONT_KNOW: // As long as the same single character is being sent
// out repeatedly, toggle the parity bit every other time
//
// This additional comment added. REV: 06/15/2001
//
// Some host systems that are 7-Even-1 or 7-Odd-1 require
// specific characters to be received in order to connect
// When HT is in AutoDetect mode, it is sending 8-None-1
// data, so the character that is sent will not be the
// character required to connect. In order to connect to
// the 7-Even-1 or 7-Odd-1 system, we must set the parity
// bit on the outbound character *pch = (*pch ^ (char)0x80);
// which will display a garbage character in HT. By setting
// the parity bit HT is now sending 7-Even-1 or 7-Odd-1 data
// depending on the character.
//
// An example is a 7E1 Genie systems require the user to
// enter <Ctrl>u or <Return> multiple times in order to
// connect.
//
if (nSize != 1) { pstPrivate->chADLastChar = '\0'; pstPrivate->fADToggleParity = FALSE; } else { if (*pch != pstPrivate->chADLastChar) { pstPrivate->chADLastChar = *pch; pstPrivate->fADToggleParity = FALSE; } else { if (pstPrivate->fADToggleParity) *pch = (*pch ^ (char)0x80); pstPrivate->fADToggleParity = !pstPrivate->fADToggleParity; } } break;
default: assert(FALSE); break; } }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * AutoDetectStart * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ void AutoDetectStart(ST_STDCOM *pstPrivate) { DBG_AD("DBG_AD: AutoDetectStart\r\n", 0,0,0,0,0); pstPrivate->nADTotal = 0; pstPrivate->nADMix = 0; pstPrivate->nAD7o1 = 0; pstPrivate->nADHighBits = 0; pstPrivate->nADBestGuess = AD_DONT_KNOW; pstPrivate->fADRunning = TRUE; pstPrivate->chADLastChar = '\0'; pstPrivate->fADToggleParity = FALSE; pstPrivate->fADReconfigure = FALSE; pstPrivate->nFramingErrors = 0; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * AutoDetectStop * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ void AutoDetectStop(ST_STDCOM *pstPrivate) { HSESSION hSession;
DBG_AD("DBG_AD: AutoDetectStop\r\n", 0,0,0,0,0); pstPrivate->stWorkSettings.fAutoDetect = FALSE; pstPrivate->fADRunning = FALSE;
ComGetSession(pstPrivate->hCom, &hSession);
PostMessage(sessQueryHwndStatusbar(hSession), SBR_NTFY_REFRESH, (WPARAM)SBR_COM_PART_NO, 0); }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * ComstdGetAutoDetectResults * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ int ComstdGetAutoDetectResults(void *pvData, BYTE *bByteSize, BYTE *bParity, BYTE *bStopBits) { ST_STDCOM *pstPrivate = (ST_STDCOM *)pvData;
assert(bByteSize); assert(bParity); assert(bStopBits);
if (pstPrivate->fADReconfigure) { *bByteSize = (BYTE)pstPrivate->stWorkSettings.nDataBits; *bParity = (BYTE)pstPrivate->stWorkSettings.nParity; *bStopBits = (BYTE)pstPrivate->stWorkSettings.nStopBits; } DBG_AD("DBG_AD: ComstdGetAutoDetectResults returning %d\r\n", pstPrivate->fADReconfigure, 0,0,0,0); DBG_AD(" (bits = %d, parity = %d, stops = %d)\r\n", *bByteSize, *bParity, *bStopBits, 0,0); return pstPrivate->fADReconfigure; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * ComstdSettingsToDCB * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ static void ComstdSettingsToDCB(ST_STDCOM_SETTINGS *pstSettings, DCB *pstDcb) { unsigned afHandshake;
afHandshake = pstSettings->afHandshake;
// fill in device control block
pstDcb->BaudRate = (DWORD)pstSettings->lBaud; pstDcb->fBinary = 1; pstDcb->fParity = 1; pstDcb->fOutxCtsFlow = (BYTE)((bittest(afHandshake, HANDSHAKE_SND_CTS)) ? 1 : 0); pstDcb->fOutxDsrFlow = (BYTE)(bittest(afHandshake, HANDSHAKE_SND_DSR) ? 1 : 0); pstDcb->fDtrControl = bittest(afHandshake, HANDSHAKE_RCV_DTR) ? DTR_CONTROL_HANDSHAKE : DTR_CONTROL_ENABLE; pstDcb->fDsrSensitivity = 0; pstDcb->fTXContinueOnXoff = TRUE; pstDcb->fOutX = (BYTE)(bittest(afHandshake, HANDSHAKE_SND_X) ? 1 :0); pstDcb->fInX = (BYTE)(bittest(afHandshake, HANDSHAKE_RCV_X) ? 1 :0); pstDcb->fErrorChar = 0; pstDcb->fNull = 0; pstDcb->fRtsControl = bittest(afHandshake, HANDSHAKE_RCV_RTS) ? RTS_CONTROL_HANDSHAKE : RTS_CONTROL_ENABLE; pstDcb->fAbortOnError = 1; // so we can count all errors
pstDcb->XonLim = 80; pstDcb->XoffLim = 200; pstDcb->ByteSize = (BYTE)pstSettings->nDataBits; pstDcb->Parity = (BYTE)pstSettings->nParity; pstDcb->StopBits = (BYTE)pstSettings->nStopBits; pstDcb->XonChar = pstSettings->chXON; pstDcb->XoffChar = pstSettings->chXOFF;
}
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: * ComstdDCBToSettings * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ static void ComstdDCBToSettings(DCB *pstDcb, ST_STDCOM_SETTINGS *pstSettings) { pstSettings->lBaud = (long)pstDcb->BaudRate; pstSettings->afHandshake = 0; if (pstDcb->fOutxCtsFlow) bitset(pstSettings->afHandshake, HANDSHAKE_SND_CTS); if (pstDcb->fOutxDsrFlow) bitset(pstSettings->afHandshake, HANDSHAKE_SND_DSR); if (pstDcb->fDtrControl == DTR_CONTROL_HANDSHAKE) bitset(pstSettings->afHandshake, HANDSHAKE_RCV_DTR); if (pstDcb->fOutX) bitset(pstSettings->afHandshake, HANDSHAKE_SND_X); if (pstDcb->fInX) bitset(pstSettings->afHandshake, HANDSHAKE_RCV_X); if (pstDcb->fRtsControl == RTS_CONTROL_HANDSHAKE) bitset(pstSettings->afHandshake, HANDSHAKE_RCV_RTS); pstSettings->nDataBits = pstDcb->ByteSize; pstSettings->nParity = pstDcb->Parity; pstSettings->nStopBits = pstDcb->StopBits; pstSettings->chXON = pstDcb->XonChar; pstSettings->chXOFF = pstDcb->XoffChar; }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceBreakTimerProc * * DESCRIPTION: * Called when the break timer goes off. A timer is started whenever we * set the break signal on. It goes off after the break signal duration. * This function clears the break signal and destroys the timer * * ARGUMENTS: * dwData -- A value stored when the timer is created. Contains pstPrivate * * RETURNS: * */ static void DeviceBreakTimerProc(void *pvData, long ulSince) { ST_STDCOM *pstPrivate = (ST_STDCOM *)pvData;
if (pstPrivate) { TimerDestroy(&pstPrivate->hTmrBreak); // this is a one-shot op
ClearCommBreak(pstPrivate->hWinComm); // have Win comm driver do it
pstPrivate->fBreakSignalOn = FALSE; }
return; }
#if 0
void StdcomRecordErrors(ST_STDCOM *pstPrivate, int iErrorBits) { if (bittest(iErrorBits, CE_FRAME | CE_OVERRUN | CE_RXOVER | CE_RXPARITY)) { if (bittest(iErrorBits, CE_FRAME)) ++pstPrivate->nFramingErrors;
if (bittest(iErrorBits, CE_OVERRUN)) ++pstPrivate->nOverrunErrors;
if (bittest(iErrorBits, CE_RXOVER)) ++pstPrivate->nOverflowErrors;
if (bittest(iErrorBits, CE_RXPARITY)) ++pstPrivate->nParityErrors; } }
/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
* FUNCTION: DeviceReportError * * DESCRIPTION: * * * ARGUMENTS: * * * RETURNS: * */ void DeviceReportError(ST_STDCOM *pstPrivate, UINT uiStringID, LPSTR pszOptInfo, BOOL fFirstOnly) { CHAR szFmtString[250]; CHAR szErrString[250];
if (LoadString(hinstDLL, uiStringID, szFmtString, sizeof(szFmtString) / sizeof(TCHAR)) > 0) { wsprintf(szErrString, szFmtString, pszOptInfo); ComReportError(pstPrivate->hCom, 0, szErrString, fFirstOnly); } }
#endif
|