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