Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2828 lines
88 KiB

/* 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