mirror of https://github.com/tongzx/nt5src
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.
2633 lines
85 KiB
2633 lines
85 KiB
/***************************************************************************
|
|
Name : FCOM.C
|
|
Comment : Functions for dealing with Windows Comm driver
|
|
|
|
Revision Log
|
|
Num Date Name Description
|
|
--- -------- ---------- -----------------------------------------------
|
|
***************************************************************************/
|
|
|
|
#include "prep.h"
|
|
|
|
|
|
#include <comdevi.h>
|
|
#include "fcomapi.h"
|
|
#include "fcomint.h"
|
|
#include "fdebug.h"
|
|
|
|
|
|
#ifdef MDRV // to check for conflicts
|
|
#include "..\class1\class1.h"
|
|
#endif
|
|
#include <filet30.h> // for registry info.
|
|
|
|
///RSL
|
|
#include "t30gl.h"
|
|
#include "glbproto.h"
|
|
|
|
|
|
|
|
#ifdef ADAPTIVE_ANSWER
|
|
# pragma message("Compiling with ADAPTIVE_ANSWER")
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
void d_TimeStamp(LPSTR lpsz, DWORD dwID);
|
|
# define TIMESTAMP(str, id)\
|
|
d_TimeStamp(str, (DWORD)(id))
|
|
#else // !DEBUG
|
|
# define TIMESTAMP(str, id)
|
|
#endif // !DEBUG
|
|
|
|
|
|
// in ms
|
|
#define TIME_CONTROL 50
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
# define ST_FC(x) if(ZONE_FC) { x; }
|
|
#else
|
|
# define ST_FC(x) { }
|
|
#endif
|
|
|
|
#define faxTlog(m) DEBUGMSG(ZONE_FC, m)
|
|
#define faxT2log(m) DEBUGMSG(ZONE_FC2, m)
|
|
#define faxT3log(m) DEBUGMSG(ZONE_FC3, m)
|
|
#define faxT4log(m) DEBUGMSG(ZONE_FC4, m)
|
|
|
|
|
|
|
|
/***------------- Local Vars and defines ------------------***/
|
|
|
|
|
|
|
|
#define LONG_DEADCOMMTIMEOUT 60000L
|
|
#define SHORT_DEADCOMMTIMEOUT 10000L
|
|
|
|
#define WAIT_FCOM_FILTER_FILLCACHE_TIMEOUT 120000
|
|
#define WAIT_FCOM_FILTER_READBUF_TIMEOUT 120000
|
|
|
|
|
|
// don't want DEADCOMMTIMEOUT to be greater than 32767, so make sure
|
|
// buf sizes are always 9000 or less. maybe OK.
|
|
|
|
#ifdef WIN32
|
|
// Our COMM timeout settings, used in call to SetCommTimeouts. These
|
|
// values (expect read_interval_timeout) are the default values for
|
|
// Daytona NT Beta 2, and seem to work fine..
|
|
#define READ_INTERVAL_TIMEOUT 100
|
|
#define READ_TOTAL_TIMEOUT_MULTIPLIER 0
|
|
#define READ_TOTAL_TIMEOUT_CONSTANT 0
|
|
#define WRITE_TOTAL_TIMEOUT_MULTIPLIER 0
|
|
#define WRITE_TOTAL_TIMEOUT_CONSTANT LONG_DEADCOMMTIMEOUT
|
|
#endif
|
|
|
|
#define CTRL_P 0x10
|
|
#define CTRL_Q 0x11
|
|
#define CTRL_S 0x13
|
|
|
|
|
|
|
|
#define MYGETCOMMERROR_FAILED 117437834L
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL FComDTR(PThrdGlbl pTG, BOOL fEnable)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ALL, "FComDTR = %d\r\n", fEnable));
|
|
|
|
if(MyGetCommState(pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "FaxDTR Before: %02x\r\n", pTG->Comm.dcb.fDtrControl));
|
|
pTG->Comm.dcb.fDtrControl = (fEnable ? DTR_CONTROL_ENABLE : DTR_CONTROL_DISABLE);
|
|
|
|
if(MySetCommState(pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "After: %02x\r\n", pTG->Comm.dcb.fDtrControl));
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> FaxDTR --- Can't Set/Get DCB\r\n"));
|
|
FComGetError(pTG);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL FComClose(PThrdGlbl pTG)
|
|
{
|
|
// Note: even if FComClose fails, pTG->Comm.nCid, pTG->Comm.fCommOpen,
|
|
// and pTG->Comm.fExternalHandle are all reset.
|
|
int nRet; // MUST be 16bit in WIN16 and 32bit in WIN32
|
|
BOOL fRet = TRUE;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "Closing Comm pTG->Comm.nCid=%d\r\n", pTG->Comm.nCid));
|
|
|
|
//
|
|
// handoff
|
|
//
|
|
if (pTG->Comm.fEnableHandoff && pTG->Comm.fDataCall) {
|
|
My2ndCloseComm(pTG->Comm.nCid, &nRet);
|
|
goto lEnd;
|
|
}
|
|
|
|
|
|
ST_FC(D_FComCheck(pTG, pTG->Comm.nCid));
|
|
|
|
#if 1
|
|
// We flush our internal buffer here...
|
|
if (pTG->Comm.lpovrCur)
|
|
{
|
|
int nNumWrote; // Must be 32bits in WIN32
|
|
if (!ov_write(pTG, pTG->Comm.lpovrCur, &nNumWrote))
|
|
{
|
|
// error...
|
|
(MyDebugPrint(pTG, LOG_ERR, "FComClose: 1st ov_write failed at %ld\n", GetTickCount() ));
|
|
|
|
}
|
|
BG_CHK (pTG->Comm.lpovrCur->eState==eFREE
|
|
|| pTG->Comm.lpovrCur->eState==eIO_PENDING);
|
|
pTG->Comm.lpovrCur=NULL;
|
|
(MyDebugPrint(pTG, LOG_ALL, "FComClose: done writing mybuf.\r\n"));
|
|
}
|
|
|
|
ov_drain(pTG, FALSE);
|
|
#endif
|
|
|
|
|
|
#ifdef METAPORT
|
|
if (1) // RSL pTG->Comm.fExternalHandle)
|
|
{
|
|
// Here we will restore settings to what it was when we
|
|
// took over the port. Currently (9/23/94) we (a) restore the
|
|
// DCB to pTG->Comm.dcbOrig and (b) If DTR was originally ON,
|
|
// try to sync the modem to
|
|
// the original speed by issueing "AT" -- because unimodem does
|
|
// only a half-hearted attempt at synching before giving up.
|
|
|
|
#ifdef ADAPTIVE_ANSWER
|
|
if(pTG->Comm.fStateChanged && (!pTG->Comm.fEnableHandoff || !pTG->Comm.fDataCall))
|
|
#else // !ADAPTIVE_ANSWER
|
|
if(pTG->Comm.fStateChanged)
|
|
#endif // !ADAPTIVE_ANSWER
|
|
{
|
|
if (MySetCommState(pTG->Comm.nCid, &(pTG->Comm.dcbOrig)))
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<WARNING>> FComClose --- Couldn't restor state. Err=0x%lx\r\n",
|
|
(unsigned long) GetLastError()));
|
|
}
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "FComClose restored DCB to Baud=%d, fOutxCtsFlow=%d, fDtrControl=%d, fOutX=%d\n",
|
|
pTG->Comm.dcbOrig.BaudRate,
|
|
pTG->Comm.dcbOrig.fOutxCtsFlow,
|
|
pTG->Comm.dcbOrig.fDtrControl,
|
|
pTG->Comm.dcbOrig.fOutX));
|
|
|
|
|
|
pTG->CurrentSerialSpeed = (UWORD) pTG->Comm.dcbOrig.BaudRate;
|
|
|
|
if (pTG->Comm.dcbOrig.fDtrControl==DTR_CONTROL_ENABLE)
|
|
{
|
|
|
|
|
|
// Try to pre-sync modem at new speed before we hand
|
|
// it back to TAPI. Can't call iiSyncModemDialog here because
|
|
// it's defined at a higher level. We don't really care
|
|
// to determine if we get an OK response anyway...
|
|
|
|
#define AT "AT"
|
|
#define cr "\r"
|
|
|
|
#define iSyncModemDialog2(pTG, s, l, w1, w2) \
|
|
iiModemDialog(pTG, s, l, 990, TRUE, 2, TRUE, (CBPSTR)w1, (CBPSTR)w2, (CBPSTR)(NULL))
|
|
|
|
if (!iSyncModemDialog2(pTG, AT cr,sizeof(AT cr)-1,"OK", "0")) {
|
|
(MyDebugPrint(pTG, LOG_ERR, "ERROR: couldn't sync AT command at %ld\n"), GetTickCount() );
|
|
}
|
|
else {
|
|
(MyDebugPrint(pTG, LOG_ERR, "Sync AT command OK at %ld\n"), GetTickCount() );
|
|
|
|
// We flush our internal buffer here...
|
|
if (pTG->Comm.lpovrCur)
|
|
{
|
|
int nNumWrote; // Must be 32bits in WIN32
|
|
if (!ov_write(pTG, pTG->Comm.lpovrCur, &nNumWrote))
|
|
{
|
|
// error...
|
|
(MyDebugPrint(pTG, LOG_ERR, "FComClose: 2nd ov_write failed at %ld\n", GetTickCount() ));
|
|
}
|
|
BG_CHK (pTG->Comm.lpovrCur->eState==eFREE
|
|
|| pTG->Comm.lpovrCur->eState==eIO_PENDING);
|
|
pTG->Comm.lpovrCur=NULL;
|
|
(MyDebugPrint(pTG, LOG_ALL, "FComClose: done writing mybuf.\r\n"));
|
|
}
|
|
|
|
ov_drain(pTG, FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
pTG->Comm.fStateChanged=FALSE;
|
|
#ifdef ADAPTIVE_ANSWER
|
|
pTG->Comm.fDataCall=FALSE;
|
|
#endif // ADAPTIVE_ANSWER
|
|
|
|
}
|
|
// RSL else
|
|
#endif // METAPORT
|
|
{
|
|
|
|
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "Closing Comm pTG->Comm.nCid=%d. \n", pTG->Comm.nCid));
|
|
// FComDTR(pTG, FALSE); // drop DTR before closing port
|
|
|
|
My2ndCloseComm(pTG->Comm.nCid, &nRet);
|
|
if(nRet)
|
|
{
|
|
DEBUGSTMT(D_PrintIE(nRet));
|
|
FComGetError(pTG);
|
|
fRet=FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef MON3 //!MON3
|
|
#ifdef MON
|
|
PutMonBufs(pTG);
|
|
#endif
|
|
#endif //!MON3
|
|
|
|
|
|
lEnd:
|
|
|
|
#ifdef WIN32
|
|
if (pTG->Comm.ovAux.hEvent) CloseHandle(pTG->Comm.ovAux.hEvent);
|
|
_fmemset(&pTG->Comm.ovAux, 0, sizeof(pTG->Comm.ovAux));
|
|
ov_deinit(pTG);
|
|
#endif
|
|
|
|
pTG->Comm.nCid = (-1);
|
|
pTG->Comm.fCommOpen = FALSE;
|
|
|
|
#ifdef METAPORT
|
|
pTG->Comm.fExternalHandle=FALSE;
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
pTG->Comm.fDoOverlapped=FALSE;
|
|
#endif
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
T30ComInit (
|
|
PThrdGlbl pTG,
|
|
HANDLE hComm
|
|
)
|
|
{
|
|
|
|
if (pTG->fCommInitialized) {
|
|
goto lSecondInit;
|
|
}
|
|
|
|
|
|
pTG->Comm.fDataCall=FALSE;
|
|
|
|
#ifdef METAPORT
|
|
BG_CHK(!pTG->Comm.fCommOpen && !pTG->Comm.fExternalHandle && !pTG->Comm.fStateChanged);
|
|
#endif
|
|
BG_CHK(!pTG->Comm.ovAux.hEvent);
|
|
|
|
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "Opening Comm Port=%x\r\n", hComm));
|
|
|
|
pTG->CommCache.dwMaxSize = 4096;
|
|
|
|
ClearCommCache(pTG);
|
|
pTG->CommCache.fReuse = 0;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "OPENCOMM:: bufs in=%d out=%d\r\n", COM_INBUFSIZE, COM_OUTBUFSIZE));
|
|
|
|
|
|
pTG->Comm.nCid = (LONG_PTR) hComm;
|
|
|
|
|
|
if(pTG->Comm.nCid < 0)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "OPENCOMM failed. nRet=%d\r\n", pTG->Comm.nCid));
|
|
//DEBUGSTMT(D_PrintIE(pTG->Comm.nCid));
|
|
goto error;
|
|
}
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "OPENCOMM succeeded nCid=%d\r\n", pTG->Comm.nCid));
|
|
pTG->Comm.fCommOpen = TRUE;
|
|
|
|
pTG->Comm.cbInSize = COM_INBUFSIZE;
|
|
pTG->Comm.cbOutSize = COM_OUTBUFSIZE;
|
|
|
|
// Reset Comm timeouts...
|
|
{
|
|
COMMTIMEOUTS cto;
|
|
_fmemset(&cto, 0, sizeof(cto));
|
|
|
|
|
|
// Out of curiosity, see what they are set at currently...
|
|
if (!GetCommTimeouts((HANDLE) pTG->Comm.nCid, &cto))
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<WARNING>> GetCommTimeouts fails for handle=0x%lx\r\n",
|
|
(unsigned long) pTG->Comm.nCid));
|
|
}
|
|
else
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ALL, "GetCommTimeouts: cto={%lu, %lu, %lu, %lu, %lu}\r\n",
|
|
(unsigned long) cto.ReadIntervalTimeout,
|
|
(unsigned long) cto.ReadTotalTimeoutMultiplier,
|
|
(unsigned long) cto.ReadTotalTimeoutConstant,
|
|
(unsigned long) cto.WriteTotalTimeoutMultiplier,
|
|
(unsigned long) cto.WriteTotalTimeoutConstant));
|
|
}
|
|
|
|
|
|
cto.ReadIntervalTimeout = READ_INTERVAL_TIMEOUT;
|
|
cto.ReadTotalTimeoutMultiplier = READ_TOTAL_TIMEOUT_MULTIPLIER;
|
|
cto.ReadTotalTimeoutConstant = READ_TOTAL_TIMEOUT_CONSTANT;
|
|
cto.WriteTotalTimeoutMultiplier = WRITE_TOTAL_TIMEOUT_MULTIPLIER;
|
|
cto.WriteTotalTimeoutConstant = WRITE_TOTAL_TIMEOUT_CONSTANT;
|
|
if (!SetCommTimeouts((HANDLE) pTG->Comm.nCid, &cto))
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<WARNING>> SetCommTimeouts fails for handle=0x%lx\r\n",
|
|
(unsigned long) pTG->Comm.nCid));
|
|
}
|
|
}
|
|
|
|
pTG->Comm.fCommOpen = TRUE;
|
|
|
|
pTG->Comm.cbInSize = COM_INBUFSIZE;
|
|
pTG->Comm.cbOutSize = COM_OUTBUFSIZE;
|
|
|
|
_fmemset(&(pTG->Comm.comstat), 0, sizeof(COMSTAT));
|
|
|
|
if(MyGetCommState(pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error2;
|
|
|
|
#ifdef METAPORT
|
|
pTG->Comm.dcbOrig = pTG->Comm.dcb; // structure copy.
|
|
pTG->Comm.fStateChanged=TRUE;
|
|
#endif
|
|
|
|
|
|
|
|
lSecondInit:
|
|
|
|
|
|
// Use of 2400/ 8N1 and 19200 8N1 is not actually specified
|
|
// in Class1, but seems to be adhered to be universal convention
|
|
// watch out for modems that break this!
|
|
|
|
if (pTG->SerialSpeedInit) {
|
|
pTG->Comm.dcb.BaudRate = pTG->SerialSpeedInit;
|
|
}
|
|
else {
|
|
pTG->Comm.dcb.BaudRate = 19200; // default
|
|
}
|
|
|
|
pTG->CurrentSerialSpeed = (UWORD) pTG->Comm.dcb.BaudRate;
|
|
|
|
pTG->Comm.dcb.ByteSize = 8;
|
|
pTG->Comm.dcb.Parity = NOPARITY;
|
|
pTG->Comm.dcb.StopBits = ONESTOPBIT;
|
|
|
|
pTG->Comm.dcb.fBinary = 1;
|
|
pTG->Comm.dcb.fParity = 0;
|
|
|
|
/************************************
|
|
Pins assignments, & Usage
|
|
|
|
Protective Gnd -- 1
|
|
Transmit TxD (DTE to DCE) 3 2
|
|
Recv RxD (DCE to DTE) 2 3
|
|
RTS (Recv Ready--DTE to DCE) 7 4
|
|
CTS (TransReady--DCE to DTE) 8 5
|
|
DSR (DCE to DTE) 6 6
|
|
signal ground 5 7
|
|
CD (DCE to DTR) 1 8
|
|
DTR (DTE to DCE) 4 20
|
|
RI (DCE to DTE) 9 22
|
|
|
|
Many 9-pin adaptors & cables use only 6 pins, 2,3,4,5, and 7.
|
|
We need to worry about this because some modems actively
|
|
use CTS, ie. pin 8.
|
|
We don't care about RI and CD (Unless a really weird modem
|
|
uses CD for flow control). We ignore DSR, but some (not so
|
|
weird, but not so common either) modems use DSR for flow
|
|
control.
|
|
|
|
Thought :: Doesn't generate DSR. Seems to tie CD and CTS together
|
|
DOVE :: Generates only CTS. But the Appletalk-9pin cable
|
|
only passes 1-5 and pin 8.
|
|
GVC :: CTS, DSR and CD
|
|
************************************/
|
|
|
|
// CTS -- dunno. There is some evidence that the
|
|
// modem actually uses it for flow control
|
|
|
|
|
|
if (pTG->fEnableHardwareFlowControl) {
|
|
pTG->Comm.dcb.fOutxCtsFlow = 1; // Using it hangs the output sometimes...
|
|
}
|
|
else {
|
|
pTG->Comm.dcb.fOutxCtsFlow = 0;
|
|
}
|
|
// Try ignoring it and see if it works?
|
|
pTG->Comm.dcb.fOutxDsrFlow = 0; // Never use this??
|
|
|
|
pTG->Comm.dcb.fRtsControl = RTS_CONTROL_ENABLE; // Current code seems to leave this ON
|
|
pTG->Comm.dcb.fDtrControl = (pTG->Comm.fExternalHandle)
|
|
? pTG->Comm.dcbOrig.fDtrControl
|
|
: DTR_CONTROL_DISABLE;
|
|
// If external handle, we preserve the
|
|
// previous state, else we
|
|
// keep it off until we need it.
|
|
|
|
pTG->Comm.dcb.fErrorChar = 0;
|
|
pTG->Comm.dcb.ErrorChar = 0;
|
|
// Can't change this cause SetCommState() resets hardware.
|
|
pTG->Comm.dcb.EvtChar = ETX; // set this when we set an EventWait
|
|
|
|
pTG->Comm.dcb.fOutX = 0; // Has to be OFF during HDLC recv phase
|
|
pTG->Comm.dcb.fInX = 0; // Will this do any good??
|
|
// Using flow-control on input is only a good
|
|
// idea if the modem has a largish buffer
|
|
pTG->Comm.dcb.fNull = 0;
|
|
|
|
|
|
pTG->Comm.dcb.XonChar = CTRL_Q;
|
|
pTG->Comm.dcb.XoffChar = CTRL_S;
|
|
pTG->Comm.dcb.XonLim = 100; // Need to set this when BufSize is set
|
|
pTG->Comm.dcb.XoffLim = 50; // Set this when BufSize is set
|
|
// actually we *never* use XON/XOFF in recv, so don't worry about this
|
|
// right now. (Later, when we have smart modems with large buffers, &
|
|
// we are worried about our ISR buffer filling up before our windows
|
|
// process gets run, we can use this). Some tuning will be reqd.
|
|
pTG->Comm.dcb.EofChar = 0;
|
|
|
|
pTG->Comm.dcb.fAbortOnError = 0; // RSL don't fail if minor problems
|
|
|
|
if(MySetCommState(pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error2;
|
|
|
|
|
|
|
|
if (pTG->fCommInitialized) {
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef METAPORT
|
|
pTG->Comm.fStateChanged=TRUE;
|
|
#endif
|
|
|
|
MySetCommMask(pTG->Comm.nCid, 0); // all events off
|
|
|
|
|
|
BG_CHK(!pTG->Comm.lpovrCur);
|
|
pTG->Comm.lpovrCur=NULL;
|
|
|
|
_fmemset(&pTG->Comm.ovAux,0, sizeof(pTG->Comm.ovAux));
|
|
pTG->Comm.ovAux.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (pTG->Comm.ovAux.hEvent==NULL)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> FComOpen: couldn't create event\r\n"));
|
|
goto error2;
|
|
}
|
|
if (!ov_init(pTG))
|
|
{
|
|
CloseHandle(pTG->Comm.ovAux.hEvent);
|
|
pTG->Comm.ovAux.hEvent=0;
|
|
goto error2;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
//DEBUGSTMT(D_PrintIE(pTG->Comm.nCid));
|
|
error2:
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> FComOpen failed\r\n"));
|
|
FComGetError(pTG);
|
|
if (pTG->Comm.fCommOpen)
|
|
{
|
|
FComClose(pTG);
|
|
BG_CHK(!pTG->Comm.fCommOpen);
|
|
#ifdef METAPORT
|
|
BG_CHK(!pTG->Comm.fExternalHandle && !pTG->Comm.fStateChanged);
|
|
#endif
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL FComSetBaudRate(PThrdGlbl pTG, UWORD uwBaudRate)
|
|
{
|
|
TRACE(("Setting BAUDRATE=%d\r\n", uwBaudRate));
|
|
|
|
if(MyGetCommState( pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error;
|
|
|
|
pTG->Comm.dcb.BaudRate = uwBaudRate;
|
|
pTG->CurrentSerialSpeed = uwBaudRate;
|
|
|
|
if(MySetCommState( pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error;
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> Set Baud Rate --- Can't Get/Set DCB\r\n"));
|
|
FComGetError(pTG);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL FComInXOFFHold(PThrdGlbl pTG)
|
|
{
|
|
int err; // _must_ be 32bits in Win32
|
|
|
|
GetCommErrorNT( pTG, (HANDLE) pTG->Comm.nCid, &err, &(pTG->Comm.comstat));
|
|
DEBUGSTMT(if(err) D_GotError(pTG, pTG->Comm.nCid, err, &(pTG->Comm.comstat)););
|
|
|
|
|
|
#ifndef WIN32
|
|
BG_CHK(!(pTG->Comm.comstat.status &
|
|
(CSTF_CTSHOLD|CSTF_DSRHOLD|CSTF_RLSDHOLD)));
|
|
if((pTG->Comm.comstat.status & CSTF_XOFFHOLD) != 0)
|
|
#else //!WIN32
|
|
BG_CHK(!(pTG->Comm.comstat.fCtsHold || pTG->Comm.comstat.fDsrHold || pTG->Comm.comstat.fRlsdHold));
|
|
if(pTG->Comm.comstat.fXoffHold)
|
|
#endif //!WIN32
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ALL, "In XOFF hold\r\n"));
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL FComXon(PThrdGlbl pTG, BOOL fEnable)
|
|
{
|
|
|
|
if (pTG->fEnableHardwareFlowControl) {
|
|
(MyDebugPrint(pTG, LOG_ALL, "FComXon = %d IGNORED : h/w flow control \r\n", fEnable));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "FComXon = %d\r\n", fEnable));
|
|
|
|
// enables/disables flow control
|
|
// returns TRUE on success, false on failure
|
|
|
|
|
|
if(MyGetCommState( pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "FaxXon Before: %02x\r\n", pTG->Comm.dcb.fOutX));
|
|
|
|
pTG->Comm.dcb.fOutX = fEnable;
|
|
|
|
if(MySetCommState(pTG->Comm.nCid, &(pTG->Comm.dcb)))
|
|
goto error;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "After: %02x\r\n", pTG->Comm.dcb.fOutX));
|
|
return TRUE;
|
|
|
|
error:
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> FaxXon --- Can't Set/Get DCB\r\n"));
|
|
FComGetError(pTG);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FComFlushQueue(PThrdGlbl pTG, int queue)
|
|
{
|
|
int nRet;
|
|
DWORD lRet;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "FlushQue = %d\r\n", queue));
|
|
ST_FC(D_FComCheck(pTG, pTG->Comm.nCid));
|
|
//RSL ST_FC(D_FComDumpFlush(pTG, pTG->Comm.nCid, queue));
|
|
BG_CHK(queue == 0 || queue == 1);
|
|
|
|
if (queue == 1) {
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "ClearCommCache in FComFlushQueue\n");
|
|
ClearCommCache(pTG);
|
|
|
|
}
|
|
|
|
if(nRet = MyFlushComm(pTG->Comm.nCid, queue))
|
|
{
|
|
DEBUGSTMT(D_PrintIE(nRet));
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> FlushComm failed nRet=%d\r\n", nRet));
|
|
FComGetError(pTG);
|
|
// Throwing away errors that happen here.
|
|
// No good reason for it!
|
|
}
|
|
if(queue == 1)
|
|
{
|
|
FComInFilterInit(pTG);
|
|
}
|
|
else // (queue == 0)
|
|
{
|
|
|
|
// Let's dump any stuff we may have in *our* buffer.
|
|
if (pTG->Comm.lpovrCur && pTG->Comm.lpovrCur->dwcb)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<WARNING>> FComFlushQueue:"
|
|
"Clearing NonNULL pTG->Comm.lpovrCur->dwcb=%lx\r\n",
|
|
(unsigned long) pTG->Comm.lpovrCur->dwcb));
|
|
pTG->Comm.lpovrCur->dwcb=0;
|
|
ov_unget(pTG, pTG->Comm.lpovrCur);
|
|
pTG->Comm.lpovrCur=NULL;
|
|
}
|
|
|
|
// Lets "drain" -- should always return immediately, because
|
|
// we have just purged the output comm buffers.
|
|
if (pTG->Comm.fovInited) {
|
|
BEFORECALL("FLUSH:ov_drain");
|
|
ov_drain(pTG, FALSE);
|
|
AFTERCALL("FLUSH:ov_drain",0);
|
|
}
|
|
|
|
// just incase it got stuck due to a mistaken XOFF
|
|
if(lRet = MySetXON(pTG->Comm.nCid))
|
|
{
|
|
// Returns the comm error value CE!!
|
|
// DEBUGSTMT(D_PrintIE(nRet));
|
|
TRACE(("EscapeCommFunc(SETXON) returned %d\r\n", lRet));
|
|
FComGetError(pTG);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef NTF
|
|
|
|
#define EV_ALL (EV_BREAK|EV_CTS|EV_CTSS|EV_DSR|EV_ERR|EV_PERR|EV_RING|EV_RLSD \
|
|
|EV_DSRS|EV_RLSDS|EV_RXCHAR|EV_RXFLAG|EV_TXEMPTY|EV_RINGTE)
|
|
|
|
|
|
#define ALWAYSEVENTS (EV_BREAK | EV_ERR)
|
|
|
|
// errors + TXEMPTY. Ignore incoming chars
|
|
#define DRAINEVENTS (ALWAYSEVENTS | EV_TXEMPTY)
|
|
// errors and TXEMPTY (also an error!)
|
|
#define WRITEEVENTS (ALWAYSEVENTS | EV_TXEMPTY)
|
|
// errors and RXCHAR
|
|
#define READLINEEVENTS (ALWAYSEVENTS | EV_RXCHAR)
|
|
// errors and RXFLAG (EvtChar already set to ETX)
|
|
#define READBUFEVENTS (ALWAYSEVENTS | EV_RXFLAG)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL FComEnableNotify(PThrdGlbl pTG, UWORD uwInTrig, UWORD uwOutTrig, UWORD events)
|
|
{
|
|
HANDLE h;
|
|
|
|
// Used incrementally, so don't clear events!
|
|
faxT4log(("EnableCommNotif(hwnd=%d uwIn=%d uwOUt=%d events=%d)\r\n", pTG->FComModem.hwndNotify, uwInTrig, uwOutTrig, events));
|
|
h = pTG->FComModem.hwndNotify;
|
|
|
|
if(!h)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> Can't set Notif -- hwnd=%d\r\n", h));
|
|
return TRUE; // Continue anyway
|
|
}
|
|
|
|
|
|
if((pTG->Comm.lpEventWord = SetCommEventMask(pTG, pTG->Comm.nCid, events)) &&
|
|
EnableCommNotification(pTG, pTG->Comm.nCid, h, uwInTrig, uwOutTrig))
|
|
{
|
|
// Both return non-zero on success
|
|
faxT4log(("ECN succ: %d %d %d %d\r\n", pTG->Comm.nCid, h, uwInTrig, uwOutTrig));
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> ECN fail: %d %d %d %d\r\n", pTG->Comm.nCid, h, uwInTrig, uwOutTrig));
|
|
FComGetError(pTG);
|
|
return FALSE;
|
|
}
|
|
}
|
|
#endif // NTF
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
Name : FComDrain(BOOL fLongTO, BOOL fDrainComm)
|
|
Purpose : Drain internal buffers. If fDrainComm, wait for Comm
|
|
ISR Output buffer to drain.
|
|
Returns when buffer is drained or if no progress is made
|
|
for DRAINTIMEOUT millisecs. (What about XOFFed sections?
|
|
Need to set Drain timeout high enough)
|
|
Parameters:
|
|
Returns : TRUE on success (buffer drained)
|
|
FALSE on failure (error or timeout)
|
|
|
|
Revision Log
|
|
Num Date Name Description
|
|
--- -------- ---------- -----------------------------------------------
|
|
101 06/03/92 arulm Created it in a new incarnation
|
|
***************************************************************************/
|
|
|
|
// This timeout has to be low at time and high at others. We want it low
|
|
// so we don't spend too much time trying to talk to a non-existent modem
|
|
// during Init/Setup. However in PhaseC, when the timer expires all we
|
|
// do is abort and kill everything. So it serves no purpose to make it
|
|
// too low. With the Hayes ESP FIFO card long stretches can elapse without
|
|
// any visible "progress", so we fail with that card because we think
|
|
// "no progress" is being made
|
|
|
|
|
|
// So....make it short for init/install
|
|
// but not too short. Some cmds (e.g. AT&F take a long time)
|
|
// Used to be 800ms & seemed to work then, so leave it at that
|
|
#define SHORT_DRAINTIMEOUT 800
|
|
|
|
// So....make it long for PhaseC
|
|
// 4secs should be about long enough
|
|
#define LONG_DRAINTIMEOUT 4000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL FComDrain(PThrdGlbl pTG, BOOL fLongTO, BOOL fDrainComm)
|
|
{
|
|
WORD wTimer = 0;
|
|
UWORD cbPrevOut = 0xFFFF;
|
|
BOOL fStuckOnce=FALSE;
|
|
BOOL fRet=FALSE;
|
|
|
|
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "Entering Drain\r\n"));
|
|
|
|
|
|
ST_FC(D_FComPrint(pTG, pTG->Comm.nCid));
|
|
/** BG_CHK(uwCurrMsg == 0); **/
|
|
|
|
|
|
// We flush our internal buffer here...
|
|
if (pTG->Comm.lpovrCur)
|
|
{
|
|
int nNumWrote; // Must be 32bits in WIN32
|
|
if (!ov_write(pTG, pTG->Comm.lpovrCur, &nNumWrote))
|
|
goto done;
|
|
BG_CHK (pTG->Comm.lpovrCur->eState==eFREE
|
|
|| pTG->Comm.lpovrCur->eState==eIO_PENDING);
|
|
pTG->Comm.lpovrCur=NULL;
|
|
(MyDebugPrint(pTG, LOG_ALL, "FComDrain: done writing mybuf.\r\n"));
|
|
}
|
|
|
|
if (!fDrainComm) {fRet=TRUE; goto done;}
|
|
|
|
// +++ Here we drain all our overlapped events..
|
|
// If we setup the system comm timeouts properly, we
|
|
// don't need to do anything else, except for the XOFF/XON
|
|
// stuff...
|
|
fRet = ov_drain(pTG, fLongTO);
|
|
goto done;
|
|
|
|
|
|
done:
|
|
|
|
return fRet; //+++ was (cbOut == 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
Name : FComDirectWrite(, lpb, cb)
|
|
Purpose : Write cb bytes starting from lpb to pTG->Comm. If Comm buffer
|
|
is full, set up notifications and timers and wait until space
|
|
is available. Returns when all bytes have been written to
|
|
the Comm buffer or if no progress is made
|
|
for WRITETIMEOUT millisecs. (What about XOFFed sections?
|
|
Need to set timeout high enough)
|
|
Parameters: , lpb, cb
|
|
Returns : Number of bytes written, i.e. cb on success and <cb on timeout,
|
|
or error.
|
|
|
|
Revision Log
|
|
Num Date Name Description
|
|
--- -------- ---------- -----------------------------------------------
|
|
101 06/03/92 arulm Created it
|
|
***************************************************************************/
|
|
|
|
// This is WRONG -- see below!!
|
|
// totally arbitrary should be no more than the time as it would
|
|
// take to write WRITEQUANTUM out at the fastest speed
|
|
// (say 14400 approx 2 bytes/ms)
|
|
// #define WRITETIMEOUT min((WRITEQUANTUM / 2), 200)
|
|
|
|
// This timeout was too low. We wanted it low so we don't spend too much
|
|
// time trying to talk to a non-existent modem during Init/Setup. But in
|
|
// those cases we _never_ reach full buffer, so we don't wait here
|
|
// we wait in FComDrain(). Here we wait _only_ in PhaseC, so when the
|
|
// timer expires all we do is abort and kill everything. So it serves
|
|
// no purpose to make it too low. With the Hayes ESP FIFO card long
|
|
// stretches can elapse without any visible "progress", so we fail with
|
|
// that card because we think "no progress" is being made
|
|
// So....make it long
|
|
// 2secs should be about long enough
|
|
#define WRITETIMEOUT 2000
|
|
|
|
|
|
UWORD FComDirectWrite(PThrdGlbl pTG, LPB lpb, UWORD cb)
|
|
{
|
|
DWORD cbLeft = cb;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "Entering (WIN32) DirectWrite(lpb=0x%08lx cb=%d)\r\n", lpb, cb));
|
|
D_SafePrint(pTG, lpb, cb);
|
|
ST_FC(D_FComPrint(pTG, pTG->Comm.nCid));
|
|
|
|
|
|
while(cbLeft)
|
|
{
|
|
DWORD dwcbCopy;
|
|
DWORD dwcbWrote;
|
|
int err;
|
|
|
|
if (!pTG->Comm.lpovrCur)
|
|
{
|
|
pTG->Comm.lpovrCur = ov_get(pTG);
|
|
if (!pTG->Comm.lpovrCur) goto error;
|
|
BG_CHK(!pTG->Comm.lpovrCur->dwcb);
|
|
}
|
|
BG_CHK(pTG->Comm.lpovrCur->eState==eALLOC);
|
|
BG_CHK(OVBUFSIZE>=pTG->Comm.lpovrCur->dwcb);
|
|
|
|
dwcbCopy = OVBUFSIZE-pTG->Comm.lpovrCur->dwcb;
|
|
|
|
if (dwcbCopy>cbLeft) dwcbCopy = cbLeft;
|
|
|
|
// Copy as much as we can to the overlapped buffer...
|
|
_fmemcpy(pTG->Comm.lpovrCur->rgby+pTG->Comm.lpovrCur->dwcb, lpb, dwcbCopy);
|
|
cbLeft-=dwcbCopy; pTG->Comm.lpovrCur->dwcb+=dwcbCopy; lpb+=dwcbCopy;
|
|
|
|
// Let's always update comstat here...
|
|
GetCommErrorNT( pTG, (HANDLE) pTG->Comm.nCid, &err, &(pTG->Comm.comstat));
|
|
DEBUGSTMT(if(err) D_GotError(pTG, pTG->Comm.nCid, err, &(pTG->Comm.comstat)););
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "DirectWrite:: OutQ has %d fDoOverlapped=%d at %ld \n",
|
|
pTG->Comm.comstat.cbOutQue, pTG->Comm.fDoOverlapped, GetTickCount()));
|
|
|
|
// We write to comm if our buffer is full or the comm buffer is
|
|
// empty or if we're not in overlapped mode...
|
|
if (!pTG->Comm.fDoOverlapped ||
|
|
pTG->Comm.lpovrCur->dwcb>=OVBUFSIZE || !pTG->Comm.comstat.cbOutQue)
|
|
{
|
|
BOOL fRet = ov_write(pTG, pTG->Comm.lpovrCur, &dwcbWrote);
|
|
BG_CHK( pTG->Comm.lpovrCur->eState==eIO_PENDING
|
|
|| pTG->Comm.lpovrCur->eState==eFREE);
|
|
pTG->Comm.lpovrCur=NULL;
|
|
if (!fRet) goto error;
|
|
}
|
|
|
|
} // while (cbLeft)
|
|
|
|
return cb;
|
|
|
|
error:
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
Name : FComFilterReadLine(, lpb, cbSize, pto)
|
|
Purpose : Reads upto cbSize bytes from Comm into memory starting from
|
|
lpb. If Comm buffer is empty, set up notifications and timers
|
|
and wait until characters are available.
|
|
|
|
Filters out DLE characters. i.e DLE-DLE is reduced to
|
|
a single DLE, DLE ETX is left intact and DLE-X is deleted.
|
|
|
|
Returns success (+ve bytes count) when CR-LF has been
|
|
encountered, and returns failure (-ve bytes count).
|
|
when either (a) cbSize bytes have been read (i.e. buffer is
|
|
full) or (b) PTO times out or an error is encountered.
|
|
|
|
It is critical that this function never returns a
|
|
timeout, as long as data
|
|
is still pouring/trickling in. This implies two things
|
|
(a) FIRST get all that is in the InQue (not more than a
|
|
line, though), THEN check the timeout.
|
|
(b) Ensure that at least 1 char-arrival-time at the
|
|
slowest Comm speed passes between the function entry
|
|
point and the last time we check for a byte, or between
|
|
two consecutive checks for a byte, before we return a timeout.
|
|
|
|
Therefor conditions to return a timeout are Macro timeout
|
|
over and inter-char timeout over.
|
|
|
|
In theory the slowest speed we need to worry about is 2400,
|
|
because that's the slowest we run the Comm at, but be paranoid
|
|
and assume the modem sends the chars at the same speed that
|
|
they come in the wire, so slowest is now 300. 1 char-arrival-time
|
|
is now 1000 / (300/8) == 26.67ms.
|
|
|
|
If pto expires, returns error, i.e. -ve of the number of
|
|
bytes read.
|
|
|
|
Returns : Number of bytes read, i.e. cb on success and -ve of number
|
|
of bytes read on timeout. 0 is a timeout error with no bytes
|
|
read.
|
|
|
|
Revision Log
|
|
Num Date Name Description
|
|
--- -------- ---------- -----------------------------------------------
|
|
101 06/03/92 arulm Created it
|
|
***************************************************************************/
|
|
|
|
// totally arbitrary
|
|
#define READLINETIMEOUT 50
|
|
|
|
#define ONECHARTIME (30 * 2) // see above *2 to be safe
|
|
|
|
// void WINAPI OutputDebugStr(LPSTR);
|
|
// char szJunk[200];
|
|
|
|
|
|
|
|
|
|
SWORD FComFilterReadLine(PThrdGlbl pTG, LPB lpb, UWORD cbSize, LPTO lptoRead)
|
|
{
|
|
WORD wTimer = 0;
|
|
UWORD cbIn = 0, cbGot = 0;
|
|
LPB lpbNext;
|
|
BOOL fPrevDLE = 0;
|
|
SWORD i, beg;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "in FilterReadLine(lpb=0x%08lx cb=%d timeout=%lu)\r\n", lpb, cbSize, lptoRead->ulTimeout));
|
|
BG_CHK(cbSize>2);
|
|
ST_FC(D_FComPrint(pTG, pTG->Comm.nCid));
|
|
|
|
cbSize--; // make room for terminal NULL
|
|
lpbNext = lpb; // we write the NULL to *lpbNext, so init this NOW!
|
|
cbGot = 0; // return value (even err return) is cbGot. Init NOW!!
|
|
fPrevDLE=0;
|
|
|
|
|
|
//
|
|
// check the cache first.
|
|
//
|
|
|
|
if ( ! pTG->CommCache.dwCurrentSize) {
|
|
MyDebugPrint(pTG, LOG_ALL, "Cache is empty. Resetting comm cache.\n");
|
|
|
|
ClearCommCache(pTG);
|
|
|
|
if ( ! FComFilterFillCache(pTG, cbSize, lptoRead) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FillCache failed \n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
while (1) {
|
|
if ( ! pTG->CommCache.dwCurrentSize) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: Cache is empty after FillCache\n");
|
|
goto error;
|
|
}
|
|
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "Cache: size=%d, offset=%d\n", pTG->CommCache.dwCurrentSize, pTG->CommCache.dwOffset);
|
|
|
|
lpbNext = pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset;
|
|
|
|
if (pTG->CommCache.dwCurrentSize >= 3) {
|
|
MyDebugPrint(pTG, LOG_ALL, "1=%x 2=%x 3=%x 4=%x 5=%x 6=%x 7=%x 8=%x 9=%x / %d=%x, %d=%x, %d=%x \n",
|
|
*lpbNext, *(lpbNext+1), *(lpbNext+2), *(lpbNext+3), *(lpbNext+4), *(lpbNext+5), *(lpbNext+6), *(lpbNext+7), *(lpbNext+8),
|
|
pTG->CommCache.dwCurrentSize-3, *(lpbNext+ pTG->CommCache.dwCurrentSize-3),
|
|
pTG->CommCache.dwCurrentSize-2, *(lpbNext+ pTG->CommCache.dwCurrentSize-2),
|
|
pTG->CommCache.dwCurrentSize-1, *(lpbNext+ pTG->CommCache.dwCurrentSize-1) );
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ALL, "1=%x 2=%x \n", *lpbNext, *(lpbNext+1) );
|
|
}
|
|
|
|
|
|
|
|
for (i=0, beg=0; i< (SWORD) pTG->CommCache.dwCurrentSize; i++) {
|
|
|
|
if (i > 0 ) {
|
|
if ( ( *(pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset + i - 1) == CR ) &&
|
|
( *(pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset + i) == LF ) ) {
|
|
|
|
if ( i - beg >= cbSize) {
|
|
|
|
// line too long. try next one.
|
|
|
|
MyDebugPrint(pTG, LOG_ERR, "Line len=%d is longer than bufsize=%d Found in cache pos=%d, CacheSize=%d, Offset=%d \n",
|
|
i-beg, cbSize, i+1, pTG->CommCache.dwCurrentSize, pTG->CommCache.dwOffset);
|
|
beg = i + 1;
|
|
continue;
|
|
}
|
|
|
|
// found the line.
|
|
|
|
CopyMemory (lpb, (pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset + beg), (i - beg + 1) );
|
|
|
|
pTG->CommCache.dwOffset += (i+1);
|
|
pTG->CommCache.dwCurrentSize -= (i+1);
|
|
*(lpb+i-beg+1) = 0;
|
|
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "Found in cache pos=%d, CacheSize=%d, Offset=%d \n",
|
|
i+1, pTG->CommCache.dwCurrentSize, pTG->CommCache.dwOffset);
|
|
|
|
|
|
return ( i-beg+1 );
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// we get here if we didn't find CrLf in Cache
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "Cache wasn't empty but we didn't find CrLf\n");
|
|
|
|
// if cache too big (and we have not found anything anyway) --> clean it
|
|
|
|
if (pTG->CommCache.dwCurrentSize >= cbSize) {
|
|
MyDebugPrint(pTG, LOG_ALL, "ClearCommCache\n");
|
|
ClearCommCache(pTG);
|
|
}
|
|
else if ( ! pTG->CommCache.dwCurrentSize) {
|
|
MyDebugPrint(pTG, LOG_ALL, "Cache is empty. Resetting comm cache.\n");
|
|
ClearCommCache(pTG);
|
|
}
|
|
|
|
|
|
if ( ! FComFilterFillCache(pTG, cbSize, lptoRead) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FillCache failed \n");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
|
|
error:
|
|
ClearCommCache(pTG);
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int FComFilterFillCache(PThrdGlbl pTG, UWORD cbSize, LPTO lptoRead)
|
|
{
|
|
|
|
|
|
WORD wTimer = 0;
|
|
UWORD cbGot = 0, cbAvail = 0;
|
|
DWORD cbRequested = 0;
|
|
char lpBuffer[4096];
|
|
LPB lpbNext;
|
|
int nNumRead; // _must_ be 32 bits in Win32!!
|
|
LPOVERLAPPED lpOverlapped;
|
|
COMMTIMEOUTS cto;
|
|
DWORD dwLastErr;
|
|
DWORD dwTimeoutRead;
|
|
char *pSrc;
|
|
char *pDest;
|
|
DWORD i, j;
|
|
DWORD dwErr;
|
|
COMSTAT ErrStat;
|
|
DWORD NumHandles=2;
|
|
HANDLE HandlesArray[2];
|
|
DWORD WaitResult;
|
|
|
|
HandlesArray[1] = pTG->AbortReqEvent;
|
|
|
|
|
|
dwTimeoutRead = (DWORD) (lptoRead->ulEnd - lptoRead->ulStart);
|
|
if (dwTimeoutRead < 0) {
|
|
dwTimeoutRead = 0;
|
|
}
|
|
|
|
|
|
|
|
lpbNext = lpBuffer;
|
|
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "in FilterReadCache: cb=%d to=%d\r\n",
|
|
cbSize, dwTimeoutRead));
|
|
|
|
// we want to request the read such that we will be back
|
|
// no much later than dwTimeOut either with the requested
|
|
// amount of data or without it.
|
|
|
|
cbRequested = cbSize;
|
|
|
|
|
|
// use COMMTIMEOUTS to detect there are no more data
|
|
|
|
cto.ReadIntervalTimeout = 50; // 30 ms is during negotiation frames; del(ff, 2ndchar> = 54 ms with USR 28.8
|
|
cto.ReadTotalTimeoutMultiplier = 0;
|
|
cto.ReadTotalTimeoutConstant = dwTimeoutRead; // RSL may want to set first time ONLY
|
|
cto.WriteTotalTimeoutMultiplier = WRITE_TOTAL_TIMEOUT_MULTIPLIER;
|
|
cto.WriteTotalTimeoutConstant = WRITE_TOTAL_TIMEOUT_CONSTANT;
|
|
if (!SetCommTimeouts((HANDLE) pTG->Comm.nCid, &cto)) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: SetCommTimeouts fails for handle %lx , le=%x\n",
|
|
(unsigned long) pTG->Comm.nCid, GetLastError());
|
|
}
|
|
|
|
lpOverlapped = &pTG->Comm.ovAux;
|
|
|
|
(lpOverlapped)->Internal = (lpOverlapped)->InternalHigh = (lpOverlapped)->Offset = \
|
|
(lpOverlapped)->OffsetHigh = 0;
|
|
|
|
if ((lpOverlapped)->hEvent)
|
|
ResetEvent((lpOverlapped)->hEvent);
|
|
|
|
nNumRead = 0;
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "Before ReadFile Req=%d time=%ld \n", cbRequested, GetTickCount() );
|
|
|
|
if (! ReadFile( (HANDLE) pTG->Comm.nCid, lpbNext, cbRequested, &nNumRead, &pTG->Comm.ovAux) ) {
|
|
if ( (dwLastErr = GetLastError() ) == ERROR_IO_PENDING) {
|
|
|
|
//
|
|
// We want to be able to un-block ONCE only from waiting on I/O when the AbortReqEvent is signaled.
|
|
//
|
|
|
|
if (pTG->fAbortRequested) {
|
|
|
|
if (pTG->fOkToResetAbortReqEvent && (!pTG->fAbortReqEventWasReset)) {
|
|
MyDebugPrint(pTG, LOG_ALL, "FComFilterFillCache RESETTING AbortReqEvent at %lx\n",GetTickCount() );
|
|
pTG->fAbortReqEventWasReset = 1;
|
|
ResetEvent(pTG->AbortReqEvent);
|
|
}
|
|
|
|
pTG->fUnblockIO = 1;
|
|
}
|
|
|
|
HandlesArray[0] = pTG->Comm.ovAux.hEvent;
|
|
|
|
if (pTG->fUnblockIO) {
|
|
NumHandles = 1;
|
|
}
|
|
else {
|
|
NumHandles = 2;
|
|
}
|
|
|
|
WaitResult = WaitForMultipleObjects(NumHandles, HandlesArray, FALSE, WAIT_FCOM_FILTER_FILLCACHE_TIMEOUT);
|
|
|
|
if (WaitResult == WAIT_TIMEOUT) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FComFilterFillCache: WaitForMultipleObjects TIMEOUT at %ld\n", GetTickCount() );
|
|
|
|
ClearCommCache(pTG);
|
|
goto error;
|
|
}
|
|
|
|
|
|
if (WaitResult == WAIT_FAILED) {
|
|
MyDebugPrint(pTG, LOG_ERR, "FComFilterFillCache: WaitForMultipleObjects FAILED le=%lx NumHandles=%d at %ld \n",
|
|
GetLastError(), NumHandles, GetTickCount() );
|
|
|
|
ClearCommCache(pTG);
|
|
goto error;
|
|
}
|
|
|
|
if ( (NumHandles == 2) && (WaitResult == WAIT_OBJECT_0 + 1) ) {
|
|
pTG->fUnblockIO = 1;
|
|
MyDebugPrint(pTG, LOG_ALL, "FComFilterFillCache ABORTed at %ld \n", GetTickCount() );
|
|
ClearCommCache(pTG);
|
|
goto error;
|
|
}
|
|
|
|
if ( ! GetOverlappedResult ( (HANDLE) pTG->Comm.nCid, &pTG->Comm.ovAux, &nNumRead, TRUE) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: GetOverlappedResult le=%x at %ld \n", GetLastError(), GetTickCount() );
|
|
if (! ClearCommError( (HANDLE) pTG->Comm.nCid, &dwErr, &ErrStat) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: ClearCommError le=%x at %ld \n", GetLastError(), GetTickCount() );
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ERR, "ClearCommError dwErr=%x ErrSTAT: Cts=%d Dsr=%d Rls=%d XoffHold=%d XoffSent=%d fEof=%d Txim=%d In=%d Out=%d \n",
|
|
dwErr, ErrStat.fCtsHold, ErrStat.fDsrHold, ErrStat.fRlsdHold, ErrStat.fXoffHold, ErrStat.fXoffSent, ErrStat.fEof,
|
|
ErrStat.fTxim, ErrStat.cbInQue, ErrStat.cbOutQue);
|
|
}
|
|
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: ReadFile le=%x \n", GetTickCount() );
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ALL, "WARNING: ReadFile returned w/o WAIT\n");
|
|
}
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "After ReadFile Req=%d Ret=%d time=%ld \n", cbRequested, nNumRead, GetTickCount() );
|
|
|
|
|
|
cbAvail = (UWORD)nNumRead;
|
|
|
|
if (!cbAvail) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: 0 read\n");
|
|
goto error;
|
|
}
|
|
|
|
|
|
// filter DLE stuff
|
|
|
|
pSrc = lpbNext;
|
|
pDest = pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset+ pTG->CommCache.dwCurrentSize;
|
|
|
|
for (i=0, j=0; i<cbAvail; ) {
|
|
|
|
if ( *(pSrc+i) == DLE) {
|
|
if ( *(pSrc+i+1) == DLE ) {
|
|
*(pDest+j) = DLE;
|
|
j += 1;
|
|
i += 2;
|
|
}
|
|
else if ( *(pSrc+i+1) == ETX ) {
|
|
*(pDest+j) = DLE;
|
|
*(pDest+j+1) = ETX;
|
|
j += 2;
|
|
i += 2;
|
|
}
|
|
else {
|
|
i += 2;
|
|
}
|
|
}
|
|
else {
|
|
*(pDest+j) = *(pSrc+i);
|
|
i++;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
|
|
pTG->CommCache.dwCurrentSize += j;
|
|
return TRUE;
|
|
|
|
|
|
error:
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
Name : FComDirectReadBuf(, lpb, cbSize, lpto, pfEOF)
|
|
Purpose : Reads upto cbSize bytes from Comm into memory starting from
|
|
lpb. If Comm buffer is empty, set up notifications and timers
|
|
and wait until characters are available.
|
|
|
|
Returns when success (+ve byte count) either (a) cbSize
|
|
bytes have been read or (b) DLE-ETX has been encountered
|
|
(in which case *pfEOF is set to TRUE).
|
|
|
|
Does no filtering. Reads the Comm buffer in large quanta.
|
|
|
|
If lpto expires, returns error, i.e. -ve of the number of
|
|
bytes read.
|
|
|
|
Returns : Number of bytes read, i.e. cb on success and -ve of number
|
|
of bytes read on timeout. 0 is a timeout error with no bytes
|
|
read.
|
|
|
|
Revision Log
|
|
Num Date Name Description
|
|
--- -------- ---------- -----------------------------------------------
|
|
101 06/03/92 arulm Created it
|
|
***************************************************************************/
|
|
|
|
// +++ #define READBUFQUANTUM (pTG->Comm.cbInSize / 8)
|
|
// totally arbitrary
|
|
// +++ #define READBUFTIMEOUT 200
|
|
|
|
// *lpswEOF is 1 on Class1 EOF, 0 on non-EOF, -1 on Class2 EOF, -2 on error -3 on timeout
|
|
|
|
UWORD FComFilterReadBuf(PThrdGlbl pTG, LPB lpb, UWORD cbSize, LPTO lptoRead, BOOL fClass2, LPSWORD lpswEOF)
|
|
{
|
|
WORD wTimer = 0;
|
|
UWORD cbGot = 0, cbAvail = 0;
|
|
DWORD cbRequested = 0;
|
|
LPB lpbNext;
|
|
int nNumRead; // _must_ be 32 bits in Win32!!
|
|
LPOVERLAPPED lpOverlapped;
|
|
COMMTIMEOUTS cto;
|
|
DWORD dwLastErr;
|
|
DWORD dwTimeoutRead;
|
|
DWORD cbFromCache = 0;
|
|
DWORD dwErr;
|
|
COMSTAT ErrStat;
|
|
DWORD NumHandles=2;
|
|
HANDLE HandlesArray[2];
|
|
DWORD WaitResult;
|
|
|
|
HandlesArray[1] = pTG->AbortReqEvent;
|
|
|
|
|
|
dwTimeoutRead = (DWORD) (lptoRead->ulEnd - lptoRead->ulStart);
|
|
if (dwTimeoutRead < 0) {
|
|
dwTimeoutRead = 0;
|
|
}
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "in FilterReadBuf: lpb=0x%08lx cb=%d to=%d\r\n",
|
|
lpb, cbSize, dwTimeoutRead));
|
|
|
|
BG_CHK((BOOL)pTG->Comm.dcb.fOutX == FALSE);
|
|
// Dont want to take ^Q/^S from modem to
|
|
// be XON/XOFF in the receive data phase!!
|
|
|
|
// BG_CHK(lpb && cbSize>2 && lptoRead && lpswEOF && *lpswEOF == 0);
|
|
BG_CHK(lpb && cbSize>3 && lptoRead && lpswEOF);
|
|
|
|
*lpswEOF=0;
|
|
|
|
ST_FC(D_FComPrint(pTG, pTG->Comm.nCid));
|
|
// BG_CHK(Filter.fStripDLE); // Always on
|
|
/** BG_CHK(uwCurrMsg == 0); **/
|
|
|
|
// Leave TWO spaces at start to make sure Out pointer will
|
|
// never get ahead of the In pointer in StripBuf, even
|
|
// if the last byte of prev block was DLE & first byte
|
|
// of this one is SUB (i.e need to insert two DLEs in
|
|
// output).
|
|
// Save a byte at end for the NULL terminator (Why? Dunno...)
|
|
|
|
lpb += 2;
|
|
cbSize -= 3;
|
|
|
|
cbRequested = cbSize;
|
|
|
|
|
|
for(lpbNext=lpb;;) {
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "in FilterReadBuf LOOP cbSize=%d cbGot=%d cbAvail=%d at %ld\r\n",
|
|
cbSize, cbGot, cbAvail, GetTickCount() );
|
|
|
|
#if 0
|
|
//
|
|
// just in case. RSL 970123
|
|
//
|
|
|
|
if (! ClearCommError( (HANDLE) pTG->Comm.nCid, &dwErr, &ErrStat) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: ClearCommError le=%x at %ld \n", GetLastError(), GetTickCount() );
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ERR, "ClearCommError dwErr=%x ErrSTAT: Cts=%d Dsr=%d Rls=%d XoffHold=%d XoffSent=%d fEof=%d Txim=%d In=%d Out=%d \n",
|
|
dwErr, ErrStat.fCtsHold, ErrStat.fDsrHold, ErrStat.fRlsdHold, ErrStat.fXoffHold, ErrStat.fXoffSent, ErrStat.fEof,
|
|
ErrStat.fTxim, ErrStat.cbInQue, ErrStat.cbOutQue);
|
|
}
|
|
#endif
|
|
|
|
if((cbSize - cbGot) < cbAvail) {
|
|
cbAvail = cbSize - cbGot;
|
|
}
|
|
|
|
|
|
if( (!cbGot) && !checkTimeOut(pTG, lptoRead) ) {
|
|
// No chars available *and* lptoRead expired
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: ReadLn:Timeout %ld-toRd=%ld start=%ld \n",
|
|
GetTickCount(), lptoRead->ulTimeout, lptoRead->ulStart);
|
|
goto failure;
|
|
}
|
|
|
|
|
|
// check Comm cache first (AT+FRH leftovers)
|
|
|
|
if ( pTG->CommCache.fReuse && pTG->CommCache.dwCurrentSize ) {
|
|
MyDebugPrint(pTG, LOG_ALL, "CommCache will REUSE %d offset=%d 0=%x 1=%x \n",
|
|
pTG->CommCache.dwCurrentSize, pTG->CommCache.dwOffset,
|
|
*(pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset),
|
|
*(pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset+1) );
|
|
|
|
if ( pTG->CommCache.dwCurrentSize >= cbRequested) {
|
|
CopyMemory (lpbNext, pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset, cbRequested);
|
|
|
|
pTG->CommCache.dwOffset += cbRequested;
|
|
pTG->CommCache.dwCurrentSize -= cbRequested;
|
|
|
|
cbAvail = (UWORD) cbRequested;
|
|
cbRequested = 0;
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "CommCache still left; no need to read\n");
|
|
|
|
goto l_merge;
|
|
}
|
|
else {
|
|
cbFromCache = pTG->CommCache.dwCurrentSize;
|
|
|
|
CopyMemory (lpbNext, pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset, cbFromCache);
|
|
|
|
ClearCommCache(pTG);
|
|
|
|
cbRequested -= cbFromCache;
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "CommCache used all %d \n", cbFromCache);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// use COMMTIMEOUTS to detect there are no more data
|
|
|
|
cto.ReadIntervalTimeout = 20; // 0 RSL make 15 later
|
|
cto.ReadTotalTimeoutMultiplier = 0;
|
|
cto.ReadTotalTimeoutConstant = dwTimeoutRead; // RSL may want to set first time ONLY
|
|
cto.WriteTotalTimeoutMultiplier = WRITE_TOTAL_TIMEOUT_MULTIPLIER;
|
|
cto.WriteTotalTimeoutConstant = WRITE_TOTAL_TIMEOUT_CONSTANT;
|
|
if (!SetCommTimeouts((HANDLE) pTG->Comm.nCid, &cto)) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: SetCommTimeouts fails for handle %lx , le=%x\n",
|
|
(unsigned long) pTG->Comm.nCid, GetLastError());
|
|
}
|
|
|
|
lpOverlapped = &pTG->Comm.ovAux;
|
|
|
|
(lpOverlapped)->Internal = (lpOverlapped)->InternalHigh = (lpOverlapped)->Offset = \
|
|
(lpOverlapped)->OffsetHigh = 0;
|
|
|
|
if ((lpOverlapped)->hEvent)
|
|
ResetEvent((lpOverlapped)->hEvent);
|
|
|
|
nNumRead = 0;
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "Before ReadFile Req=%d time=%ld \n", cbRequested, GetTickCount() );
|
|
|
|
if (! ReadFile( (HANDLE) pTG->Comm.nCid, lpbNext+cbFromCache, cbRequested, &nNumRead, &pTG->Comm.ovAux) ) {
|
|
if ( (dwLastErr = GetLastError() ) == ERROR_IO_PENDING) {
|
|
|
|
// We want to be able to un-block ONCE only from waiting on I/O when the AbortReqEvent is signaled.
|
|
//
|
|
|
|
if (pTG->fAbortRequested) {
|
|
|
|
if (pTG->fOkToResetAbortReqEvent && (!pTG->fAbortReqEventWasReset)) {
|
|
MyDebugPrint(pTG, LOG_ALL, "FComFilterReadBuffer RESETTING AbortReqEvent at %lx\n",GetTickCount() );
|
|
pTG->fAbortReqEventWasReset = 1;
|
|
ResetEvent(pTG->AbortReqEvent);
|
|
}
|
|
|
|
pTG->fUnblockIO = 1;
|
|
*lpswEOF = -2;
|
|
return cbGot;
|
|
|
|
}
|
|
|
|
HandlesArray[0] = pTG->Comm.ovAux.hEvent;
|
|
HandlesArray[1] = pTG->AbortReqEvent;
|
|
|
|
if (pTG->fUnblockIO) {
|
|
NumHandles = 1;
|
|
}
|
|
else {
|
|
NumHandles = 2;
|
|
}
|
|
|
|
WaitResult = WaitForMultipleObjects(NumHandles, HandlesArray, FALSE, WAIT_FCOM_FILTER_READBUF_TIMEOUT);
|
|
|
|
if (WaitResult == WAIT_TIMEOUT) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FComFilterReadBuf: WaitForMultipleObjects TIMEOUT at %ld\n", GetTickCount() );
|
|
ClearCommCache(pTG);
|
|
|
|
goto failure;
|
|
}
|
|
|
|
|
|
if (WaitResult == WAIT_FAILED) {
|
|
MyDebugPrint(pTG, LOG_ERR, "FComFilterReadBuf: WaitForMultipleObjects FAILED le=%lx at %ld \n",
|
|
GetLastError(), GetTickCount() );
|
|
|
|
ClearCommCache(pTG);
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
if ( (NumHandles == 2) && (WaitResult == WAIT_OBJECT_0 + 1) ) {
|
|
pTG->fUnblockIO = 1;
|
|
MyDebugPrint(pTG, LOG_ALL, "FComFilterReadBuf ABORTed at %ld \n", GetTickCount() );
|
|
ClearCommCache(pTG);
|
|
*lpswEOF = -2;
|
|
return cbGot;
|
|
}
|
|
|
|
if ( ! GetOverlappedResult ( (HANDLE) pTG->Comm.nCid, &pTG->Comm.ovAux, &nNumRead, TRUE) ) {
|
|
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: GetOverlappedResult le=%x at %ld \n", GetLastError(), GetTickCount() );
|
|
if (! ClearCommError( (HANDLE) pTG->Comm.nCid, &dwErr, &ErrStat) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: ClearCommError le=%x at %ld \n", GetLastError(), GetTickCount() );
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ERR, "ClearCommError dwErr=%x ErrSTAT: Cts=%d Dsr=%d Rls=%d XoffHold=%d XoffSent=%d fEof=%d Txim=%d In=%d Out=%d \n",
|
|
dwErr, ErrStat.fCtsHold, ErrStat.fDsrHold, ErrStat.fRlsdHold, ErrStat.fXoffHold, ErrStat.fXoffSent, ErrStat.fEof,
|
|
ErrStat.fTxim, ErrStat.cbInQue, ErrStat.cbOutQue);
|
|
}
|
|
|
|
goto failure;
|
|
}
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: ReadFile le=%x at %ld \n", dwLastErr, GetTickCount() );
|
|
goto failure;
|
|
}
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ALL, "WARNING: ReadFile returned w/o WAIT\n");
|
|
}
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "After ReadFile Req=%d Ret=%d time=%ld \n", cbRequested, nNumRead, GetTickCount() );
|
|
|
|
|
|
cbAvail = (UWORD) (nNumRead + cbFromCache);
|
|
|
|
|
|
|
|
l_merge:
|
|
// RSL PUTBACK INMON(pTG, lpbNext, cbAvail);
|
|
|
|
if(!cbAvail) {
|
|
MyDebugPrint(pTG, LOG_ALL, "cbAvail = %d --> continue \n", cbAvail);
|
|
continue;
|
|
}
|
|
// else we just drop through
|
|
|
|
|
|
// try to catch COMM read problems
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "DEBUG: Just read %d bytes, from cache =%d, log [%x .. %x], 1st=%x last=%x \n",
|
|
nNumRead, cbFromCache, pTG->CommLogOffset, (pTG->CommLogOffset+cbAvail),
|
|
*lpbNext, *(lpbNext+cbAvail-1) );
|
|
|
|
|
|
// RSL TEMP. Check T4 problems.
|
|
|
|
if (gT30.T4LogLevel) {
|
|
_lwrite(ghComLogFile, lpbNext, cbAvail);
|
|
}
|
|
|
|
pTG->CommLogOffset += cbAvail;
|
|
|
|
|
|
|
|
cbAvail = FComStripBuf(pTG, lpbNext-2, lpbNext, cbAvail, fClass2, lpswEOF);
|
|
BG_CHK(*lpswEOF == 0 || *lpswEOF == -1 || *lpswEOF == 1);
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "After FComStripBuf cbAvail=%ld \n", cbAvail );
|
|
|
|
|
|
cbGot += cbAvail;
|
|
lpbNext += cbAvail;
|
|
|
|
|
|
// RSL 970123. Dont wanna loop if got anything.
|
|
|
|
if ( (*lpswEOF != 0) || (cbGot > 0) ) { // some eof or full buf
|
|
goto done;
|
|
}
|
|
|
|
}
|
|
BG_CHK(FALSE);
|
|
|
|
|
|
*lpswEOF = -2;
|
|
goto done;
|
|
|
|
|
|
failure:
|
|
|
|
;
|
|
|
|
|
|
//timeout:
|
|
*lpswEOF = -3;
|
|
// fall through to done
|
|
|
|
|
|
done:
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "ex FilterReadBuf: cbGot=%d swEOF=%d\r\n", cbGot, *lpswEOF));
|
|
return cbGot;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FComCritical(PThrdGlbl pTG, BOOL x)
|
|
{
|
|
if (x) pTG->Comm.bDontYield++;
|
|
else if (pTG->Comm.bDontYield) pTG->Comm.bDontYield--;
|
|
else {BG_CHK(FALSE);}
|
|
|
|
#ifdef DEBUG
|
|
if (pTG->Comm.bDontYield) {(MyDebugPrint(pTG, LOG_ALL, "Exiting NESTED FComCritical\r\n"));}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if !defined(WFW) && !defined(WFWBG)
|
|
|
|
BOOL FComCheckRing(PThrdGlbl pTG)
|
|
{
|
|
int err; // must be 32 bits in WIN32
|
|
BOOL fRet=0;
|
|
COMSTAT comstatCheckActivity;
|
|
|
|
BG_CHK(pTG->Comm.nCid >= 0);
|
|
GetCommErrorNT( pTG, (HANDLE) pTG->Comm.nCid, &err, &comstatCheckActivity);
|
|
|
|
if(err)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> NCUCheckRing: Got Comm Error %04x\r\n", err));
|
|
D_GotError(pTG, pTG->Comm.nCid, err, &comstatCheckActivity);
|
|
}
|
|
|
|
|
|
fRet = (comstatCheckActivity.cbInQue > 0);
|
|
|
|
// get rid of RING sitting in buffer,
|
|
// or well wait until kingdom come with it
|
|
// in some situations, like someone refuses to
|
|
// answer or we have a full recv-filename-cache
|
|
|
|
MyFlushComm(pTG->Comm.nCid, 0);
|
|
MyFlushComm(pTG->Comm.nCid, 1);
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "CheckRing: fRet=%d Q=%d\r\n", fRet, comstatCheckActivity.cbInQue));
|
|
|
|
return fRet;
|
|
}
|
|
|
|
#endif //!WFWBG
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
FComGetOneChar(
|
|
PThrdGlbl pTG,
|
|
UWORD ch
|
|
)
|
|
|
|
{
|
|
BYTE rgbRead[10]; // must be 3 or more. 10 for safety
|
|
TO toCtrlQ;
|
|
int nNumRead; // _must_ be 32 bits in WIN32
|
|
LPOVERLAPPED lpOverlapped;
|
|
DWORD dwErr;
|
|
COMSTAT ErrStat;
|
|
DWORD NumHandles=2;
|
|
HANDLE HandlesArray[2];
|
|
DWORD WaitResult;
|
|
DWORD dwLastErr;
|
|
SWORD i;
|
|
|
|
HandlesArray[1] = pTG->AbortReqEvent;
|
|
|
|
//
|
|
// check the cache first.
|
|
//
|
|
|
|
if ( ! pTG->CommCache.dwCurrentSize) {
|
|
MyDebugPrint(pTG, LOG_ALL, "FComGetOneChar: Cache is empty. Resetting comm cache.\n");
|
|
|
|
ClearCommCache(pTG);
|
|
}
|
|
else {
|
|
for (i=0; i< (SWORD) pTG->CommCache.dwCurrentSize; i++) {
|
|
if ( *(pTG->CommCache.lpBuffer + pTG->CommCache.dwOffset + i) == ch) {
|
|
// found in cache
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "FComGetOneChar: Found XON in cache pos=%d total=%d\n", i, pTG->CommCache.dwCurrentSize);
|
|
|
|
pTG->CommCache.dwOffset += (i+1);
|
|
pTG->CommCache.dwCurrentSize -= (i+1);
|
|
|
|
goto GotCtrlQ;
|
|
|
|
}
|
|
}
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "FComGetOneChar: Cache wasn't empty. Didn't find XON. Resetting comm cache.\n");
|
|
ClearCommCache(pTG);
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send nothing - look for cntl-Q (XON) after connect
|
|
BG_CHK(ch == 0x11); // so far looking for just ctrlQ
|
|
startTimeOut(pTG, &toCtrlQ, 1000);
|
|
do
|
|
{
|
|
////MyReadComm(Comm.nCid, rgbRead, 1, &nNumRead);
|
|
|
|
lpOverlapped = &pTG->Comm.ovAux;
|
|
|
|
(lpOverlapped)->Internal = (lpOverlapped)->InternalHigh = (lpOverlapped)->Offset = \
|
|
(lpOverlapped)->OffsetHigh = 0;
|
|
|
|
if ((lpOverlapped)->hEvent)
|
|
ResetEvent((lpOverlapped)->hEvent);
|
|
|
|
nNumRead = 0;
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "Before ReadFile Req=%d time=%ld \n", 1, GetTickCount() );
|
|
|
|
if (! ReadFile( (HANDLE) pTG->Comm.nCid, rgbRead, 1, &nNumRead, &pTG->Comm.ovAux) ) {
|
|
if ( (dwLastErr = GetLastError() ) == ERROR_IO_PENDING) {
|
|
|
|
// We want to be able to un-block ONCE only from waiting on I/O when the AbortReqEvent is signaled.
|
|
//
|
|
|
|
if (pTG->fAbortRequested) {
|
|
|
|
if (pTG->fOkToResetAbortReqEvent && (!pTG->fAbortReqEventWasReset)) {
|
|
MyDebugPrint(pTG, LOG_ALL, "FComGetOneChar RESETTING AbortReqEvent at %lx\n",GetTickCount() );
|
|
pTG->fAbortReqEventWasReset = 1;
|
|
ResetEvent(pTG->AbortReqEvent);
|
|
}
|
|
|
|
pTG->fUnblockIO = 1;
|
|
goto error;
|
|
|
|
}
|
|
|
|
HandlesArray[0] = pTG->Comm.ovAux.hEvent;
|
|
HandlesArray[1] = pTG->AbortReqEvent;
|
|
|
|
if (pTG->fUnblockIO) {
|
|
NumHandles = 1;
|
|
}
|
|
else {
|
|
NumHandles = 2;
|
|
}
|
|
|
|
WaitResult = WaitForMultipleObjects(NumHandles, HandlesArray, FALSE, WAIT_FCOM_FILTER_READBUF_TIMEOUT);
|
|
|
|
if (WaitResult == WAIT_TIMEOUT) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FComGetOneChar: WaitForMultipleObjects TIMEOUT at %ld\n", GetTickCount() );
|
|
ClearCommCache(pTG);
|
|
|
|
goto error;
|
|
}
|
|
|
|
|
|
if (WaitResult == WAIT_FAILED) {
|
|
MyDebugPrint(pTG, LOG_ERR, "FComGetOneChar: WaitForMultipleObjects FAILED le=%lx at %ld \n",
|
|
GetLastError(), GetTickCount() );
|
|
|
|
ClearCommCache(pTG);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
if ( (NumHandles == 2) && (WaitResult == WAIT_OBJECT_0 + 1) ) {
|
|
pTG->fUnblockIO = 1;
|
|
MyDebugPrint(pTG, LOG_ALL, "FComGetOneChar ABORTed at %ld \n", GetTickCount() );
|
|
ClearCommCache(pTG);
|
|
goto error;
|
|
}
|
|
|
|
if ( ! GetOverlappedResult ( (HANDLE) pTG->Comm.nCid, &pTG->Comm.ovAux, &nNumRead, TRUE) ) {
|
|
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FComGetOneChar GetOverlappedResult le=%x at %ld \n", GetLastError(), GetTickCount() );
|
|
if (! ClearCommError( (HANDLE) pTG->Comm.nCid, &dwErr, &ErrStat) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FComGetOneChar ClearCommError le=%x at %ld \n", GetLastError(), GetTickCount() );
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ERR, "FComGetOneChar ClearCommError dwErr=%x ErrSTAT: Cts=%d Dsr=%d Rls=%d XoffHold=%d XoffSent=%d fEof=%d Txim=%d In=%d Out=%d \n",
|
|
dwErr, ErrStat.fCtsHold, ErrStat.fDsrHold, ErrStat.fRlsdHold, ErrStat.fXoffHold, ErrStat.fXoffSent, ErrStat.fEof,
|
|
ErrStat.fTxim, ErrStat.cbInQue, ErrStat.cbOutQue);
|
|
}
|
|
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR: FComGetOneChar ReadFile le=%x at %ld \n", dwLastErr, GetTickCount() );
|
|
goto error;
|
|
}
|
|
}
|
|
else {
|
|
MyDebugPrint(pTG, LOG_ALL, "WARNING: FComGetOneChar ReadFile returned w/o WAIT\n");
|
|
}
|
|
|
|
MyDebugPrint(pTG, LOG_ALL, "After FComGetOneChar ReadFile Req=%d Ret=%d time=%ld \n", 1, nNumRead, GetTickCount() );
|
|
|
|
|
|
switch(nNumRead)
|
|
{
|
|
case 0: break; // loop until we get something
|
|
case 1: // INMON(rgbRead, 1);
|
|
if(rgbRead[0] == ch)
|
|
goto GotCtrlQ;
|
|
else
|
|
{
|
|
ERRMSG(("<<ERROR>> GetCntlQ: Found non ^Q char\n\r"));
|
|
goto error;
|
|
}
|
|
default:
|
|
BG_CHK(FALSE);
|
|
goto error;
|
|
}
|
|
}
|
|
while(checkTimeOut(pTG, &toCtrlQ));
|
|
////ERRMSG(("<<ERROR>> GetCntlQ: Timed out\n\r"));
|
|
goto error;
|
|
|
|
GotCtrlQ:
|
|
////TRACE(("GetCntlQ: YES!!! Found cntl q\n\r"));
|
|
return TRUE;
|
|
|
|
error:
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****
|
|
#ifdef USE_HWND
|
|
# define MyGetMessage(x) \
|
|
( GetMessage(&x, NULL, 0, 0), \
|
|
(x.hwnd ? (DispatchMessage(&x), x.message=WM_NULL) : 0), \
|
|
(x.message != WM_QUIT) )
|
|
|
|
# define MyPeekMessage(x) \
|
|
( (x.message=WM_NULL), \
|
|
( PeekMessage(&x, NULL, 0, 0, PM_RNOY) ? \
|
|
(x.hwnd ? (DispatchMessage(&x), x.message=WM_NULL) : TRUE) \
|
|
: FALSE ) )
|
|
#else
|
|
|
|
# define MyGetMessage(x) ( GetMessage(&x, NULL, 0, 0), \
|
|
BG_CHK(x.hwnd==0), \
|
|
(x.message != IF_QUIT) )
|
|
|
|
# define MyPeekMessage(x) ( (x.message = WM_NULL), \
|
|
PeekMessage(&x, NULL, 0, 0, PM_RNOY), \
|
|
BG_CHK(x.hwnd==0), \
|
|
(x.message != WM_NULL) )
|
|
#endif
|
|
*****/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef MDRV
|
|
extern void iModemParamsReset(PThrdGlbl pTG);
|
|
extern void iModemInitGlobals(PThrdGlbl pTG);
|
|
#endif
|
|
|
|
|
|
|
|
#define szMODULENAME "awfxio32"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OVREC *ov_get(PThrdGlbl pTG)
|
|
{
|
|
OVREC *lpovr=NULL;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "In ov_get at %ld \n", GetTickCount() ) );
|
|
|
|
if (!pTG->Comm.covAlloced)
|
|
{
|
|
BG_CHK(!pTG->Comm.uovLast && !pTG->Comm.uovFirst);
|
|
lpovr = pTG->Comm.rgovr;
|
|
BG_CHK(lpovr->eState==eFREE);
|
|
BG_CHK(!(lpovr->dwcb));
|
|
}
|
|
else
|
|
{
|
|
UINT uNewLast = (pTG->Comm.uovLast+1) % NUM_OVS;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "iov_flush: 1st=%d, last=%d \n", pTG->Comm.uovFirst, pTG->Comm.uovLast) );
|
|
|
|
lpovr = pTG->Comm.rgovr+uNewLast;
|
|
if (uNewLast != pTG->Comm.uovFirst)
|
|
{
|
|
BG_CHK(lpovr->eState==eFREE);
|
|
}
|
|
else
|
|
{
|
|
BG_CHK(lpovr->eState==eIO_PENDING);
|
|
if (!iov_flush(pTG, lpovr, TRUE))
|
|
{
|
|
BG_CHK(lpovr->eState==eALLOC);
|
|
ov_unget(pTG, lpovr);
|
|
lpovr=NULL; // We fail if a flush operation failed...
|
|
}
|
|
else
|
|
{
|
|
BG_CHK(lpovr->eState==eALLOC);
|
|
pTG->Comm.uovFirst = (pTG->Comm.uovFirst+1) % NUM_OVS;
|
|
}
|
|
}
|
|
if (lpovr) pTG->Comm.uovLast = uNewLast;
|
|
}
|
|
if (lpovr && lpovr->eState!=eALLOC)
|
|
{
|
|
BG_CHK(lpovr->eState==eFREE && !lpovr->dwcb);
|
|
BG_CHK(pTG->Comm.covAlloced < NUM_OVS);
|
|
pTG->Comm.covAlloced++;
|
|
lpovr->eState=eALLOC;
|
|
}
|
|
return lpovr;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL ov_unget(PThrdGlbl pTG, OVREC *lpovr)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "In ov_UNget lpovr=%lx at %ld \n", lpovr, GetTickCount() ) );
|
|
|
|
|
|
if (lpovr->eState!=eALLOC ||
|
|
!pTG->Comm.covAlloced || lpovr!=(pTG->Comm.rgovr+pTG->Comm.uovLast))
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "ov_unget: invalid lpovr.\r\n"));
|
|
BG_CHK(FALSE);
|
|
goto end;
|
|
}
|
|
|
|
BG_CHK(!lpovr->dwcb);
|
|
|
|
if (pTG->Comm.covAlloced==1)
|
|
{
|
|
BG_CHK(pTG->Comm.uovLast == pTG->Comm.uovFirst);
|
|
pTG->Comm.uovLast = pTG->Comm.uovFirst = 0;
|
|
}
|
|
else
|
|
{
|
|
pTG->Comm.uovLast = (pTG->Comm.uovLast)? (pTG->Comm.uovLast-1) : (NUM_OVS-1);
|
|
}
|
|
pTG->Comm.covAlloced--;
|
|
lpovr->eState=eFREE;
|
|
fRet = TRUE;
|
|
|
|
end:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL ov_write(PThrdGlbl pTG, OVREC *lpovr, LPDWORD lpdwcbWrote)
|
|
{
|
|
// Write out the buffer associated with lpovr.
|
|
BG_CHK(lpovr->eState==eALLOC);
|
|
if (!lpovr->dwcb)
|
|
{
|
|
ov_unget(pTG, lpovr);
|
|
lpovr=NULL;
|
|
}
|
|
else
|
|
{
|
|
BOOL fRet;
|
|
DWORD dw;
|
|
int err;
|
|
OVERLAPPED *lpov = &(lpovr->ov);
|
|
|
|
|
|
DWORD cbQueue;
|
|
|
|
BG_CHK(lpovr->dwcb<=OVBUFSIZE);
|
|
pTG->Comm.comstat.cbOutQue += lpovr->dwcb;
|
|
|
|
|
|
GetCommErrorNT( pTG, (HANDLE) pTG->Comm.nCid, &err, &(pTG->Comm.comstat));
|
|
|
|
if(err) {
|
|
|
|
(MyDebugPrint(pTG, LOG_ERR, "ov_write GetCommError failed \n") );
|
|
D_GotError(pTG, pTG->Comm.nCid, err, &(pTG->Comm.comstat));
|
|
}
|
|
|
|
cbQueue = pTG->Comm.comstat.cbOutQue;
|
|
|
|
|
|
OUTMON(pTG, lpovr->rgby, (USHORT)lpovr->dwcb);
|
|
{
|
|
BEFORECALL;
|
|
INTERCALL("Write");
|
|
|
|
( MyDebugPrint(pTG, LOG_ALL, "Before WriteFile lpb=%x, cb=%d lpovr=%lx at %ld \n",
|
|
lpovr->rgby, lpovr->dwcb, lpovr, GetTickCount() ) );
|
|
|
|
if (!(fRet = WriteFile((HANDLE)pTG->Comm.nCid, lpovr->rgby, lpovr->dwcb,
|
|
lpdwcbWrote, lpov)))
|
|
{
|
|
dw=GetLastError();
|
|
}
|
|
AFTERCALL("Write",*lpdwcbWrote);
|
|
|
|
GetCommErrorNT( pTG, (HANDLE) pTG->Comm.nCid, &err, &(pTG->Comm.comstat));
|
|
|
|
if(err) {
|
|
(MyDebugPrint(pTG, LOG_ERR, "ov_write GetCommError failed \n") );
|
|
D_GotError(pTG, pTG->Comm.nCid, err, &(pTG->Comm.comstat));
|
|
}
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "Queue before=%lu; after = %lu. n= %lu, *pn=%lu\r\n",
|
|
(unsigned long) cbQueue,
|
|
(unsigned long) (pTG->Comm.comstat.cbOutQue),
|
|
(unsigned long) lpovr->dwcb,
|
|
(unsigned long) *lpdwcbWrote));
|
|
|
|
}
|
|
if (fRet)
|
|
{
|
|
// Write operation completed
|
|
(MyDebugPrint(pTG, LOG_ALL, "WARNING: WriteFile returned w/o wait %ld\n", GetTickCount() ) );
|
|
|
|
OVL_CLEAR( lpov);
|
|
lpovr->dwcb=0;
|
|
ov_unget(pTG, lpovr);
|
|
lpovr=NULL;
|
|
}
|
|
else
|
|
{
|
|
if (dw==ERROR_IO_PENDING)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ALL, "WriteFile returned PENDING at %ld\n", GetTickCount() ));
|
|
*lpdwcbWrote = lpovr->dwcb; // We set *pn to n on success else 0.
|
|
lpovr->eState=eIO_PENDING;
|
|
}
|
|
else
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "WriteFile returns error 0x%lx",
|
|
(unsigned long) dw));
|
|
OVL_CLEAR(lpov);
|
|
lpovr->dwcb=0;
|
|
ov_unget(pTG, lpovr);
|
|
lpovr=NULL;
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
BG_CHK(!lpovr || lpovr->eState==eIO_PENDING);
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
BG_CHK(!lpovr);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL ov_drain(PThrdGlbl pTG, BOOL fLongTO)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
UINT u = pTG->Comm.covAlloced;
|
|
|
|
while(u--)
|
|
{
|
|
OVREC *lpovr = pTG->Comm.rgovr+pTG->Comm.uovFirst;
|
|
OVERLAPPED *lpov = &(lpovr->ov);
|
|
|
|
if (lpovr->eState==eIO_PENDING)
|
|
{
|
|
if (!iov_flush(pTG, lpovr, fLongTO)) fRet=FALSE;
|
|
BG_CHK(lpovr->eState==eALLOC);
|
|
lpovr->eState=eFREE;
|
|
BG_CHK(pTG->Comm.covAlloced);
|
|
pTG->Comm.covAlloced--;
|
|
pTG->Comm.uovFirst = (pTG->Comm.uovFirst+1) % NUM_OVS;
|
|
}
|
|
else
|
|
{
|
|
// Only the newest (last) structure can be still in the
|
|
// allocated state.
|
|
BG_CHK(lpovr->eState==eALLOC && !u);
|
|
BG_CHK(pTG->Comm.lpovrCur == lpovr); // Ugly check
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<WARNING>> ov_drain:"
|
|
" called when alloc'd structure pending\r\n"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!pTG->Comm.covAlloced) pTG->Comm.uovFirst=pTG->Comm.uovLast=0;
|
|
else
|
|
{
|
|
BG_CHK( pTG->Comm.covAlloced==1
|
|
&& pTG->Comm.uovFirst==pTG->Comm.uovLast
|
|
&& pTG->Comm.uovFirst<NUM_OVS
|
|
&& pTG->Comm.rgovr[pTG->Comm.uovFirst].eState==eALLOC);
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL ov_init(PThrdGlbl pTG)
|
|
{
|
|
UINT u;
|
|
OVREC *lpovr = pTG->Comm.rgovr;
|
|
|
|
// init overlapped structures, including creating events...
|
|
if (pTG->Comm.fovInited)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> ov_init: we're *already* inited.\r\n>>",
|
|
(unsigned long) (pTG->Comm.covAlloced)));
|
|
BG_CHK(FALSE);
|
|
ov_deinit(pTG);
|
|
}
|
|
|
|
BG_CHK(!pTG->Comm.fovInited && !pTG->Comm.covAlloced);
|
|
|
|
for (u=0;u<NUM_OVS;u++,lpovr++) {
|
|
OVERLAPPED *lpov = &(lpovr->ov);
|
|
BG_CHK(lpovr->eState==eDEINIT);
|
|
BG_CHK(!(lpovr->dwcb));
|
|
_fmemset(lpov, 0, sizeof(OVERLAPPED));
|
|
lpov->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (lpov->hEvent==NULL)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> ov_init: couldn't create event #%lu\r\n",
|
|
(unsigned long) u));
|
|
goto failure;
|
|
}
|
|
lpovr->eState=eFREE;
|
|
lpovr->dwcb=0;
|
|
}
|
|
|
|
pTG->Comm.fovInited=TRUE;
|
|
|
|
return TRUE;
|
|
|
|
failure:
|
|
BG_CHK(!pTG->Comm.fovInited);
|
|
while (u--)
|
|
{--lpovr; CloseHandle(lpovr->ov.hEvent); lpovr->eState=eDEINIT;}
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL ov_deinit(PThrdGlbl pTG)
|
|
{
|
|
UINT u=NUM_OVS;
|
|
OVREC *lpovr = pTG->Comm.rgovr;
|
|
|
|
if (!pTG->Comm.fovInited)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<WARNING>> ov_deinit: Already deinited.\r\n"));
|
|
goto end;
|
|
}
|
|
|
|
|
|
//
|
|
// if handoff ==> dont flush
|
|
//
|
|
if (pTG->Comm.fEnableHandoff && pTG->Comm.fDataCall) {
|
|
goto lNext;
|
|
}
|
|
|
|
// deinit overlapped structures, including freeing events...
|
|
if (pTG->Comm.covAlloced)
|
|
{
|
|
DWORD dw;
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<WARNING>> ov_deinit: %lu IO's pending.\r\n",
|
|
(unsigned long) pTG->Comm.covAlloced));
|
|
if (pTG->Comm.lpovrCur) {ov_write(pTG, pTG->Comm.lpovrCur,&dw); pTG->Comm.lpovrCur=NULL;}
|
|
ov_drain(pTG, FALSE);
|
|
}
|
|
BG_CHK(!pTG->Comm.covAlloced);
|
|
|
|
|
|
|
|
lNext:
|
|
|
|
while (u--)
|
|
{
|
|
BG_CHK(!(lpovr->dwcb));
|
|
BG_CHK(lpovr->eState==eFREE);
|
|
lpovr->eState=eDEINIT;
|
|
if (lpovr->ov.hEvent) CloseHandle(lpovr->ov.hEvent);
|
|
_fmemset(&(lpovr->ov), 0, sizeof(lpovr->ov));
|
|
lpovr++;
|
|
}
|
|
|
|
pTG->Comm.fovInited=FALSE;
|
|
|
|
end:
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL iov_flush(PThrdGlbl pTG, OVREC *lpovr, BOOL fLongTO)
|
|
// On return, state of lpovr is *always* eALLOC, but
|
|
// it returns FALSE if there was a comm error while trying
|
|
// to flush (i.e. drain) the buffer.
|
|
// If we timeout with the I/O operation still pending, we purge
|
|
// the output buffer and abort all pending write operations.
|
|
{
|
|
DWORD dwcbPrev;
|
|
DWORD dwStart = GetTickCount();
|
|
BOOL fRet=FALSE;
|
|
DWORD dw;
|
|
int err;
|
|
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "In iov_flush fLongTo=%d lpovr=%lx at %ld \n", fLongTO, lpovr, GetTickCount() ) );
|
|
|
|
|
|
BG_CHK(lpovr->eState==eIO_PENDING);
|
|
|
|
if (pTG->Comm.nCid<0) {lpovr->eState=eALLOC; goto end;}
|
|
|
|
// We call
|
|
// WaitForSingleObject multiple times ... basically
|
|
// the same logic as the code in the old FComDirectWrite...
|
|
// fLongTO is TRUE except when initing
|
|
// the modem (see comments for FComDrain).
|
|
|
|
BG_CHK(lpovr->ov.hEvent);
|
|
GetCommErrorNT( pTG, (HANDLE) pTG->Comm.nCid, &err, &(pTG->Comm.comstat));
|
|
DEBUGSTMT(if(err) D_GotError(pTG, pTG->Comm.nCid, err, &(pTG->Comm.comstat)););
|
|
dwcbPrev = pTG->Comm.comstat.cbOutQue;
|
|
|
|
while(WaitForSingleObject(lpovr->ov.hEvent,
|
|
fLongTO? LONG_DRAINTIMEOUT : SHORT_DRAINTIMEOUT)==WAIT_TIMEOUT)
|
|
{
|
|
BOOL fStuckOnce=FALSE;
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "After WaitForSingleObject TIMEOUT %ld \n", GetTickCount() ) );
|
|
|
|
|
|
|
|
GetCommErrorNT( pTG, (HANDLE) pTG->Comm.nCid, &err, &(pTG->Comm.comstat));
|
|
DEBUGSTMT(if(err) D_GotError(pTG, pTG->Comm.nCid, err, &(pTG->Comm.comstat)););
|
|
|
|
// Timed out -- check if any progress
|
|
if (dwcbPrev == pTG->Comm.comstat.cbOutQue)
|
|
{
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "WARNING: No progress %d %ld \n", dwcbPrev, GetTickCount() ) );
|
|
|
|
// No progress... If not in XOFFHold, we break....
|
|
if(!FComInXOFFHold(pTG))
|
|
{
|
|
if(fStuckOnce)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> iov_flush:: No Progress -- OutQ still %d at %lu\r\n", (int)pTG->Comm.comstat.cbOutQue, GetTickCount()));
|
|
iModemSetError(pTG, MODEMERR_TIMEOUT, 0, MODEMERRFLAGS_TRANSIENT);
|
|
goto done;
|
|
}
|
|
else
|
|
fStuckOnce=TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Some progress...
|
|
dwcbPrev= pTG->Comm.comstat.cbOutQue;
|
|
fStuckOnce=FALSE;
|
|
}
|
|
|
|
// Independant deadcom timeout... I don't want
|
|
// to use TO because of the 16bit limitation.
|
|
{
|
|
DWORD dwNow = GetTickCount();
|
|
DWORD dwDelta = (dwNow>dwStart)
|
|
? (dwNow-dwStart)
|
|
: (0xFFFFFFFFL-dwStart) + dwNow;
|
|
if (dwDelta > (unsigned long)
|
|
((fLongTO)?LONG_DEADCOMMTIMEOUT:SHORT_DEADCOMMTIMEOUT))
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> Drain:: Deadman Timer -- OutQ still %d at %lu\r\n", (int) pTG->Comm.comstat.cbOutQue, GetTickCount()));
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
(MyDebugPrint(pTG, LOG_ALL, "Before GetOverlappedResult %ld \n", GetTickCount() ) );
|
|
|
|
if (GetOverlappedResult((HANDLE)pTG->Comm.nCid, &(lpovr->ov), &dw, FALSE))
|
|
{
|
|
|
|
fRet=TRUE;
|
|
}
|
|
else
|
|
{
|
|
dw = GetLastError();
|
|
(MyDebugPrint(pTG, LOG_ERR, "<<ERROR>> iov_flush:GetOverlappedResult returns error 0x%lx\r\n",
|
|
(unsigned long) dw));
|
|
if (dw==ERROR_IO_INCOMPLETE)
|
|
{
|
|
// IO operation still pending, but we *have* to
|
|
// reuse this buffer -- what should we do?!-
|
|
// purge the output buffer and abort all pending
|
|
// write operations on it..
|
|
|
|
(MyDebugPrint(pTG, LOG_ERR, "ERROR: Incomplete at %ld \n", GetTickCount() ) );
|
|
PurgeComm((HANDLE)pTG->Comm.nCid, PURGE_TXABORT);
|
|
}
|
|
fRet=FALSE;
|
|
}
|
|
OVL_CLEAR( &(lpovr->ov));
|
|
lpovr->eState=eALLOC;
|
|
lpovr->dwcb=0;
|
|
|
|
end:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void WINAPI FComOverlappedIO(PThrdGlbl pTG, BOOL fBegin)
|
|
{
|
|
(MyDebugPrint(pTG, LOG_ALL, "Turning %s OVERLAPPED IO\r\n", (fBegin) ? "ON" : "OFF"));
|
|
pTG->Comm.fDoOverlapped=fBegin;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
void d_TimeStamp(LPSTR lpsz, DWORD dwID)
|
|
{
|
|
(MyDebugPrint(0, LOG_ERR, "<<TS>> %s(%lu):%lu\r\n",
|
|
(LPSTR) lpsz, (unsigned long) dwID, (unsigned long) GetTickCount()));
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
GetCommErrorNT(
|
|
PThrdGlbl pTG,
|
|
HANDLE h,
|
|
int * pn,
|
|
LPCOMSTAT pstat)
|
|
|
|
|
|
{
|
|
|
|
if (!ClearCommError( h, pn, pstat) ) {
|
|
MyDebugPrint(pTG, LOG_ERR, "ERROR ClearComError(0x%lx) FAILS. Returns 0x%lu\n",
|
|
h, GetLastError() );
|
|
*(pn) = MYGETCOMMERROR_FAILED;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
void
|
|
ClearCommCache(
|
|
PThrdGlbl pTG
|
|
)
|
|
|
|
{
|
|
pTG->CommCache.dwCurrentSize = 0;
|
|
pTG->CommCache.dwOffset = 0;
|
|
|
|
|
|
}
|