4676 lines
121 KiB
C
4676 lines
121 KiB
C
/******************************************************************************
|
|
|
|
(C) Copyright MICROSOFT Corp., 1987-1994
|
|
|
|
Rob Williams, June 93 w/ State machine and parser plagarized from RAS
|
|
|
|
Chris Caputo, 1994 - and superheavily modified since then...
|
|
|
|
******************************************************************************/
|
|
|
|
#include "unimdm.h"
|
|
#include "mcxp.h"
|
|
#include "common.h"
|
|
|
|
#include <ntddmodm.h>
|
|
|
|
#ifdef VOICEVIEW
|
|
#include "voicview.h"
|
|
int VVSetClass(MODEMINFORMATION *hPort, WORD wClass);
|
|
int VVCallBackFunc(MODEMINFORMATION *hPort, WORD wFunction);
|
|
void VVTimerCallback( void );
|
|
RealMonitor(APIINFO *pInfo);
|
|
|
|
|
|
char szMonitorVVon[] = "MonitorVoiceViewOn";
|
|
char szMonitorVVoff[] = "MonitorVoiceViewOff";
|
|
#endif // VOICEVIEW
|
|
|
|
VOID WINAPI
|
|
HWDetectionRoutine(
|
|
MODEMINFORMATION * pModemInfo,
|
|
LPOVERNODE pNode
|
|
);
|
|
|
|
void WINAPI
|
|
CancelPendingIoAndPurgeCommBuffers(
|
|
PMODEMINFORMATION pModemInfo,
|
|
BOOL Purge
|
|
);
|
|
|
|
LPSTR
|
|
CreateDialCommands(
|
|
MODEMINFORMATION *pModemInfo,
|
|
LPSTR szPhoneNumber,
|
|
BOOL *fOriginate,
|
|
DWORD DialOptions
|
|
);
|
|
|
|
|
|
|
|
VOID SynchronizeCommConfigSettings(MODEMINFORMATION * pModemInfo,
|
|
BOOL fUpdateModemSys);
|
|
|
|
|
|
//****************************************************************************
|
|
// LONG MCXOpen (LPTSTR, HANDLE, LPTSTR, LPHANDLE, DWORD, DWORD)
|
|
//
|
|
// Function: Open the modem port
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXOpen (LPTSTR szModemName,
|
|
HANDLE hDevice,
|
|
LPTSTR szKey,
|
|
LPHANDLE lph,
|
|
DWORD dwID,
|
|
DWORD dwCompletionKey)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
|
|
ASSERT(*lph == NULL);
|
|
|
|
// Allocate the modeminfo control block
|
|
//
|
|
pModemInfo = AllocateModem(szKey, szModemName, hDevice);
|
|
|
|
if (pModemInfo != NULL)
|
|
{
|
|
// We can get the control block
|
|
//
|
|
pModemInfo->mi_PortHandle = hDevice;
|
|
pModemInfo->mi_dwID = dwID;
|
|
pModemInfo->mi_dwCompletionKey = dwCompletionKey;
|
|
|
|
*lph = (HANDLE)pModemInfo;
|
|
|
|
MCXPRINTF("MCXOpen");
|
|
|
|
return MODEM_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
*lph = NULL;
|
|
return MODEM_FAILURE;
|
|
};
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXClose (HANDLE, HANDLE)
|
|
//
|
|
// Function: Close the modem port
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG
|
|
MCXClose(
|
|
HANDLE hModem,
|
|
HANDLE hComm,
|
|
BOOL LineClosed
|
|
)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("MCXClose");
|
|
|
|
// Sets CommMask to 0, waits for any I/O to complete and purges buffers.
|
|
CancelPendingIoAndPurgeCommBuffers(pModemInfo, TRUE);
|
|
|
|
// Reset the modem if it is not connected
|
|
//
|
|
if ((pModemInfo->mi_ModemState != STATE_CONNECTED)
|
|
&&
|
|
(pModemInfo->mi_pszReset != NULL)
|
|
&&
|
|
(!LineClosed))
|
|
{
|
|
COMMTIMEOUTS commtimeout;
|
|
HANDLE hEvent;
|
|
|
|
// Set write timeout to one second
|
|
//
|
|
commtimeout.ReadIntervalTimeout = 100;
|
|
commtimeout.ReadTotalTimeoutMultiplier = 10;
|
|
commtimeout.ReadTotalTimeoutConstant = 5000;
|
|
commtimeout.WriteTotalTimeoutMultiplier= 0;
|
|
commtimeout.WriteTotalTimeoutConstant = 1000;
|
|
|
|
SetCommTimeouts(pModemInfo->mi_PortHandle, &commtimeout);
|
|
|
|
if ((hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) != NULL)
|
|
{
|
|
DWORD cb;
|
|
OVERLAPPED ov;
|
|
|
|
ov.Internal = 0;
|
|
ov.InternalHigh = 0;
|
|
ov.Offset = 0;
|
|
ov.OffsetHigh = 0;
|
|
|
|
// OR with 1 to prevent it from being posted to the completion port.
|
|
//
|
|
ov.hEvent = (HANDLE)((DWORD)hEvent | 1);
|
|
|
|
MCXPRINTF("Sending Reset string.");
|
|
|
|
PrintString(pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
pModemInfo->mi_pszReset,
|
|
pModemInfo->mi_dwResetLen,
|
|
PS_SEND);
|
|
|
|
if (FALSE == WriteFile(pModemInfo->mi_PortHandle, pModemInfo->mi_pszReset,
|
|
pModemInfo->mi_dwResetLen, &cb, &ov))
|
|
{
|
|
DWORD dwResult = GetLastError();
|
|
|
|
if (ERROR_IO_PENDING == dwResult)
|
|
{
|
|
GetOverlappedResult(pModemInfo->mi_PortHandle,
|
|
&ov,
|
|
&cb,
|
|
TRUE);
|
|
} else {
|
|
|
|
MCXPRINTF1("WriteFile() in MCXClose() failed (0x%8x)!", dwResult);
|
|
cb=0;
|
|
|
|
}
|
|
}
|
|
|
|
if (cb == pModemInfo->mi_dwResetLen) {
|
|
//
|
|
// wrote the reset string, see if can get a response
|
|
//
|
|
|
|
BYTE ResponseBuffer[20];
|
|
|
|
ResetEvent(hEvent);
|
|
|
|
if (!ReadFile(
|
|
pModemInfo->mi_PortHandle,
|
|
ResponseBuffer,
|
|
20,
|
|
&cb,
|
|
&ov
|
|
)) {
|
|
|
|
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
|
|
GetOverlappedResult(
|
|
pModemInfo->mi_PortHandle,
|
|
&ov,
|
|
&cb,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
PrintString(pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
ResponseBuffer,
|
|
cb,
|
|
PS_RECV
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MCXPRINTF1("WriteFile() in MCXClose() only wrote %d bytes!",
|
|
cb);
|
|
}
|
|
|
|
CloseHandle(hEvent);
|
|
}
|
|
|
|
SetCommMask(
|
|
pModemInfo->mi_PortHandle,
|
|
0
|
|
);
|
|
|
|
PurgeComm(
|
|
pModemInfo->mi_PortHandle,
|
|
PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR
|
|
);
|
|
|
|
|
|
}
|
|
|
|
if (pModemInfo->mi_CompatibilityFlags & COMPAT_FLAG_LOWER_DTR) {
|
|
//
|
|
// For USR 33.6 modem that stop working after being open and closed
|
|
//
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, CLRDTR);
|
|
|
|
Sleep(50);
|
|
}
|
|
|
|
|
|
ASSERT(pModemInfo->mi_lpOverlappedRW == NULL);
|
|
ASSERT(pModemInfo->mi_lpOverlappedEvent == NULL);
|
|
|
|
// Free the modem control block
|
|
//
|
|
FreeModem(pModemInfo, hComm);
|
|
return MODEM_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXInit (HANDLE, HANDLE)
|
|
//
|
|
// Function: Initializes the modem port
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_PENDING if operation is pending
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXInit(HANDLE hModem, MCX_IN *pmcxi)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
LPSTR pszzCmdInMem1, pszzCmdInMem2;
|
|
DWORD dwRet = MODEM_FAILURE;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("MCXInit");
|
|
|
|
if (pModemInfo->mi_ModemState == STATE_UNKNOWN ||
|
|
pModemInfo->mi_ModemState == STATE_DISCONNECTED ||
|
|
pModemInfo->mi_ModemState == STATE_MONITORING)
|
|
{
|
|
#ifdef VOICEVIEW
|
|
VVSetClass( pModemInfo, VVCLASS_0 ); // verify modem is in correct fclass
|
|
#endif // VOICEVIEW
|
|
|
|
pModemInfo->mi_dwNegotiatedModemOptions = 0;
|
|
pModemInfo->mi_dwNegotiatedDCERate = 0;
|
|
pModemInfo->mi_dwNegotiatedDTERate = 0;
|
|
|
|
// Get the current comm config modem settings from modem.sys and set
|
|
// the current DCE rate and modem options. Also, fSettingsInitStringsBuilt
|
|
// will be set appropriately.
|
|
//
|
|
SynchronizeCommConfigSettings(pModemInfo,
|
|
TRUE);
|
|
|
|
if (pszzCmdInMem1 = GetCommonCommandStringCopy(pModemInfo->mi_hCommon,
|
|
COMMON_INIT_COMMANDS))
|
|
{
|
|
MCXPRINTF("Initializing modem...");
|
|
if (pModemInfo->mi_fSettingsInitStringsBuilt == FALSE)
|
|
{
|
|
MCXPRINTF("building SettingsInit.");
|
|
if (!CreateSettingsInitEntry(pModemInfo))
|
|
{
|
|
// only catastrophic if it is a modem
|
|
//
|
|
if (pModemInfo->mi_fModem)
|
|
{
|
|
MCXPRINTF("CreateSettingsInitEntry failed!!!");
|
|
LogString(pModemInfo->mi_hLogFile, pModemInfo->mi_dwID,
|
|
IDS_MSGERR_FAILED_INITSTRINGCONSTRUCTION);
|
|
LocalFree(pszzCmdInMem1);
|
|
goto Failure;
|
|
}
|
|
}
|
|
pModemInfo->mi_fSettingsInitStringsBuilt = TRUE;
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("using cached SettingsInit.");
|
|
}
|
|
|
|
if (pszzCmdInMem2 = LoadRegCommands(pModemInfo, szSettingsInit,
|
|
pszzCmdInMem1))
|
|
{
|
|
pModemInfo->mi_ModemState = STATE_INITIALIZING;
|
|
MCXPRINTF("State <- Initializing");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,IDS_MSGLOG_INIT);
|
|
dwRet = ModemCommand(pModemInfo, pmcxi->dwReqID,
|
|
pmcxi->pMcxOut, pszzCmdInMem2);
|
|
};
|
|
|
|
// Free the first buffer
|
|
//
|
|
LocalFree(pszzCmdInMem1);
|
|
}
|
|
}
|
|
|
|
if (MODEM_FAILURE == dwRet)
|
|
{
|
|
Failure:
|
|
MCXPRINTF("Init failed.");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGERR_FAILED_INIT);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXDial (HANDLE, LPSTR, HANDLE)
|
|
//
|
|
// Function: Dials the modem with the provided number. The number could be in
|
|
// the following formats:
|
|
// "" - originate
|
|
// ";" - dialtone detection
|
|
// "5551212" - dial and originate
|
|
// "5551212;" - dial
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_PENDING if operation is pending
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG
|
|
MCXDial(
|
|
HANDLE hModem,
|
|
LPSTR szData,
|
|
MCX_IN *pmcxi,
|
|
DWORD DialOptions
|
|
)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
LPSTR pszzCmdInMem;
|
|
DWORD dwRet = MODEM_FAILURE;
|
|
BOOL fOriginate;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("MCXDial");
|
|
|
|
if (pModemInfo->mi_ModemState == STATE_DISCONNECTED ||
|
|
pModemInfo->mi_ModemState == STATE_MONITORING ||
|
|
pModemInfo->mi_ModemState == STATE_DIALED)
|
|
{
|
|
#ifdef VOICEVIEW
|
|
VVSetClass( pModemInfo, VVCLASS_0 ); // verify modem is in correct fclass
|
|
#endif // VOICEVIEW
|
|
|
|
MCXPRINTF("building dial strings...");
|
|
|
|
// build dial commands in memory (as opposed to the registry)
|
|
//
|
|
if (pszzCmdInMem = CreateDialCommands(pModemInfo, szData, &fOriginate, DialOptions))
|
|
{
|
|
MCXPRINTF("Dialing...");
|
|
pModemInfo->mi_ModemState = fOriginate ? STATE_ORIGINATING : STATE_DIALING;
|
|
|
|
|
|
if (fOriginate)
|
|
{
|
|
MCXPRINTF("State <- Dialing and Originating");
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("State <- Dialing");
|
|
}
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_DIAL);
|
|
dwRet = ModemCommand(pModemInfo, pmcxi->dwReqID,
|
|
pmcxi->pMcxOut, pszzCmdInMem);
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("couldn't build dial strings...");
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGERR_FAILED_DIALSTRINGCONSTRUCTION, szData);
|
|
}
|
|
}
|
|
|
|
if (MODEM_FAILURE == dwRet)
|
|
{
|
|
MCXPRINTF("Dial failed.");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGERR_FAILED_DIAL);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXMonitor (HANDLE, DWORD, HANDLE)
|
|
//
|
|
// Function: initializes the modem to monitor the incoming call
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_PENDING if operation is pending
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXMonitor(HANDLE hModem, DWORD dwType, MCX_IN *pmcxi)
|
|
{
|
|
#ifdef VOICEVIEW
|
|
MCXPRINTF("MCXMonitoring...-go to fclass80");
|
|
VVSetClass( pInfo->hPort, VVCLASS_80 ); // verify modem is in correct fclass
|
|
return( RealMonitor(pInfo));
|
|
}
|
|
|
|
LONG RealMonitor(HANDLE hModem, DWORD dwType, HANDLE hEvent)
|
|
{
|
|
#endif // VOICEVIEW
|
|
PMODEMINFORMATION pModemInfo;
|
|
LPSTR pszzCmdInMem;
|
|
// LPSTR pszMonitorKey = szMonitor; // default reg key
|
|
DWORD dwRet = MODEM_FAILURE;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("MCXMonitor");
|
|
|
|
if (pModemInfo->mi_ModemState == STATE_DISCONNECTED ||
|
|
pModemInfo->mi_ModemState == STATE_MONITORING)
|
|
{
|
|
#ifdef VOICEVIEW
|
|
if ( pModemInfo->VVInfo.wState != VVSTATE_NONE )
|
|
{
|
|
// only enabled when voiceview is waiting for stuff
|
|
// use the commands to set the modem into fclass80 (voiceview)
|
|
// or use the commands to set default fclass0
|
|
if ( pModemInfo->VVInfo.wClass == VVCLASS_80 )
|
|
{
|
|
pszMonitorKey = szMonitorVVon;
|
|
// we will tell VV can use the port after the OK
|
|
}
|
|
else
|
|
{
|
|
pszMonitorKey = szMonitorVVoff;
|
|
VVCallBackFunc( pModemInfo, VVR_LINE_GONE ); // tell VV CAN'T use port
|
|
}
|
|
}
|
|
#endif // VOICEVIEW
|
|
|
|
if (pszzCmdInMem = GetCommonCommandStringCopy(pModemInfo->mi_hCommon,
|
|
COMMON_MONITOR_COMMANDS))
|
|
{
|
|
MCXPRINTF("Monitoring...");
|
|
pModemInfo->mi_ModemState = STATE_MONITORING;
|
|
MCXPRINTF("State <- Monitoring");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_MONITOR);
|
|
|
|
// does the caller want continuous monitoring?
|
|
//
|
|
pModemInfo->mi_fContinuousMonitoring = dwType;
|
|
dwRet = ModemCommand(pModemInfo, pmcxi->dwReqID,
|
|
pmcxi->pMcxOut, pszzCmdInMem);
|
|
}
|
|
}
|
|
|
|
if (MODEM_FAILURE == dwRet)
|
|
{
|
|
MCXPRINTF("Monitor failed.");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGERR_FAILED_MONITOR);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXAnswer (HANDLE, HANDLE)
|
|
//
|
|
// Function: Answers the incoming call
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_PENDING if operation is pending
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXAnswer(HANDLE hModem, MCX_IN *pmcxi)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
LPSTR pszzCmdInMem;
|
|
DWORD dwRet = MODEM_FAILURE;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("MCXAnswer");
|
|
|
|
if (pModemInfo->mi_ModemState == STATE_DISCONNECTED ||
|
|
pModemInfo->mi_ModemState == STATE_MONITORING ||
|
|
pModemInfo->mi_ModemState == STATE_DIALED)
|
|
{
|
|
#ifdef VOICEVIEW
|
|
VVSetClass( pModemInfo, VVCLASS_0 ); // verify modem is in correct fclass
|
|
#endif // VOICEVIEW
|
|
|
|
if (pszzCmdInMem = GetCommonCommandStringCopy(pModemInfo->mi_hCommon, COMMON_ANSWER_COMMANDS))
|
|
{
|
|
MCXPRINTF("Answering...");
|
|
pModemInfo->mi_ModemState = STATE_ANSWERING;
|
|
MCXPRINTF("State <- Answering");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_ANSWER);
|
|
dwRet = ModemCommand(pModemInfo, pmcxi->dwReqID,
|
|
pmcxi->pMcxOut, pszzCmdInMem);
|
|
}
|
|
}
|
|
|
|
if (MODEM_FAILURE == dwRet)
|
|
{
|
|
MCXPRINTF("Answer failed.");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGERR_FAILED_ANSWER);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXHangup (HANDLE, HANDLE)
|
|
//
|
|
// Function: hangs up the modem
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_PENDING if operation is pending
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXHangup(HANDLE hModem, MCX_IN *pmcxi)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
DWORD dwTmp;
|
|
ULONG ulError;
|
|
DWORD dwRet = MODEM_FAILURE;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("MCXHangup");
|
|
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_HANGUP);
|
|
MCXPRINTF("Hanging up...");
|
|
|
|
// Since a hangup was requested, make sure we always return MODEM_SUCCESS
|
|
// when we complete the hangup.
|
|
// (Note: this even applies to STATE_HANGING_UP_REMOTE!)
|
|
//
|
|
pModemInfo->mi_dwUnconditionalReturnValue = MODEM_SUCCESS;
|
|
|
|
pModemInfo->mi_DisconnectHandler=NULL;
|
|
|
|
|
|
// Handle hangup requests for when we are already hanging up by returning PENDING
|
|
//
|
|
if (STATE_HANGING_UP_REMOTE == pModemInfo->mi_ModemState ||
|
|
STATE_HANGING_UP_DTR == pModemInfo->mi_ModemState ||
|
|
STATE_HANGING_UP_NON_CMD == pModemInfo->mi_ModemState ||
|
|
STATE_HANGING_UP_CMD == pModemInfo->mi_ModemState)
|
|
{
|
|
MCXPRINTF("received a hang up request while already hanging up. Returning PENDING...");
|
|
|
|
pModemInfo->mi_ReqID = pmcxi->dwReqID;
|
|
pModemInfo->mi_pmcxo = pmcxi->pMcxOut;
|
|
|
|
return MODEM_PENDING;
|
|
}
|
|
|
|
// Flush the port and close all of the gates!
|
|
//
|
|
//
|
|
ModemSetPassthrough(pModemInfo, MODEM_NOPASSTHROUGH);
|
|
|
|
// Sets CommMask to 0, waits for any I/O to complete and purges buffers.
|
|
CancelPendingIoAndPurgeCommBuffers(
|
|
pModemInfo,
|
|
(pModemInfo->mi_ModemState != STATE_REMOTE_DROPPED)
|
|
);
|
|
|
|
// Make sure RTS is high.
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, SETRTS);
|
|
|
|
// Free the current cmd if one exists
|
|
if (pModemInfo->mi_pszzCmds)
|
|
{
|
|
if (pModemInfo->mi_pszzCmds != pModemInfo->mi_pszzHangupCmds)
|
|
{
|
|
LocalFree(pModemInfo->mi_pszzCmds);
|
|
}
|
|
pModemInfo->mi_pszzCmds = NULL;
|
|
}
|
|
|
|
// Reset hangup counter to 1.
|
|
//
|
|
pModemInfo->mi_dwHangupTryCount = 1;
|
|
|
|
switch (pModemInfo->mi_ModemState)
|
|
{
|
|
case STATE_DIALED:
|
|
//
|
|
// need to send "ATH<cr>"
|
|
//
|
|
MCXPRINTF("State <- Hanging up cmd");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_CMD;
|
|
dwRet = ModemCommand(pModemInfo, pmcxi->dwReqID,
|
|
pmcxi->pMcxOut, pModemInfo->mi_pszzHangupCmds);
|
|
break;
|
|
|
|
|
|
|
|
case STATE_DIALING:
|
|
case STATE_ANSWERING:
|
|
case STATE_ORIGINATING:
|
|
// case STATE_INITIALIZING:
|
|
if (pModemInfo->mi_fModem)
|
|
{
|
|
// send a character to cancel the call/answer
|
|
// don't need to #define \r because modems only care about it being a character,
|
|
// not a specific one.
|
|
//
|
|
PrintString(pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
"\r",
|
|
1,
|
|
PS_SEND);
|
|
dwRet = ModemWrite(pModemInfo, "\r", 1, &dwTmp, TO_FLUSH);
|
|
if (dwRet == MODEM_FAILURE)
|
|
{
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("WriteComm Error. State <- Unknown");
|
|
}
|
|
else
|
|
{
|
|
pModemInfo->mi_ReqID = pmcxi->dwReqID;
|
|
pModemInfo->mi_pmcxo = pmcxi->pMcxOut;
|
|
|
|
pModemInfo->mi_RcvState = FLUSH_WRITE_QUEUE;
|
|
|
|
// Initialize receive state machine
|
|
//
|
|
MCXPRINTF("State <- Hanging up non-cmd");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_NON_CMD;
|
|
dwRet = MODEM_PENDING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// nothing to do for null-modems
|
|
//
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
dwRet = MODEM_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case STATE_WAIT_FOR_RLSD:
|
|
case STATE_CONNECTED:
|
|
//
|
|
// Drop the DTR line
|
|
//
|
|
MCXPRINTF("lowering DTR");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_HARDWAREHANGUP);
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, CLRDTR);
|
|
|
|
pModemInfo->mi_ReqID = pmcxi->dwReqID;
|
|
pModemInfo->mi_pmcxo = pmcxi->pMcxOut;
|
|
|
|
MCXPRINTF("State <- Hanging up dtr");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_DTR;
|
|
|
|
// Initialize receive state machine
|
|
//
|
|
pModemInfo->mi_RcvState = SET_TIMEOUT;
|
|
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
|
|
dwRet = MODEM_PENDING;
|
|
break;
|
|
|
|
case STATE_INITIALIZING:
|
|
case STATE_MONITORING:
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
dwRet = MODEM_SUCCESS;
|
|
break;
|
|
|
|
case STATE_UNKNOWN:
|
|
case STATE_DISCONNECTED:
|
|
// no need to change state
|
|
//
|
|
dwRet = MODEM_SUCCESS;
|
|
break;
|
|
|
|
case STATE_REMOTE_DROPPED:
|
|
|
|
pModemInfo->mi_ReqID = pmcxi->dwReqID;
|
|
pModemInfo->mi_pmcxo = pmcxi->pMcxOut;
|
|
|
|
pModemInfo->mi_ModemState=STATE_HANGING_UP_REMOTE;
|
|
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
|
|
dwRet = MODEM_PENDING;
|
|
|
|
break;
|
|
|
|
// case STATE_HANGING_UP_REMOTE:
|
|
// case STATE_HANGING_UP_DTR:
|
|
// case STATE_HANGING_UP_NON_CMD:
|
|
// case STATE_HANGING_UP_CMD:
|
|
default:
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("being hung up at a non-standard time. Why??? State <- Unknown");
|
|
dwRet = MODEM_SUCCESS;
|
|
break;
|
|
} // switch
|
|
|
|
ClearCommError(pModemInfo->mi_PortHandle, &ulError, NULL);
|
|
|
|
if (MODEM_FAILURE == dwRet)
|
|
{
|
|
MCXPRINTF("hangup failed.");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGERR_FAILED_HANGUP);
|
|
}
|
|
return dwRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXGetCommConfig (HANDLE, LPCOMMCONFIG, LPDWORD)
|
|
//
|
|
// Function: Gets modem config
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXGetCommConfig (HANDLE hModem, LPCOMMCONFIG lpCommConfig, LPDWORD lpcb)
|
|
{
|
|
return GetCommConfig(((PMODEMINFORMATION)hModem)->mi_PortHandle,
|
|
lpCommConfig,
|
|
lpcb)
|
|
? MODEM_SUCCESS
|
|
: MODEM_FAILURE;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXSetCommConfig (HANDLE, LPCOMMCONFIG, DWORD)
|
|
//
|
|
// Function: Sets modem config
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXSetCommConfig (HANDLE hModem, LPCOMMCONFIG lpCommConfig, DWORD cb)
|
|
{
|
|
PMODEMINFORMATION pModemInfo = (PMODEMINFORMATION)hModem;
|
|
BOOL fRet;
|
|
|
|
|
|
LPMODEMSETTINGS lpMS;
|
|
|
|
lpMS = (LPMODEMSETTINGS)((LPBYTE)lpCommConfig
|
|
+ lpCommConfig->dwProviderOffset);
|
|
|
|
|
|
if (lpMS->dwPreferredModemOptions & MDM_FLOWCONTROL_HARD) {
|
|
|
|
DPRINTF("McxSetCommConfig: enabling rts/cts control in DCB.");
|
|
|
|
lpCommConfig->dcb.fOutxCtsFlow=1;
|
|
lpCommConfig->dcb.fRtsControl=RTS_CONTROL_HANDSHAKE;
|
|
|
|
lpCommConfig->dcb.fOutX=FALSE;
|
|
lpCommConfig->dcb.fInX=FALSE;
|
|
|
|
} else {
|
|
|
|
lpCommConfig->dcb.fOutxCtsFlow=0;
|
|
lpCommConfig->dcb.fRtsControl=RTS_CONTROL_ENABLE;
|
|
}
|
|
|
|
|
|
|
|
|
|
fRet = SetCommConfig(pModemInfo->mi_PortHandle,
|
|
lpCommConfig,
|
|
cb);
|
|
|
|
if (fRet == TRUE)
|
|
{
|
|
PrintCommSettings(pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
&lpCommConfig->dcb);
|
|
|
|
// Set DTR so modems will talk to us.
|
|
//
|
|
if (EscapeCommFunction(pModemInfo->mi_PortHandle, SETDTR) == FALSE)
|
|
{
|
|
DPRINTF("EscapeCommFunction SETDTR in SetCommConfig failed.");
|
|
return MODEM_FAILURE;
|
|
}
|
|
|
|
// Set RTS initially to high, so modems will talk to us. This is important
|
|
// even if we aren't doing HW flow control. (ex. USR Sportster 14,400,
|
|
// Microcom Desporte FAST)
|
|
// Of course, it won't help the case where we have one of these modems and
|
|
// only 3 wires.
|
|
//
|
|
if (EscapeCommFunction(pModemInfo->mi_PortHandle, SETRTS) == FALSE)
|
|
{
|
|
DPRINTF("EscapeCommFunction SETRTS in SetCommConfig failed.");
|
|
//
|
|
// BUGBUG: this call fails on the digiboard, preventing the lower code
|
|
// BUGBUG: from running.
|
|
// BUGBUG: nothing that calls this function bothers to check the return
|
|
// BUGBUG: code anyway
|
|
//
|
|
//return MODEM_FAILURE;
|
|
}
|
|
|
|
// Synchronize with modem.sys
|
|
SynchronizeCommConfigSettings(pModemInfo,
|
|
FALSE);
|
|
}
|
|
|
|
return fRet ? MODEM_SUCCESS : MODEM_FAILURE;
|
|
}
|
|
|
|
|
|
LONG WINAPI
|
|
MCXSetModemSettings(
|
|
HANDLE hModem,
|
|
PMODEMSETTINGS lpMS
|
|
)
|
|
|
|
{
|
|
PMODEMINFORMATION pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
|
|
// Need to rebuild init string?
|
|
//
|
|
if (pModemInfo->mi_dwCallSetupFailTimerSetting != lpMS->dwCallSetupFailTimer
|
|
|| pModemInfo->mi_dwInactivityTimeoutSetting != lpMS->dwInactivityTimeout
|
|
|| pModemInfo->mi_dwSpeakerVolumeSetting != lpMS->dwSpeakerVolume
|
|
|| pModemInfo->mi_dwSpeakerModeSetting != lpMS->dwSpeakerMode
|
|
|| pModemInfo->mi_dwPreferredModemOptions != lpMS->dwPreferredModemOptions)
|
|
{
|
|
pModemInfo->mi_fSettingsInitStringsBuilt = FALSE;
|
|
}
|
|
|
|
pModemInfo->mi_dwCallSetupFailTimerSetting =
|
|
lpMS->dwCallSetupFailTimer;
|
|
|
|
pModemInfo->mi_dwInactivityTimeoutSetting =
|
|
lpMS->dwInactivityTimeout;
|
|
|
|
pModemInfo->mi_dwSpeakerVolumeSetting =
|
|
lpMS->dwSpeakerVolume;
|
|
|
|
pModemInfo->mi_dwSpeakerModeSetting =
|
|
lpMS->dwSpeakerMode;
|
|
|
|
pModemInfo->mi_dwPreferredModemOptions =
|
|
lpMS->dwPreferredModemOptions;
|
|
|
|
|
|
return MODEM_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//****************************************************************************
|
|
// LONG MCXSetPassthrough (HANDLE, DWORD)
|
|
//
|
|
// Function: Sets modem passthrough modes
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXSetPassthrough(HANDLE hModem, DWORD dwType)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
ULONG ulError;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("MCXSetPassthrough");
|
|
|
|
// Clear any outstanding timer callbacks
|
|
//
|
|
//;ClearReadTimer(pModemInfo);
|
|
|
|
// Determine Passthrough enabled/disabled/disabled and connected
|
|
//
|
|
switch(dwType)
|
|
{
|
|
case PASSTHROUGH_ON:
|
|
MCXPRINTF("Passthrough ON...");
|
|
|
|
CancelPendingIoAndPurgeCommBuffers(pModemInfo, TRUE);
|
|
|
|
// Put the port in the connected state
|
|
//
|
|
pModemInfo->mi_ModemState = STATE_CONNECTED;
|
|
|
|
ModemSetPassthrough(pModemInfo, MODEM_PASSTHROUGH);
|
|
break;
|
|
|
|
case PASSTHROUGH_OFF:
|
|
MCXPRINTF("Passthrough OFF...");
|
|
|
|
|
|
// Take the port out of connected state
|
|
//
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
|
|
ModemSetPassthrough(pModemInfo, MODEM_NOPASSTHROUGH);
|
|
break;
|
|
|
|
case PASSTHROUGH_OFF_BUT_CONNECTED:
|
|
MCXPRINTF("Passthrough OFF (but connected)...");
|
|
|
|
ModemSetPassthrough(pModemInfo, MODEM_DCDSNIFF);
|
|
break;
|
|
}
|
|
return MODEM_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXGetNegotiatedRate (HANDLE, LPDWORD)
|
|
//
|
|
// Function: Gets the modem connection speed
|
|
//
|
|
// Returns: MODEM_SUCCESS if success
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG MCXGetNegotiatedRate(HANDLE hModem, LPDWORD lpdwRate)
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
DWORD dwRet;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
if (STATE_CONNECTED == pModemInfo->mi_ModemState)
|
|
{
|
|
*lpdwRate = pModemInfo->mi_dwNegotiatedDCERate;
|
|
dwRet = MODEM_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
*lpdwRate = 0;
|
|
MCXPRINTF("GetNegotiatedRate failed.");
|
|
dwRet = MODEM_FAILURE;
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// LONG MCXRegisterDisconneectHandler(HANDLE, MCX_IN *)
|
|
//
|
|
// Function: Start monitoring remote disconnection
|
|
//
|
|
// Returns: MODEM_PENDING if start successfully
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG WINAPI
|
|
McxRegisterDisconectHandler(
|
|
HANDLE hModem,
|
|
DISCONNECT_HANDLER Handler,
|
|
HANDLE Context
|
|
)
|
|
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
DWORD dwRet;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("McxRegisterDisconnectHandler:");
|
|
|
|
pModemInfo->mi_DisconnectHandler=Handler;
|
|
pModemInfo->mi_DisconnectContext=Context;
|
|
|
|
dwRet = ModemWaitEvent(pModemInfo, EV_DSR | EV_RLSD, 0);
|
|
|
|
return dwRet;
|
|
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// LONG MCXDeregisterDisconneectHandler(HANDLE, MCX_IN *)
|
|
//
|
|
// Function: Stop monitoring remote disconnection
|
|
//
|
|
// Returns: MODEM_PENDING if start successfully
|
|
// MODEM_FAILURE if fails
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
LONG WINAPI
|
|
McxDeregisterDisconnectHandler(
|
|
HANDLE hModem
|
|
)
|
|
|
|
{
|
|
PMODEMINFORMATION pModemInfo;
|
|
|
|
pModemInfo = (PMODEMINFORMATION)hModem;
|
|
|
|
MCXPRINTF("McxDeregisterDisconnectHandler:");
|
|
|
|
pModemInfo->mi_DisconnectHandler=NULL;
|
|
pModemInfo->mi_DisconnectContext=NULL;
|
|
|
|
return MODEM_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// void MCXAsyncComplete (HANDLE, LPOVERLAPPED)
|
|
//
|
|
// Function: Completes an async operation
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************/
|
|
|
|
void MCXAsyncComplete (HANDLE hModem, LPOVERLAPPED lpOverlapped)
|
|
{
|
|
PMODEMINFORMATION pModemInfo = (PMODEMINFORMATION)hModem;
|
|
LPOVERNODE pNode = (LPOVERNODE)lpOverlapped;
|
|
|
|
ASSERT(lpOverlapped != NULL);
|
|
|
|
if (pNode->Type == OVERNODE_TYPE_READWRITE) {
|
|
//
|
|
// it's a read/write op
|
|
//
|
|
if (pNode->dwToken == pModemInfo->mi_dwRWIOExpected) {
|
|
//
|
|
// It the one we wanted
|
|
//
|
|
MCXPRINTF1("MCXAsyncComplete() handling RW i/o. # %d",pNode->dwToken);
|
|
|
|
pModemInfo->mi_dwRWIOExpected++;
|
|
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
|
|
} else {
|
|
|
|
MCXPRINTF1("MCXAsyncComplete() ignoring old RW i/o. # %d",pNode->dwToken);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pNode->Type == OVERNODE_TYPE_COMMEVENT) {
|
|
//
|
|
// WaitCommEvent completion
|
|
//
|
|
if (pNode->dwToken == pModemInfo->mi_dwEventIOExpected) {
|
|
|
|
MCXPRINTF2("MCXAsyncComplete() handling Event i/o, # %d, %08lx.",pNode->dwToken, pNode);
|
|
|
|
pModemInfo->mi_dwEventIOExpected++;
|
|
|
|
HWDetectionRoutine(pModemInfo, pNode);
|
|
|
|
} else {
|
|
|
|
MCXPRINTF2("MCXAsyncComplete() ignoring old Event i/o, # %d, %08lx.",pNode->dwToken, pNode);
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pNode->Type == OVERNODE_TYPE_WORKITEM) {
|
|
|
|
if (pNode->dwToken == pModemInfo->mi_dwDeferedExpected) {
|
|
|
|
pModemInfo->mi_dwDeferedExpected++;
|
|
|
|
MCXPRINTF1("MCXAsyncComplete() handling defered work item # %d.",pNode->dwToken);
|
|
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
|
|
} else {
|
|
|
|
MCXPRINTF1("MCXAsyncComplete() ignoring old defered work item # %d.",pNode->dwToken);
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// unknown io type
|
|
//
|
|
ASSERT(0);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// void ModemCallClient ()
|
|
//
|
|
// Function: Completes an async operation
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
void ModemCallClient (MODEMINFORMATION * pModemInfo, DWORD Param)
|
|
{
|
|
MCX_OUT *pMcxOut;
|
|
|
|
MCXPRINTF1("ModemCallClient: %d",Param);
|
|
|
|
// There must be an output buffer
|
|
//
|
|
if ((pMcxOut = pModemInfo->mi_pmcxo) != NULL)
|
|
{
|
|
pMcxOut->dwReqID = pModemInfo->mi_ReqID;
|
|
|
|
if (pModemInfo->mi_dwUnconditionalReturnValue == MODEM_NO_UNCONDITIONAL)
|
|
{
|
|
pMcxOut->dwResult= Param;
|
|
}
|
|
else
|
|
{
|
|
pMcxOut->dwResult= pModemInfo->mi_dwUnconditionalReturnValue;
|
|
};
|
|
pModemInfo->mi_dwUnconditionalReturnValue = MODEM_NO_UNCONDITIONAL;
|
|
|
|
PostQueuedCompletionStatus(ghCompletionPort,
|
|
CP_BYTES_WRITTEN(0),
|
|
pModemInfo->mi_dwCompletionKey,
|
|
NULL);
|
|
};
|
|
return;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// void ReadNotifyClient(MODEMINFORMATION *, DWORD)
|
|
//
|
|
// Function: The modem's state machine
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
void ReadNotifyClient(MODEMINFORMATION * pModemInfo, DWORD Param)
|
|
{
|
|
ULONG ulError;
|
|
|
|
pModemInfo->mi_RcvState = END_READ;
|
|
|
|
switch (pModemInfo->mi_ModemState)
|
|
{
|
|
case STATE_DIALING:
|
|
switch (Param)
|
|
{
|
|
case MODEM_SUCCESS:
|
|
pModemInfo->mi_ModemState = STATE_DIALED;
|
|
MCXPRINTF("State <- Dialed");
|
|
break;
|
|
|
|
case MODEM_BUSY:
|
|
case MODEM_NOANSWER:
|
|
case MODEM_NOCARRIER:
|
|
case MODEM_NODIALTONE:
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
break;
|
|
|
|
default:
|
|
Param = MODEM_FAILURE;
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
break;
|
|
} // switch (Param)
|
|
break;
|
|
|
|
case STATE_WAIT_FOR_RLSD:
|
|
//
|
|
// fall through
|
|
//
|
|
|
|
|
|
case STATE_ORIGINATING:
|
|
case STATE_ANSWERING:
|
|
switch (Param)
|
|
{
|
|
case MODEM_SUCCESS:
|
|
|
|
if (pModemInfo->mi_fModem && (pModemInfo->mi_ModemState != STATE_WAIT_FOR_RLSD)) {
|
|
//
|
|
// real mode and we are not already waiting
|
|
//
|
|
|
|
DWORD dwStat;
|
|
DWORD dwRet;
|
|
|
|
GetCommModemStatus(pModemInfo->mi_PortHandle, &dwStat);
|
|
|
|
//
|
|
// does it look like the modem is connected
|
|
//
|
|
if (!(dwStat & MS_RLSD_ON)) {
|
|
//
|
|
// got connect, but rlsd not high
|
|
//
|
|
MCXPRINTF("Got Connect, but rlsd is low, waiting");
|
|
|
|
pModemInfo->mi_ModemState=STATE_WAIT_FOR_RLSD;
|
|
|
|
dwRet=ModemWaitEvent(pModemInfo, EV_RLSD, pModemInfo->mi_dwWaitForCDTime);
|
|
|
|
if (dwRet == MODEM_PENDING) {
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
pModemInfo->mi_ModemState = STATE_CONNECTED;
|
|
MCXPRINTF("State <- Connected");
|
|
|
|
// do we need to adjust the dwNegotiatedDCERate?
|
|
//
|
|
if (pModemInfo->mi_dwNegotiatedDCERate == 0)
|
|
{
|
|
DCB Dcb;
|
|
|
|
// get the DCB
|
|
//
|
|
if (!GetCommState(pModemInfo->mi_PortHandle, &Dcb))
|
|
{
|
|
MCXPRINTF("was unable to get the comm state!");
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
Param = MODEM_FAILURE;
|
|
break;
|
|
}
|
|
|
|
// Did we have any DTE rate info reported
|
|
//
|
|
if (pModemInfo->mi_dwNegotiatedDTERate)
|
|
{
|
|
// Yes, use it.
|
|
//
|
|
pModemInfo->mi_dwNegotiatedDCERate = pModemInfo->mi_dwNegotiatedDTERate;
|
|
|
|
if (pModemInfo->mi_dwNegotiatedDTERate != Dcb.BaudRate)
|
|
{
|
|
// set DCB
|
|
//
|
|
MCXPRINTF("adjusting DTE to match reported DTE");
|
|
Dcb.BaudRate = pModemInfo->mi_dwNegotiatedDTERate;
|
|
PrintCommSettings(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,&Dcb);
|
|
if (!SetCommState(pModemInfo->mi_PortHandle, &Dcb))
|
|
{
|
|
MCXPRINTF("was unable to set the comm state!");
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
Param = MODEM_FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No, use the current DTE baud rate
|
|
//
|
|
MCXPRINTF("using current DTE");
|
|
pModemInfo->mi_dwNegotiatedDCERate = Dcb.BaudRate;
|
|
}
|
|
}
|
|
|
|
|
|
if (pModemInfo->mi_dwNegotiatedDCERate)
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_CONNECTEDBPS, pModemInfo->mi_dwNegotiatedDCERate);
|
|
}
|
|
else
|
|
{
|
|
if (pModemInfo->mi_dwNegotiatedDTERate)
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_CONNECTEDBPS, pModemInfo->mi_dwNegotiatedDTERate);
|
|
}
|
|
else
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_CONNECTED);
|
|
}
|
|
}
|
|
|
|
if (pModemInfo->mi_dwNegotiatedModemOptions & MDM_ERROR_CONTROL)
|
|
{
|
|
if (pModemInfo->mi_dwNegotiatedModemOptions & MDM_CELLULAR)
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_CELLULAR);
|
|
}
|
|
else
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_ERRORCONTROL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_UNKNOWNERRORCONTROL);
|
|
}
|
|
|
|
if (pModemInfo->mi_dwNegotiatedModemOptions & MDM_COMPRESSION)
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_COMPRESSION);
|
|
}
|
|
else
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_UNKNOWNCOMPRESSION);
|
|
}
|
|
|
|
MCXPRINTF1("Negotiated DCE is %d bits per second.", pModemInfo->mi_dwNegotiatedDCERate);
|
|
MCXPRINTF1("Error Correction is %s.",
|
|
pModemInfo->mi_dwNegotiatedModemOptions & MDM_ERROR_CONTROL ?
|
|
(pModemInfo->mi_dwNegotiatedModemOptions & MDM_CELLULAR ? "CELLULAR" : "ON") :
|
|
"OFF");
|
|
MCXPRINTF1("Data Compression is %s.",
|
|
pModemInfo->mi_dwNegotiatedModemOptions & MDM_COMPRESSION ? "ON" : "OFF");
|
|
|
|
if (pModemInfo->mi_hLogFile)
|
|
FlushLog(pModemInfo->mi_hLogFile);
|
|
|
|
// Send down the negotiated parts of MODEMSETTINGS struct to
|
|
// modem.sys.
|
|
SynchronizeCommConfigSettings(pModemInfo,
|
|
TRUE);
|
|
break;
|
|
|
|
case MODEM_BUSY:
|
|
case MODEM_NOANSWER:
|
|
case MODEM_NOCARRIER:
|
|
case MODEM_NODIALTONE:
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
break;
|
|
|
|
default:
|
|
Param = MODEM_FAILURE;
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
break;
|
|
} // switch (Param)
|
|
break;
|
|
|
|
case STATE_INITIALIZING:
|
|
switch (Param)
|
|
{
|
|
case MODEM_SUCCESS:
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
break;
|
|
|
|
case MODEM_BUSY:
|
|
case MODEM_NOANSWER:
|
|
case MODEM_NOCARRIER:
|
|
case MODEM_NODIALTONE:
|
|
if (pModemInfo->mi_dwCommandTryCount < MAX_COMMAND_TRIES)
|
|
{
|
|
pModemInfo->mi_dwCommandTryCount++;
|
|
|
|
MCXPRINTF("received an unanticipated response. Retrying previous command...");
|
|
pModemInfo->mi_pszCurCmd = pModemInfo->mi_pszPrevCmd;
|
|
|
|
switch(ModemWriteCommand(pModemInfo))
|
|
{
|
|
case MODEM_PENDING:
|
|
return;
|
|
|
|
case MODEM_SUCCESS:
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
break;
|
|
|
|
case MODEM_FAILURE:
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
break;
|
|
|
|
default:
|
|
MCXPRINTF("hit a default in ReadNotifyClient (STATE_INITIALIZING)! BAD!");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("gave up trying to do the command.");
|
|
}
|
|
// FALLTHROUGH...
|
|
|
|
default:
|
|
Param = MODEM_FAILURE;
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case STATE_HANGING_UP_REMOTE:
|
|
switch (Param)
|
|
{
|
|
case MODEM_SUCCESS:
|
|
case MODEM_BUSY:
|
|
case MODEM_NOANSWER:
|
|
case MODEM_NOCARRIER:
|
|
case MODEM_NODIALTONE:
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
break;
|
|
default:
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
break;
|
|
}
|
|
// Set HIWORD to MDM_ID_NULL to indicate this was an unexpected message
|
|
//
|
|
Param = MODEM_SUCCESS; // inform the app of the hangup, not matter what
|
|
break;
|
|
|
|
case STATE_HANGING_UP_DTR:
|
|
{
|
|
DWORD dwStat = 0;
|
|
|
|
// Did RLSD or DSR go down?
|
|
//
|
|
GetCommModemStatus(pModemInfo->mi_PortHandle, &dwStat);
|
|
if (dwStat & MS_RLSD_ON && dwStat & MS_DSR_ON)
|
|
{
|
|
// nope, RLSD/RSD are both high
|
|
//
|
|
if (pModemInfo->mi_fModem)
|
|
{
|
|
static char cszEscapeSequence[] = MODEM_ESCAPE_SEQUENCE;
|
|
DWORD dwTmp;
|
|
DWORD dwRet;
|
|
|
|
MCXPRINTF("DTR droppage failed to hangup modem. Trying '+++'");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGWARN_FAILEDDTRDROPPAGE);
|
|
|
|
// send "+++"
|
|
PrintString(pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
cszEscapeSequence,
|
|
MODEM_ESCAPE_SEQUENCE_LEN,
|
|
PS_SEND);
|
|
dwRet = ModemWrite(pModemInfo, cszEscapeSequence,
|
|
MODEM_ESCAPE_SEQUENCE_LEN, &dwTmp, TO_FLUSH);
|
|
if (dwRet == MODEM_FAILURE)
|
|
{
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("WriteComm Error. State <- Unknown");
|
|
Param = MODEM_FAILURE;
|
|
}
|
|
else
|
|
{
|
|
pModemInfo->mi_RcvState = FLUSH_WRITE_QUEUE;
|
|
|
|
// Initialize receive state machine
|
|
//
|
|
MCXPRINTF("State <- Hanging up non-cmd");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_NON_CMD;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// only try once to hangup a null-modem connection
|
|
MCXPRINTF("failed to hangup the null-modem connection!");
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
Param = MODEM_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// yep, RLSD and/or DSR went low
|
|
// Raise DTR line
|
|
//
|
|
MCXPRINTF("raising DTR");
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, SETDTR);
|
|
if (pModemInfo->mi_fModem)
|
|
{
|
|
// Initialize receive state machine
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
MCXPRINTF("State <- Hanging up non-cmd");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_NON_CMD;
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
Param = MODEM_SUCCESS;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case STATE_HANGING_UP_NON_CMD:
|
|
if (MODEM_FAILURE != Param)
|
|
{
|
|
// need to send "ATH<cr>"
|
|
MCXPRINTF("State <- Hanging up cmd");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_CMD;
|
|
|
|
// free the memory for non-hangup commands
|
|
if (pModemInfo->mi_pszzCmds)
|
|
{
|
|
if (pModemInfo->mi_pszzCmds != pModemInfo->mi_pszzHangupCmds)
|
|
{
|
|
LocalFree(pModemInfo->mi_pszzCmds);
|
|
}
|
|
pModemInfo->mi_pszzCmds = NULL;
|
|
}
|
|
Param = ModemCommand(pModemInfo,
|
|
pModemInfo->mi_ReqID, pModemInfo->mi_pmcxo,
|
|
pModemInfo->mi_pszzHangupCmds);
|
|
if (MODEM_PENDING == Param)
|
|
{
|
|
return;
|
|
}
|
|
// SUCCESS or FAILURE hits the below if statement
|
|
}
|
|
|
|
if (MODEM_SUCCESS == Param)
|
|
{
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
}
|
|
else
|
|
{
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
}
|
|
break;
|
|
|
|
case STATE_HANGING_UP_CMD:
|
|
if (MODEM_FAILURE != Param)
|
|
{
|
|
// Raise DTR line
|
|
//
|
|
MCXPRINTF("raising DTR");
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, SETDTR);
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
Param = MODEM_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
if (pModemInfo->mi_dwHangupTryCount < MAX_HANGUP_TRIES)
|
|
{
|
|
pModemInfo->mi_dwHangupTryCount++;
|
|
|
|
// Lower DTR line
|
|
//
|
|
MCXPRINTF("lowering DTR");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_HARDWAREHANGUP);
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, CLRDTR);
|
|
MCXPRINTF("State <- Hanging up dtr");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_DTR;
|
|
|
|
// Initialize receive state machine
|
|
//
|
|
pModemInfo->mi_RcvState = SET_TIMEOUT;
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("failed to hangup!");
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_MONITORING:
|
|
if (MODEM_SUCCESS == Param)
|
|
{
|
|
// do we monitor again?
|
|
//
|
|
if (pModemInfo->mi_fContinuousMonitoring)
|
|
{
|
|
MCXPRINTF("Monitoring again.");
|
|
|
|
//
|
|
// send up ring notification to the completion port
|
|
// they are handled differently because the BytesWritten field is non-zero
|
|
//
|
|
|
|
PostQueuedCompletionStatus(
|
|
ghCompletionPort,
|
|
CP_BYTES_WRITTEN(CP_TYPE_RING),
|
|
pModemInfo->mi_dwCompletionKey,
|
|
NULL
|
|
);
|
|
|
|
|
|
// Initialize receive state machine
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("not monitoring again.");
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Param = MODEM_FAILURE;
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("State <- Unknown");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Param = MODEM_FAILURE;
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("hit a default in ReadNotifyClient(). State <- Unknown");
|
|
break;
|
|
}
|
|
|
|
// do we have a post hangup command that we would like to do?
|
|
//
|
|
if (pModemInfo->mi_dwPostHangupModemState &&
|
|
pModemInfo->mi_pszzPostHangupCmds)
|
|
{
|
|
MCXPRINTF1("State <- %d (numeric because we are doing a post hangup command)", pModemInfo->mi_dwPostHangupModemState);
|
|
pModemInfo->mi_ModemState = pModemInfo->mi_dwPostHangupModemState;
|
|
|
|
pModemInfo->mi_pszzCmds = NULL;
|
|
|
|
Param = ModemCommand(pModemInfo,
|
|
pModemInfo->mi_ReqID, pModemInfo->mi_pmcxo,
|
|
pModemInfo->mi_pszzPostHangupCmds);
|
|
|
|
// don't clear mi_dwPostHangupModemState, because we use that to indicate if we are doing a post hangup command
|
|
pModemInfo->mi_pszzPostHangupCmds = NULL;
|
|
|
|
if (MODEM_PENDING == Param)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// make sure BOTH are clear
|
|
pModemInfo->mi_pszzPostHangupCmds = NULL;
|
|
pModemInfo->mi_dwPostHangupModemState = 0;
|
|
}
|
|
|
|
// Free the current command.
|
|
// Don't worry about affecting continuous monitoring; it will bail out
|
|
// earlier than this with a "return".
|
|
//
|
|
if (pModemInfo->mi_pszzCmds)
|
|
{
|
|
if (pModemInfo->mi_pszzCmds != pModemInfo->mi_pszzHangupCmds)
|
|
{
|
|
LocalFree(pModemInfo->mi_pszzCmds);
|
|
}
|
|
pModemInfo->mi_pszzCmds = NULL;
|
|
}
|
|
|
|
ClearCommError(pModemInfo->mi_PortHandle, &ulError, NULL);
|
|
|
|
if (pModemInfo->mi_ModemState == STATE_CONNECTED)
|
|
{
|
|
if (pModemInfo->mi_pszStartReadSpoof)
|
|
{
|
|
MCXPRINTF("spoofing remains:");
|
|
PrintString(pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
pModemInfo->mi_pszStartReadSpoof,
|
|
pModemInfo->mi_pszEndReadSpoof -
|
|
pModemInfo->mi_pszStartReadSpoof + 1,
|
|
PS_RECV);
|
|
}
|
|
|
|
ModemSetPassthrough(pModemInfo, MODEM_DCDSNIFF);
|
|
}
|
|
else
|
|
{
|
|
// If we didn't switch to data mode, then we still want to make sure that
|
|
// read callbacks are turned off until the next commad. We can get here
|
|
// because of a succesful init command or any unsuccesful command
|
|
//
|
|
ModemSetPassthrough(pModemInfo, MODEM_NOPASSTHROUGH);
|
|
}
|
|
|
|
ModemCallClient(pModemInfo, Param);
|
|
return;
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api DWORD | ModemCommand | This function sends a meta-command to the
|
|
modem. A meta-command is a command read from the registry
|
|
|
|
@parm MODEMINFORMATION * | pModemInfo | port handle
|
|
|
|
@parm DWORD | hWnd | The window handle to callback to when the command completes
|
|
|
|
@parm DWORD | msg | message
|
|
|
|
@parm DWORD | lParam | lparam
|
|
|
|
@parm char * | pszzCmdInMem | ptr to doubly-null terminated buffer of psz's.
|
|
|
|
@rdesc TRUE, PENDING or FALSE
|
|
|
|
******************************************************************************/
|
|
DWORD ModemCommand(MODEMINFORMATION * pModemInfo,
|
|
DWORD dwReqID,
|
|
MCX_OUT *pmcxo,
|
|
LPSTR pszzCmdInMem)
|
|
{
|
|
DWORD result;
|
|
ULONG ulError;
|
|
|
|
#ifdef VOICEVIEW
|
|
if (pModemInfo->VVInfo.wState == VVSTATE_NONE)
|
|
return MODEM_FAILURE;
|
|
#endif // VOICEVIEW
|
|
|
|
pModemInfo->mi_ReqID = dwReqID;
|
|
pModemInfo->mi_pmcxo = pmcxo;
|
|
|
|
// are we interrupting a command in progress?
|
|
//
|
|
if (pModemInfo->mi_pszzCmds)
|
|
{
|
|
MCXPRINTF("memory command interrupted. (ok, if monitoring)");
|
|
}
|
|
|
|
if (pszzCmdInMem)
|
|
{
|
|
pModemInfo->mi_pszzCmds = pszzCmdInMem;
|
|
pModemInfo->mi_pszCurCmd = pszzCmdInMem;
|
|
}
|
|
else
|
|
{
|
|
return MODEM_FAILURE;
|
|
}
|
|
|
|
// Flush the port
|
|
//
|
|
ModemSetPassthrough(pModemInfo, MODEM_NOPASSTHROUGH);
|
|
ClearCommError(pModemInfo->mi_PortHandle, &ulError, NULL);
|
|
|
|
// Sets CommMask to 0, waits for any I/O to complete and purges buffers.
|
|
CancelPendingIoAndPurgeCommBuffers(pModemInfo, TRUE);
|
|
|
|
// Raise DTR line
|
|
//
|
|
if (STATE_HANGING_UP_CMD != pModemInfo->mi_ModemState)
|
|
{
|
|
MCXPRINTF("raising DTR to make sure it is high");
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, SETDTR);
|
|
}
|
|
|
|
pModemInfo->mi_dwCommandTryCount = 1;
|
|
result = ModemWriteCommand(pModemInfo);
|
|
|
|
if (result != MODEM_PENDING)
|
|
{
|
|
if (pModemInfo->mi_pszzCmds)
|
|
{
|
|
if (pModemInfo->mi_pszzCmds != pModemInfo->mi_pszzHangupCmds)
|
|
{
|
|
LocalFree(pModemInfo->mi_pszzCmds);
|
|
}
|
|
pModemInfo->mi_pszzCmds = NULL;
|
|
}
|
|
}
|
|
|
|
return (result);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api DWORD | ModemWriteCommand | This function writes a modem command to the
|
|
modem.
|
|
|
|
@parm MODEMINFORMATION * | pModemInfo | port handle
|
|
|
|
@rdesc TRUE, PENDING or FALSE
|
|
|
|
******************************************************************************/
|
|
|
|
DWORD ModemWriteCommand(MODEMINFORMATION * pModemInfo)
|
|
{
|
|
BYTE szData[MAXSTRINGLENGTH];
|
|
static char cszNullCmd[] = "None";
|
|
DWORD dwRet = MODEM_SUCCESS;
|
|
|
|
|
|
// check to see if we are doing commands in memory, and if there are any left
|
|
//
|
|
if (pModemInfo->mi_pszzCmds && *pModemInfo->mi_pszCurCmd)
|
|
{
|
|
// set szData to the current string
|
|
//
|
|
lstrcpyA(szData, pModemInfo->mi_pszCurCmd);
|
|
pModemInfo->mi_cbCmd = lstrlenA(szData);
|
|
|
|
// save away a pointer to this current string
|
|
//
|
|
pModemInfo->mi_pszPrevCmd = pModemInfo->mi_pszCurCmd;
|
|
|
|
// point to the next string
|
|
//
|
|
pModemInfo->mi_pszCurCmd += pModemInfo->mi_cbCmd + 1;
|
|
}
|
|
else
|
|
{
|
|
return MODEM_SUCCESS; // not really success, but it means we don't have any commands
|
|
}
|
|
|
|
if (!ExpandMacros(szData, pModemInfo->mi_szCmd, &(pModemInfo->mi_cbCmd), NULL, 0))
|
|
{
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("ExpandMacro Error. State <- Unknown");
|
|
return MODEM_FAILURE;
|
|
}
|
|
|
|
// only send command if it is not "None"/Null
|
|
//
|
|
if (lstrcmpA(pModemInfo->mi_szCmd, cszNullCmd))
|
|
{
|
|
DWORD cbWrite;
|
|
|
|
// Don't log the actual digits of the phone number when dialing.
|
|
//
|
|
PrintString(pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
pModemInfo->mi_szCmd,
|
|
pModemInfo->mi_cbCmd,
|
|
(STATE_DIALING == pModemInfo->mi_ModemState ||
|
|
STATE_ORIGINATING == pModemInfo->mi_ModemState) ?
|
|
PS_SEND_SECURE : PS_SEND);
|
|
|
|
// Make sure there nothing in the receive queue
|
|
//
|
|
PurgeComm(pModemInfo->mi_PortHandle, PURGE_RXCLEAR);
|
|
|
|
// Send the command to the Port
|
|
//
|
|
dwRet = ModemWrite(pModemInfo, pModemInfo->mi_szCmd,
|
|
pModemInfo->mi_cbCmd, &cbWrite, TO_FLUSH);
|
|
|
|
if (dwRet == MODEM_FAILURE)
|
|
{
|
|
// There is a failure, bail now
|
|
//
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("WriteComm Error. State <- Unknown");
|
|
return MODEM_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("'None' command. Just waiting to read...");
|
|
|
|
}
|
|
|
|
// Initialize receive state machine
|
|
//
|
|
ASSERT(dwRet == MODEM_SUCCESS || dwRet == MODEM_PENDING);
|
|
if(dwRet == MODEM_SUCCESS) // ModemWrite can not return SUCCESS, so this
|
|
// must be from a "None"/Null command.
|
|
{
|
|
// Start receiving response immediately
|
|
//
|
|
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
|
|
if (!CreateDeferedWorkItem(pModemInfo)) {
|
|
|
|
pModemInfo->mi_ModemState = STATE_UNKNOWN;
|
|
MCXPRINTF("WriteComm Error. State <- Unknown");
|
|
return MODEM_FAILURE;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// Pending and come back to check the result from write operation
|
|
//
|
|
pModemInfo->mi_RcvState = FLUSH_WRITE_QUEUE;
|
|
};
|
|
|
|
return MODEM_PENDING;
|
|
}
|
|
|
|
|
|
|
|
BOOL WINAPI
|
|
SyncRead(
|
|
HANDLE CommHandle,
|
|
HANDLE Event,
|
|
LPVOID Buffer,
|
|
DWORD BufferLength,
|
|
LPDWORD BytesRead
|
|
)
|
|
|
|
{
|
|
OVERLAPPED OverLapped;
|
|
BOOL bResult;
|
|
COMMTIMEOUTS commtimeout;
|
|
|
|
commtimeout.ReadIntervalTimeout = MAXDWORD;
|
|
commtimeout.ReadTotalTimeoutMultiplier = 0;
|
|
commtimeout.ReadTotalTimeoutConstant = 0;
|
|
commtimeout.WriteTotalTimeoutMultiplier= 0;
|
|
commtimeout.WriteTotalTimeoutConstant = 0;
|
|
SetCommTimeouts(
|
|
CommHandle,
|
|
&commtimeout
|
|
);
|
|
|
|
*BytesRead=0;
|
|
|
|
// OR with 1 to prevent it from being posted to the completion port.
|
|
//
|
|
OverLapped.hEvent = (HANDLE)((DWORD)Event | 1);
|
|
|
|
bResult=ReadFile(
|
|
CommHandle,
|
|
Buffer,
|
|
BufferLength,
|
|
BytesRead,
|
|
&OverLapped
|
|
);
|
|
|
|
#ifdef DEBUG
|
|
if (!bResult) {
|
|
|
|
ASSERT(GetLastError() != ERROR_IO_PENDING);
|
|
}
|
|
#endif
|
|
|
|
|
|
return bResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
//****************************************************************************
|
|
// DWORD ReadComm(MODEMINFORMATION *, LPBYTE lpBuf, DWORD dwToRead,
|
|
// LPDWORD pdwRead, DWORD dwTimeout)
|
|
//
|
|
// Function: Check the spoof buffer and read from it if characters are
|
|
// available. Otherwise, call ModemRead.
|
|
//
|
|
// Returns: Spoof buffer: MODEM_SUCCESS or MODEM_FAILURE
|
|
// ModemRead : MODEM_PENDING or MODEM_FAILURE (never MODEM_SUCCESS)
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
DWORD ReadComm(MODEMINFORMATION *pModemInfo, LPBYTE lpBuf, DWORD dwToRead,
|
|
LPDWORD pdwRead, DWORD dwTimeout)
|
|
{
|
|
DWORD dwRet;
|
|
|
|
if (pModemInfo->mi_pszStartReadSpoof)
|
|
{
|
|
if (!pModemInfo->mi_pszEndReadSpoof)
|
|
{
|
|
MCXPRINTF("ACK! StartReadSpoof was real when EndReadSpoof was NULL!");
|
|
}
|
|
|
|
*pdwRead = 0;
|
|
|
|
while (dwToRead-- &&
|
|
pModemInfo->mi_pszStartReadSpoof <= pModemInfo->mi_pszEndReadSpoof)
|
|
{
|
|
// MCXPRINTF3("SPOOF mode! %8x %8x %8x", pModemInfo->mi_szResponse,
|
|
// pModemInfo->mi_pszStartReadSpoof,
|
|
// pModemInfo->mi_pszEndReadSpoof);
|
|
*lpBuf++ = *pModemInfo->mi_pszStartReadSpoof++;
|
|
(*pdwRead)++;
|
|
}
|
|
|
|
// have we run out of spoofing material?
|
|
if (pModemInfo->mi_pszStartReadSpoof > pModemInfo->mi_pszEndReadSpoof)
|
|
{
|
|
pModemInfo->mi_pszStartReadSpoof = NULL;
|
|
pModemInfo->mi_pszEndReadSpoof = NULL;
|
|
}
|
|
|
|
dwRet = MODEM_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
|
|
dwRet = MODEM_SUCCESS;
|
|
|
|
SyncRead(
|
|
pModemInfo->mi_PortHandle,
|
|
pModemInfo->mi_SyncReadEvent,
|
|
lpBuf,
|
|
dwToRead,
|
|
pdwRead
|
|
);
|
|
|
|
if (*pdwRead==0) {
|
|
|
|
dwRet = ModemRead(pModemInfo, lpBuf, dwToRead, pdwRead, dwTimeout);
|
|
|
|
if (MODEM_PENDING != dwRet)
|
|
{
|
|
// ReadComm failed
|
|
MCXPRINTF1("ReadComm error = %ld", dwRet);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// VOID ReadCompletionRoutine2(MODEMINFORMATION *)
|
|
//
|
|
// Function: Response Read State Machine and Timeout handler
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
VOID ReadCompletionRoutine2(MODEMINFORMATION * pModemInfo)
|
|
{
|
|
DWORD dwResult;
|
|
MSS Mss;
|
|
static char cszNoResponse[] = "NoResponse";
|
|
|
|
// A good ole state machine
|
|
while (TRUE)
|
|
{
|
|
switch (pModemInfo->mi_RcvState)
|
|
{
|
|
// ******************************************************************
|
|
//
|
|
// ModemWrite was pending and now completes or times out
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case FLUSH_WRITE_QUEUE:
|
|
{
|
|
DWORD cb;
|
|
|
|
// Assume success and we will start receiving the response
|
|
//
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
|
|
// Comm Errors?
|
|
HandleCommErrors(pModemInfo, 0);
|
|
|
|
// Has all of the write buffer been emptied?
|
|
//
|
|
if (ModemRWAsyncComplete(pModemInfo, &cb) != MODEM_SUCCESS)
|
|
{
|
|
MCXPRINTF1("Write error = %ld", GetLastError());
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// Start receiving a response
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case START_READ:
|
|
//
|
|
// Has all of the write buffer been emptied?
|
|
//
|
|
|
|
ASSERT(pModemInfo->mi_lpOverlappedRW == NULL);
|
|
|
|
// If we are doing commands, check for "NoResponse" on the
|
|
// next command and handle it.
|
|
//
|
|
if (pModemInfo->mi_pszzCmds &&
|
|
!lstrcmpA(pModemInfo->mi_pszCurCmd, cszNoResponse))
|
|
{
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
return;
|
|
}
|
|
|
|
|
|
// If we get here, it means that the write queue is empty
|
|
//
|
|
pModemInfo->mi_pszStartReadSpoof = NULL;
|
|
pModemInfo->mi_pszEndReadSpoof = NULL;
|
|
//
|
|
//*****************************************************************
|
|
// FALLTHROUGH
|
|
//*****************************************************************
|
|
//
|
|
|
|
// ******************************************************************
|
|
//
|
|
// Continue receiving the response
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case RESTART_READ:
|
|
pModemInfo->mi_cbTotalResponse = 0;
|
|
pModemInfo->mi_dwPossibleResponseLen = 0;
|
|
pModemInfo->mi_RcvState = TRY_READ;
|
|
pModemInfo->mi_fBadResponseCleanupMode = FALSE;
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// Try receiving the response now
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case TRY_READ:
|
|
{
|
|
DWORD dwTimeOut;
|
|
DWORD dwCharsReceived;
|
|
DWORD dwRet;
|
|
|
|
if (pModemInfo->mi_cbTotalResponse >= sizeof(pModemInfo->mi_szResponse))
|
|
{
|
|
MCXPRINTF("read in the maximum # of chars and still couldn't identify the response!");
|
|
pModemInfo->mi_RcvState = BAIL_O_RAMA_NO_MORE_DATA;
|
|
break;
|
|
}
|
|
|
|
// Determine the read timeout for the response
|
|
// Is this the first character? (either, at all, or after an echo)
|
|
//
|
|
if (pModemInfo->mi_cbTotalResponse == 0)
|
|
{
|
|
// set timeout for waiting for the first character
|
|
switch(pModemInfo->mi_ModemState)
|
|
{
|
|
case STATE_MONITORING:
|
|
// BUGBUG: (Chris Caputo - 1/22/96)
|
|
// BUGBUG: It seems that if we send some init commands for the
|
|
// BUGBUG: monitor (like ATS0=0), that we are screwed here
|
|
// BUGBUG: because the infinite timeout is not appropriate for
|
|
// BUGBUG: waiting to see if a command was accepted.
|
|
dwTimeOut = TO_INFINITE; // wait forever
|
|
break;
|
|
|
|
case STATE_INITIALIZING:
|
|
case STATE_HANGING_UP_NON_CMD:
|
|
case STATE_HANGING_UP_CMD:
|
|
case STATE_HANGING_UP_REMOTE:
|
|
dwTimeOut = TO_FIRST_CHAR_AFTER_INIT_CMD; // should be short (ie. 2 seconds)
|
|
break;
|
|
|
|
case STATE_DIALING:
|
|
case STATE_ANSWERING:
|
|
case STATE_ORIGINATING:
|
|
// do we support dwCallSetupFailTimer?
|
|
if (pModemInfo->mi_dwCallSetupFailTimerCap &&
|
|
pModemInfo->mi_dwCallSetupFailTimerSetting)
|
|
{
|
|
dwTimeOut = pModemInfo->mi_dwCallSetupFailTimerSetting
|
|
* MILISECONDS_PER_SECOND
|
|
+ TO_ADDITIONAL_TO_CALL_SETUP_FAIL_TIMER;
|
|
}
|
|
else
|
|
{
|
|
dwTimeOut = pModemInfo->mi_fModem ?
|
|
TO_FIRST_CHAR_AFTER_CONNECTION_CMD :
|
|
TO_FIRST_CHAR_AFTER_CONNECTION_CMD_NON_MODEM;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
MCXPRINTF("hit a default in SET_READ_CALLBACK!");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
}
|
|
|
|
MCXPRINTF2("waits for a new response for %d secs @%d.", dwTimeOut/1000,
|
|
GETTICKCOUNT());
|
|
}
|
|
else
|
|
{
|
|
dwTimeOut = TO_NEXT_CHAR_RCV_INTERVAL;
|
|
}
|
|
|
|
if (dwTimeOut)
|
|
{
|
|
dwTimeOut += GET_PORT_LATENCY(pModemInfo);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (GET_PORT_LATENCY(pModemInfo))
|
|
{
|
|
ASSERT(gRegistryFlags & fGRF_PORTLATENCY);
|
|
// DPRINTF2(
|
|
// "PL:%lu. New TO:%lu",
|
|
// GET_PORT_LATENCY(pModemInfo),
|
|
// dwTimeOut
|
|
// );
|
|
}
|
|
#endif
|
|
|
|
// Read the next character
|
|
//
|
|
dwRet = ReadComm(pModemInfo,
|
|
pModemInfo->mi_szResponse +
|
|
pModemInfo->mi_cbTotalResponse,
|
|
1, &dwCharsReceived, dwTimeOut);
|
|
switch (dwRet)
|
|
{
|
|
case MODEM_SUCCESS: // Only Spoof mode returns SUCCESS here.
|
|
pModemInfo->mi_cbTotalResponse += dwCharsReceived;
|
|
pModemInfo->mi_RcvState = CHECK_RESPONSE;
|
|
break;
|
|
|
|
case MODEM_PENDING:
|
|
pModemInfo->mi_RcvState = POST_READ_CALLBACK;
|
|
return;
|
|
|
|
default:
|
|
MCXPRINTF("had errors while trying to read from the port.");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// ******************************************************************
|
|
//
|
|
// The pending read completes successfully or fails
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case POST_READ_CALLBACK:
|
|
{
|
|
DWORD dwCharsReceived;
|
|
DWORD dwRet;
|
|
|
|
// Comm Errors?
|
|
HandleCommErrors(pModemInfo, 0);
|
|
|
|
// Has all of the write buffer been emptied?
|
|
dwRet = ModemRWAsyncComplete(pModemInfo, &dwCharsReceived);
|
|
|
|
switch (dwRet)
|
|
{
|
|
case MODEM_SUCCESS:
|
|
pModemInfo->mi_cbTotalResponse += dwCharsReceived;
|
|
pModemInfo->mi_RcvState = CHECK_RESPONSE;
|
|
break;
|
|
|
|
case MODEM_PENDING:
|
|
// Timeout on read
|
|
// did we have a possible response?
|
|
//
|
|
if (pModemInfo->mi_dwPossibleResponseLen)
|
|
{
|
|
MCXPRINTF("SUCCESS (Timeout)");
|
|
pModemInfo->mi_RcvState = USE_POSSIBLE_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
if (!pModemInfo->mi_fBadResponseCleanupMode)
|
|
{
|
|
MCXPRINTF1("timeout expired while waiting for a response @%d.",
|
|
GETTICKCOUNT());
|
|
}
|
|
|
|
// timeouts while in non-cmd stage of hanging up will be followed
|
|
// by a command hangup (ATH) started in ReadNotifyClient.
|
|
//
|
|
if (STATE_HANGING_UP_NON_CMD == pModemInfo->mi_ModemState)
|
|
{
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
return;
|
|
}
|
|
|
|
pModemInfo->mi_RcvState = BAIL_O_RAMA_NO_MORE_DATA;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// Fail on read
|
|
//
|
|
MCXPRINTF("had errors while trying to read from the port.");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
};
|
|
break;
|
|
}
|
|
|
|
// ******************************************************************
|
|
//
|
|
// The read completes successfully, checks new response
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case CHECK_RESPONSE:
|
|
if (pModemInfo->mi_fBadResponseCleanupMode)
|
|
{
|
|
pModemInfo->mi_RcvState = TRY_READ;
|
|
break;
|
|
}
|
|
|
|
switch (CheckResponse(pModemInfo, &Mss))
|
|
{
|
|
case SUCCESS:
|
|
pModemInfo->mi_RcvState = USE_WHOLE_RESPONSE;
|
|
break;
|
|
|
|
case ECHO:
|
|
PrintString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,pModemInfo->mi_szResponse, pModemInfo->mi_cbTotalResponse, PS_RECV);
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
break;
|
|
|
|
case PARTIAL_RESPONSE:
|
|
pModemInfo->mi_RcvState = TRY_READ;
|
|
break;
|
|
|
|
case UNRECOGNIZED_RESPONSE:
|
|
if (pModemInfo->mi_dwPossibleResponseLen)
|
|
{
|
|
pModemInfo->mi_RcvState = USE_POSSIBLE_RESPONSE;
|
|
}
|
|
else
|
|
{
|
|
if (STATE_MONITORING == pModemInfo->mi_ModemState)
|
|
{
|
|
PrintString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,pModemInfo->mi_szResponse, pModemInfo->mi_cbTotalResponse, PS_RECV);
|
|
|
|
// BUGBUG possibly put some autobauding crap in here. For now, just
|
|
// BUGBUG continue waiting.
|
|
#ifdef DEBUG
|
|
// Probably have to listen at 115200 for this to resolve the
|
|
// ambiguities. Scrap the lower baud rates too.
|
|
switch (pModemInfo->mi_szResponse[0] & 0xff)
|
|
{
|
|
case 0x00:
|
|
MCXPRINTF("looks like client is at 300 or 1200 bps");
|
|
break;
|
|
case 0x80:
|
|
MCXPRINTF("looks like client is at 2400 bps");
|
|
break;
|
|
case 0xf8:
|
|
MCXPRINTF("looks like client is at 4800 bps");
|
|
break;
|
|
case 0x1e:
|
|
MCXPRINTF("looks like client is at 9600 bps");
|
|
break;
|
|
case 0xf1:
|
|
case 0xf9:
|
|
case 0xfe:
|
|
MCXPRINTF("looks like client is at 38400 bps");
|
|
break;
|
|
case 0xff:
|
|
MCXPRINTF("looks like client is at 57600 or 115200 bps");
|
|
break;
|
|
}
|
|
#endif // DEBUG
|
|
|
|
// ignore the error and keep monitoring
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("unrecognized response! (inf file bad?) Reading rest of input...");
|
|
pModemInfo->mi_RcvState = BAIL_O_RAMA_MORE_DATA;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case POSSIBLE_RESPONSE:
|
|
pModemInfo->mi_RcvState = TRY_READ;
|
|
CopyMemory(&pModemInfo->mi_mssPossible, &Mss, sizeof(MSS));
|
|
|
|
// store the length of this possible response
|
|
pModemInfo->mi_dwPossibleResponseLen = pModemInfo->mi_cbTotalResponse;
|
|
break;
|
|
|
|
default:
|
|
MCXPRINTF("hit a default in CHECK_RESPONSE!");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// We got a bad response from modem
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case BAD_RESPONSE_CLEANUP_END:
|
|
// were we trying to connect? if yes, then check RLSD
|
|
if (pModemInfo->mi_fModem)
|
|
{
|
|
if (pModemInfo->mi_ModemState == STATE_ANSWERING ||
|
|
pModemInfo->mi_ModemState == STATE_ORIGINATING)
|
|
{
|
|
DWORD dwStat = 0;
|
|
|
|
// Is RLSD on?
|
|
//
|
|
GetCommModemStatus(pModemInfo->mi_PortHandle, &dwStat);
|
|
if (dwStat & MS_RLSD_ON)
|
|
{
|
|
MCXPRINTF("couldn't recognize connection response, but it did detect a successful connection.");
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("couldn't recognize connection response, and it appears that the connection failed.");
|
|
|
|
// Drop the DTR line
|
|
MCXPRINTF("lowering DTR");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_HARDWAREHANGUP);
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, CLRDTR);
|
|
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (pModemInfo->mi_ModemState == STATE_DIALING) // check for DIALING
|
|
{
|
|
MCXPRINTF("couldn't recognize response to a dial command and since we are dialing, we can't retry, nor continue. Hanging up...");
|
|
pModemInfo->mi_dwUnconditionalReturnValue = MODEM_FAILURE; // make sure the Dialing command gets a failure return
|
|
|
|
// need to send "ATH<cr>"
|
|
MCXPRINTF("State <- Hanging up cmd");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_CMD;
|
|
|
|
// Reset hangup counter to 0.
|
|
pModemInfo->mi_dwHangupTryCount = 1;
|
|
|
|
// free the memory for non-hangup commands
|
|
if (pModemInfo->mi_pszzCmds)
|
|
{
|
|
if (pModemInfo->mi_pszzCmds != pModemInfo->mi_pszzHangupCmds)
|
|
{
|
|
LocalFree(pModemInfo->mi_pszzCmds);
|
|
}
|
|
pModemInfo->mi_pszzCmds = NULL;
|
|
}
|
|
dwResult = ModemCommand(pModemInfo,
|
|
pModemInfo->mi_ReqID, pModemInfo->mi_pmcxo,
|
|
pModemInfo->mi_pszzHangupCmds);
|
|
if (dwResult != MODEM_PENDING)
|
|
{
|
|
ReadNotifyClient(pModemInfo, dwResult);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// still no luck... then adjust speed after trying at this speed 2 times
|
|
// BUGBUG - adjust speed
|
|
|
|
// and try the command again (if there was one)
|
|
if (pModemInfo->mi_pszzCmds)
|
|
{
|
|
MCXPRINTF("retrying last command...");
|
|
pModemInfo->mi_pszCurCmd = pModemInfo->mi_pszPrevCmd;
|
|
dwResult = ModemWriteCommand(pModemInfo);
|
|
if (dwResult != MODEM_PENDING)
|
|
{
|
|
ReadNotifyClient(pModemInfo, dwResult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// I think we only get here if we failed to read a response for a hangup.
|
|
MCXPRINTF("failed to read a response for a hangup. Returning SUCCESS anyways.");
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
}
|
|
return;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// we get here if we had an unrecognized response
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case BAIL_O_RAMA_MORE_DATA:
|
|
// get the rest of the bad response
|
|
pModemInfo->mi_dwPossibleResponseLen = 0; // ditch any possible responses
|
|
pModemInfo->mi_fBadResponseCleanupMode = TRUE; // turn on cleanup mode
|
|
pModemInfo->mi_RcvState = TRY_READ;
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// we get here if a timeout has occured and no data was available or
|
|
// if we read too much data also after cleaning up an unrecognized
|
|
// response
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case BAIL_O_RAMA_NO_MORE_DATA:
|
|
switch (pModemInfo->mi_ModemState)
|
|
{
|
|
case STATE_INITIALIZING:
|
|
{
|
|
DWORD dwStat = 0;
|
|
|
|
// Check modem control signals.
|
|
//
|
|
GetCommModemStatus(pModemInfo->mi_PortHandle, &dwStat);
|
|
|
|
// does it look like the modem is still online from a previous call?
|
|
if (dwStat & MS_RLSD_ON)
|
|
{
|
|
MCXPRINTF("It appears that a previous connection was not hung up! Attempting hangup...");
|
|
|
|
// are we already in the midst of a post hangup command?
|
|
if (pModemInfo->mi_dwPostHangupModemState)
|
|
{
|
|
MCXPRINTF("won't do a second hangup attempt of the existing call. Bail!");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGWARN_PREVIOUSCONNECTIONNOTHUNGUP);
|
|
|
|
// save state
|
|
pModemInfo->mi_dwPostHangupModemState = STATE_INITIALIZING;
|
|
pModemInfo->mi_pszzPostHangupCmds = pModemInfo->mi_pszzCmds;
|
|
pModemInfo->mi_pszzCmds = NULL;
|
|
|
|
// do hangup
|
|
// Reset hangup counter to 1.
|
|
pModemInfo->mi_dwHangupTryCount = 1;
|
|
|
|
// Drop the DTR line
|
|
MCXPRINTF("lowering DTR");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_HARDWAREHANGUP);
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, CLRDTR);
|
|
MCXPRINTF("State <- Hanging up dtr");
|
|
pModemInfo->mi_ModemState = STATE_HANGING_UP_DTR;
|
|
|
|
// Initialize receive state machine
|
|
pModemInfo->mi_RcvState = SET_TIMEOUT;
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
// FALLTHROUGH
|
|
|
|
default:
|
|
// Print out what we were able to get
|
|
MCXPRINTF("data from failed or unrecognized response:");
|
|
if (pModemInfo->mi_dwCommandTryCount < MAX_COMMAND_TRIES)
|
|
{
|
|
PrintString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,pModemInfo->mi_szResponse, pModemInfo->mi_cbTotalResponse, PS_RECV);
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGWARN_UNRECOGNIZEDRESPONSE);
|
|
pModemInfo->mi_dwCommandTryCount++;
|
|
pModemInfo->mi_RcvState = BAD_RESPONSE_CLEANUP_END;
|
|
}
|
|
else
|
|
{
|
|
PrintString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,pModemInfo->mi_szResponse, pModemInfo->mi_cbTotalResponse, PS_RECV);
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGERR_FAILED_RESPONSE);
|
|
MCXPRINTF("gave up trying to do the command.");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// The entire modem response is good.
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case USE_WHOLE_RESPONSE:
|
|
PrintString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,pModemInfo->mi_szResponse, pModemInfo->mi_cbTotalResponse, PS_RECV);
|
|
pModemInfo->mi_RcvState = GOOD_RESPONSE;
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// Some part of the response matches one of the possible responses
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case USE_POSSIBLE_RESPONSE:
|
|
|
|
// use the stored MSS
|
|
CopyMemory(&Mss, &pModemInfo->mi_mssPossible, sizeof(MSS));
|
|
|
|
// setup up the ReadComm spoofing mechanism if it isn't already active
|
|
if (!pModemInfo->mi_pszStartReadSpoof)
|
|
{
|
|
pModemInfo->mi_pszStartReadSpoof = pModemInfo->mi_szResponse + pModemInfo->mi_dwPossibleResponseLen;
|
|
pModemInfo->mi_pszEndReadSpoof = pModemInfo->mi_szResponse + pModemInfo->mi_cbTotalResponse - 1;
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("USE_POSSIBLE_RESPONSE while in Spoof mode!");
|
|
}
|
|
|
|
PrintString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID,pModemInfo->mi_szResponse, pModemInfo->mi_dwPossibleResponseLen, PS_RECV);
|
|
pModemInfo->mi_RcvState = GOOD_RESPONSE;
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// We have a good modem response
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case GOOD_RESPONSE:
|
|
|
|
|
|
PrintGoodResponse(
|
|
pModemInfo->mi_hLogFile,
|
|
pModemInfo->mi_dwID,
|
|
Mss.bResponseState
|
|
);
|
|
|
|
// negotiated modem options... only allow compression and error correction results
|
|
pModemInfo->mi_dwNegotiatedModemOptions |= (Mss.bNegotiatedOptions &
|
|
(MDM_COMPRESSION |
|
|
MDM_ERROR_CONTROL |
|
|
MDM_CELLULAR));
|
|
|
|
// check for DCE and DTE info
|
|
if (Mss.dwNegotiatedDCERate)
|
|
pModemInfo->mi_dwNegotiatedDCERate = Mss.dwNegotiatedDCERate;
|
|
|
|
if (Mss.dwNegotiatedDTERate)
|
|
pModemInfo->mi_dwNegotiatedDTERate = Mss.dwNegotiatedDTERate;
|
|
|
|
// BUGBUG: Consolidate ReadNotifyClients
|
|
switch (Mss.bResponseState)
|
|
{
|
|
case RESPONSE_OK: // more commands
|
|
if (STATE_HANGING_UP_NON_CMD == pModemInfo->mi_ModemState)
|
|
{
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
return;
|
|
}
|
|
// fallthrough
|
|
|
|
case RESPONSE_CONNECT:
|
|
pModemInfo->mi_dwCommandTryCount = 1;
|
|
dwResult = ModemWriteCommand(pModemInfo);
|
|
if (dwResult != MODEM_PENDING)
|
|
{
|
|
ReadNotifyClient(pModemInfo, dwResult);
|
|
}
|
|
|
|
if (STATE_MONITORING == pModemInfo->mi_ModemState) {
|
|
//
|
|
// monitoring, wait for DSR changes
|
|
//
|
|
if (pModemInfo->mi_fModem) {
|
|
//
|
|
// only if we are a real modem
|
|
//
|
|
if (!CurrentlyWaitingForCommEvent(pModemInfo)) {
|
|
//
|
|
// and if we ain't already waiting, ie. more that on monitor command
|
|
//
|
|
ModemWaitEvent(pModemInfo, EV_DSR , 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef VOICEVIEW
|
|
if (STATE_MONITORING == pModemInfo->mi_ModemState)
|
|
{
|
|
if (VVCLASS_80 == pModemInfo->VVInfo.wClass)
|
|
{
|
|
// modem is now in fclass+80, notify VxD ddi
|
|
MCXPRINTF("VVR_LINE_BACK");
|
|
VVCallBackFunc( pModemInfo, VVR_LINE_BACK ); // tell VV CAN use port
|
|
}
|
|
|
|
if (pModemInfo->VVInfo.hSemaphore)
|
|
{
|
|
// unblock the waiting command
|
|
MCXPRINTF("received OK - signaled semaphore");
|
|
Signal_Semaphore( pModemInfo->VVInfo.hSemaphore );
|
|
}
|
|
}
|
|
#endif // VOICEVIEW
|
|
return;
|
|
|
|
#ifdef VOICEVIEW
|
|
case RESPONSE_VV_SSV:
|
|
// got a DATA VoiceView response from the modem
|
|
// call VoiceView ddi VxD and let him know
|
|
MCXPRINTF("VOICEVIEW DATA");
|
|
VVCallBackFunc( pModemInfo, (WORD)(Mss.bResponseState - RESPONSE_VV_BASE) );
|
|
return;
|
|
|
|
case RESPONSE_VV_SMD:
|
|
case RESPONSE_VV_SFA:
|
|
case RESPONSE_VV_SRA:
|
|
case RESPONSE_VV_SRQ:
|
|
case RESPONSE_VV_SRC:
|
|
case RESPONSE_VV_STO:
|
|
case RESPONSE_VV_SVM:
|
|
// got a VoiceView response from the modem
|
|
// call VoiceView ddi VxD and let him know
|
|
MCXPRINTF("RESPONSE_VOICEVIEW");
|
|
VVCallBackFunc( pModemInfo, (WORD)(Mss.bResponseState - RESPONSE_VV_BASE) );
|
|
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
return;
|
|
#endif // VOICEVIEW
|
|
|
|
case RESPONSE_RING:
|
|
// try to resend the previous command if we aren't connecting or monitoring
|
|
if (!(pModemInfo->mi_ModemState == STATE_ANSWERING ||
|
|
pModemInfo->mi_ModemState == STATE_ORIGINATING) &&
|
|
pModemInfo->mi_pszzCmds)
|
|
{
|
|
if (STATE_MONITORING != pModemInfo->mi_ModemState)
|
|
{
|
|
// We got a RING while we were trying to do a command unrelated to
|
|
// monitoring. Retry the command...
|
|
MCXPRINTF("retrying last command due to unexpected RESPONSE_RING");
|
|
pModemInfo->mi_pszCurCmd = pModemInfo->mi_pszPrevCmd;
|
|
}
|
|
|
|
pModemInfo->mi_dwCommandTryCount = 1;
|
|
dwResult = ModemWriteCommand(pModemInfo);
|
|
if (dwResult != MODEM_PENDING)
|
|
{
|
|
ReadNotifyClient(pModemInfo, dwResult);
|
|
}
|
|
return;
|
|
}
|
|
// fall through (when we are doing a connection command of some type)
|
|
|
|
case RESPONSE_LOOP: // more responses
|
|
pModemInfo->mi_RcvState = RESTART_READ;
|
|
break;
|
|
|
|
case RESPONSE_BUSY:
|
|
ReadNotifyClient(pModemInfo, MODEM_BUSY);
|
|
return;
|
|
|
|
case RESPONSE_NOCARRIER:
|
|
ReadNotifyClient(pModemInfo, MODEM_NOCARRIER);
|
|
return;
|
|
|
|
case RESPONSE_NODIALTONE:
|
|
ReadNotifyClient(pModemInfo, MODEM_NODIALTONE);
|
|
return;
|
|
|
|
case RESPONSE_NOANSWER:
|
|
ReadNotifyClient(pModemInfo, MODEM_NOANSWER);
|
|
return;
|
|
|
|
case RESPONSE_ERROR:
|
|
// If we get an ERROR while HANGING_UP_NON_CMD, that is okay.
|
|
// During any other states, it is bad.
|
|
if (STATE_HANGING_UP_NON_CMD == pModemInfo->mi_ModemState)
|
|
{
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
}
|
|
#ifdef VOICEVIEW
|
|
if (STATE_MONITORING == pModemInfo->mi_ModemState)
|
|
{
|
|
if (VVCLASS_80 == pModemInfo->VVInfo.wClass)
|
|
{
|
|
// modem could not get into fclass+80, notify VxD ddi
|
|
MCXPRINTF("VVRS_NO_CLS80_SUPPORT");
|
|
VVCallBackFunc( pModemInfo, VVRS_NO_CLS80_SUPPORT ); // tell VV CAN'T work
|
|
VVSetClass( pModemInfo, VVCLASS_0 ); // make sure we are in fclass=0 now
|
|
}
|
|
|
|
if (pModemInfo->VVInfo.hSemaphore)
|
|
{
|
|
// unblock the waiting command
|
|
MCXPRINTF("Received ERROR - signaled semaphore");
|
|
Signal_Semaphore( pModemInfo->VVInfo.hSemaphore );
|
|
}
|
|
}
|
|
#endif // VOICEVIEW
|
|
return;
|
|
|
|
default:
|
|
MCXPRINTF("hit a default!");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
} /* Switch on dwResponseState */
|
|
break;
|
|
|
|
// ******************************************************************
|
|
//
|
|
// End of recieving response phase
|
|
//
|
|
// ******************************************************************
|
|
//
|
|
case END_READ:
|
|
MCXPRINTF("had an extra callback caught by END_READ!");
|
|
ASSERT(0);
|
|
return;
|
|
|
|
// ******************************************************************
|
|
case SET_TIMEOUT:
|
|
{
|
|
DWORD dwTimeOut;
|
|
|
|
// set timeout based on current modem state
|
|
switch (pModemInfo->mi_ModemState)
|
|
{
|
|
case STATE_HANGING_UP_DTR:
|
|
// Enable a few events that we would like to hear about
|
|
//
|
|
dwTimeOut = TO_DTR_DROP * pModemInfo->mi_dwHangupTryCount; // wait longer on successive hangup tries
|
|
|
|
switch (ModemWaitEvent(pModemInfo, EV_DSR | EV_RLSD, dwTimeOut))
|
|
{
|
|
case MODEM_SUCCESS:
|
|
case MODEM_PENDING:
|
|
pModemInfo->mi_RcvState = POST_TIMEOUT;
|
|
break;
|
|
|
|
default:
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
MCXPRINTF1("in state SET_TIMEOUT for a state that doesn't make sense (%d)",
|
|
pModemInfo->mi_ModemState);
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// ******************************************************************
|
|
case POST_TIMEOUT:
|
|
|
|
if (pModemInfo->mi_ModemState != STATE_HANGING_UP_DTR)
|
|
{
|
|
MCXPRINTF("hit POST_TIMEOUT when it shouldn't have.");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("POST_TIMEOUT");
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
}
|
|
return;
|
|
|
|
// ******************************************************************
|
|
default:
|
|
MCXPRINTF("hit a default in ReadCompletionRoutine2!");
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
return;
|
|
} /* Switch on RcvState */
|
|
} /* While */
|
|
}
|
|
|
|
//****************************************************************************
|
|
// VOID HWDetectionRoutine(MODEMINFORMATION *)
|
|
//
|
|
// Function: Called when:
|
|
// EV_RLSD, EV_DSR, or EV_ERR during STATE_CONNECTED (non-passthrough)
|
|
// EV_ERR during STATE_CONNECTED (passthrough)
|
|
// EV_RLSD or EV_DSR during STATE_HANGING_UP_DTR
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
VOID WINAPI
|
|
HWDetectionRoutine(
|
|
MODEMINFORMATION * pModemInfo,
|
|
LPOVERNODE pNode
|
|
)
|
|
{
|
|
DWORD dwDummy;
|
|
BOOL fClearEventHandle = TRUE;
|
|
DWORD dwRet;
|
|
|
|
|
|
// are we listening for this event?
|
|
|
|
dwRet=ModemWaitEventComplete(
|
|
pModemInfo,
|
|
pNode
|
|
);
|
|
|
|
|
|
if (((dwRet) == MODEM_SUCCESS) || (dwRet == MODEM_PENDING)) {
|
|
|
|
|
|
switch (pModemInfo->mi_ModemState)
|
|
{
|
|
case STATE_HANGING_UP_DTR:
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
|
|
|
|
break;
|
|
|
|
case STATE_MONITORING:
|
|
|
|
MCXPRINTF("DSR drop while monitoring, monitor fails");
|
|
|
|
CancelPendingIoAndPurgeCommBuffers(pModemInfo, TRUE);
|
|
|
|
ReadNotifyClient(pModemInfo, MODEM_FAILURE);
|
|
|
|
break;
|
|
|
|
case STATE_WAIT_FOR_RLSD:
|
|
|
|
MCXPRINTF("RLSD went high, completing connection.");
|
|
|
|
ReadNotifyClient(pModemInfo, MODEM_SUCCESS);
|
|
|
|
break;
|
|
|
|
case STATE_CONNECTED:
|
|
|
|
MCXPRINTF("reporting dropped line.");
|
|
LogString(pModemInfo->mi_hLogFile,pModemInfo->mi_dwID, IDS_MSGLOG_REMOTEHANGUP);
|
|
|
|
|
|
ModemSetPassthrough(pModemInfo, MODEM_NOPASSTHROUGH);
|
|
|
|
// purge only the transmit queue. We want to receive the 3<cr> / NO CARRIER msg
|
|
PurgeComm(pModemInfo->mi_PortHandle, PURGE_TXCLEAR);
|
|
|
|
if (pModemInfo->mi_fModem)
|
|
{
|
|
MCXPRINTF("State <- Remote dropped");
|
|
pModemInfo->mi_ModemState = STATE_REMOTE_DROPPED;
|
|
#if 0
|
|
// Initialize receive state machine
|
|
pModemInfo->mi_RcvState = START_READ;
|
|
ReadCompletionRoutine2(pModemInfo);
|
|
fClearEventHandle = FALSE; // Same reason as above.
|
|
#endif
|
|
}
|
|
else // we don't need to wait for a response from a null-modem
|
|
{
|
|
// Drop the DTR line to let the other side know you acknowledge
|
|
MCXPRINTF("dropping DTR to acknowledge remote hangup");
|
|
EscapeCommFunction(pModemInfo->mi_PortHandle, CLRDTR);
|
|
pModemInfo->mi_ModemState = STATE_DISCONNECTED;
|
|
MCXPRINTF("State <- Disconnected");
|
|
|
|
#if 0
|
|
// Set HIWORD to MDM_ID_NULL to indicate this was an unexpected message
|
|
//
|
|
ModemCallClient(pModemInfo, MODEM_HANGUP); // inform the app of the hangup
|
|
#endif
|
|
}
|
|
|
|
if (NULL != pModemInfo->mi_DisconnectHandler) {
|
|
//
|
|
// the upper level code has register a disconnect hanlder
|
|
//
|
|
(*pModemInfo->mi_DisconnectHandler)(
|
|
pModemInfo->mi_DisconnectContext
|
|
);
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
MCXPRINTF("got a CN_EVENT when it didn't expect one!!! (bad)");
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF("HWDetectionRoutine called with an event it wasn't interested in.");
|
|
ASSERT(0);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api DWORD | CheckResponse | This function checks to see if the response is
|
|
an echo, and if it isn't it call MatchResponse().
|
|
|
|
@parm MODEMINFORMATION * | pModemInfo | Modem port handle
|
|
|
|
@parm MSS * | pMss | ptr to a buffer to copy the Modem State Structure data
|
|
into. Valid on SUCCES return.
|
|
|
|
@rdesc returns SUCCESS if there is 1 match and 0 partials.
|
|
Otherwise, a non-zero error code is returned:
|
|
|
|
@flag ECHO if the response was an echo of the command
|
|
|
|
@flag PARTIAL_RESPONSE if there no match and 1 or more partials.
|
|
|
|
@flag UNRECOGNIZED_RESPONSE if there are no matches or partials.
|
|
|
|
@flag POSSIBLE_RESPONSE if there is 1 match and 1 or more partials.
|
|
|
|
*****************************************************************************/
|
|
|
|
DWORD CheckResponse(MODEMINFORMATION * pModemInfo, MSS *pMss)
|
|
{
|
|
DWORD dwResult;
|
|
|
|
ASSERT (pModemInfo->mi_cbTotalResponse <= sizeof(pModemInfo->mi_szResponse));
|
|
|
|
if ((dwResult = MatchResponse(pModemInfo, pMss)) == UNRECOGNIZED_RESPONSE)
|
|
{
|
|
// Is it an echo so far?
|
|
//
|
|
if (!Mystrncmp(pModemInfo->mi_szCmd, pModemInfo->mi_szResponse,
|
|
pModemInfo->mi_cbTotalResponse))
|
|
{
|
|
// is it a complete echo?
|
|
//
|
|
if (pModemInfo->mi_cbCmd == pModemInfo->mi_cbTotalResponse)
|
|
{
|
|
dwResult = ECHO;
|
|
}
|
|
else
|
|
{
|
|
dwResult = PARTIAL_RESPONSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(dwResult);
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api DWORD | MatchResponse | Scans the responses linked-list for a response
|
|
keyword that matches the response.
|
|
|
|
@parm MODEMINFORMATION * | pModemInfo | The modem port handle.
|
|
|
|
@parm MSS * | pMss | ptr to a buffer to copy the Modem State Structure data
|
|
into. Valid on SUCCESS return.
|
|
|
|
@rdesc returns SUCCESS if there is 1 match and 0 partials.
|
|
Otherwise, a non-zero error code is returned:
|
|
|
|
@flag PARTIAL_RESPONSE if there no match and 1 or more partials.
|
|
|
|
@flag UNRECOGNIZED_RESPONSE if there are no matches or partials.
|
|
|
|
@flag POSSIBLE_RESPONSE if there is 1 match and 1 or more partials.
|
|
|
|
*****************************************************************************/
|
|
|
|
DWORD MatchResponse(MODEMINFORMATION * pModemInfo, MSS * pMss)
|
|
{
|
|
PRESPONSE_NODE pRN = pModemInfo->mi_prnResponseHead;
|
|
DWORD dwMatch = 0; // match is defined as strings being equal up to the length of the reference string
|
|
// strcmpn(incoming, reference, incoming_len) == 0 && incoming_len == reference_len
|
|
DWORD dwPartials = 0; // partial is defined as strings being equal up to the length of the incoming string
|
|
// strcmpn(incoming, reference, incoming_len) == 0 && incoming_len != reference_len
|
|
DWORD dwResponseLength = pModemInfo->mi_cbTotalResponse;
|
|
LPSTR pszResponse = pModemInfo->mi_szResponse;
|
|
|
|
while(pRN)
|
|
{
|
|
if (!strncmpi(pszResponse, pRN->szResponse, dwResponseLength))
|
|
{
|
|
// match or partial?
|
|
if (dwResponseLength == (DWORD)(pRN->bLen + 1)) // add 1 (range = 1..256)
|
|
{
|
|
dwMatch = 1;
|
|
CopyMemory(pMss, &pRN->Mss, sizeof(MSS));
|
|
}
|
|
else
|
|
{
|
|
dwPartials = 1;
|
|
}
|
|
}
|
|
pRN = pRN->pNext;
|
|
}
|
|
|
|
switch ((dwMatch << 1) + dwPartials)
|
|
{
|
|
case 0: // 00 - nothing
|
|
return UNRECOGNIZED_RESPONSE;
|
|
|
|
case 1: // 01 - partials
|
|
return PARTIAL_RESPONSE;
|
|
|
|
case 2: // 10 - match
|
|
return SUCCESS;
|
|
|
|
case 3: // 11 - match and partials
|
|
return POSSIBLE_RESPONSE;
|
|
|
|
default:
|
|
MCXPRINTF("hit a default in MatchResponse!");
|
|
return UNRECOGNIZED_RESPONSE;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api BOOL | ExpandMacros | Takes the string pszLine, and copies it to
|
|
lpszVal after expanding macros
|
|
|
|
@parm LPSTR | pszRegResponse | ptr to response string from registry.
|
|
|
|
@parm LPSTR | pszExpanded | ptr to buffer to copy string to w/ macros expanded
|
|
|
|
@parm LPDWORD | pdwValLen | length of pszVal w/ expanded macros.
|
|
|
|
@rdesc Returns FALSE if a needed macro translation could not be found in the
|
|
pMacroXlations table, TRUE otherwise.
|
|
|
|
*****************************************************************************/
|
|
|
|
// BUGBUG: this should be fixed to allow a max length to be passed in
|
|
//
|
|
BOOL ExpandMacros(LPSTR pszRegResponse,
|
|
LPSTR pszExpanded,
|
|
LPDWORD pdwValLen,
|
|
MODEMMACRO * pMdmMacro,
|
|
DWORD cbMacros)
|
|
{
|
|
LPSTR pszValue;
|
|
DWORD cbTmp;
|
|
BOOL bFound;
|
|
LPSTR pchTmp;
|
|
DWORD i;
|
|
|
|
pszValue = pszExpanded;
|
|
|
|
for ( ; *pszRegResponse; )
|
|
{
|
|
// check for a macro
|
|
//
|
|
if ( *pszRegResponse == LMSCH )
|
|
{
|
|
// <cr>
|
|
//
|
|
if (!strncmpi(pszRegResponse,CR_MACRO,CR_MACRO_LENGTH))
|
|
{
|
|
*pszValue++ = CR;
|
|
pszRegResponse += CR_MACRO_LENGTH;
|
|
continue;
|
|
}
|
|
|
|
// <lf>
|
|
//
|
|
if (!strncmpi(pszRegResponse,LF_MACRO,LF_MACRO_LENGTH))
|
|
{
|
|
*pszValue++ = LF;
|
|
pszRegResponse += LF_MACRO_LENGTH;
|
|
continue;
|
|
}
|
|
|
|
// <hxx>
|
|
//
|
|
if ((pszRegResponse[1] == 'h' || pszRegResponse[1] == 'H') &&
|
|
isxdigit(pszRegResponse[2]) &&
|
|
isxdigit(pszRegResponse[3]) &&
|
|
pszRegResponse[4] == RMSCH )
|
|
{
|
|
*pszValue++ = (char) ((ctox(pszRegResponse[2]) << 4) + ctox(pszRegResponse[3]));
|
|
pszRegResponse += 5;
|
|
continue;
|
|
}
|
|
|
|
// <macro>
|
|
//
|
|
if (pMdmMacro)
|
|
{
|
|
bFound = FALSE;
|
|
|
|
// Check for a matching macro.
|
|
//
|
|
for (i = 0; i < cbMacros; i++)
|
|
{
|
|
cbTmp = lstrlenA(pMdmMacro[i].MacroName);
|
|
if (!strncmpi(pszRegResponse, pMdmMacro[i].MacroName, cbTmp))
|
|
{
|
|
pchTmp = pMdmMacro[i].MacroValue;
|
|
while (*pchTmp)
|
|
{
|
|
*pszValue++ = *pchTmp++;
|
|
}
|
|
pszRegResponse += cbTmp;
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Did we get a match?
|
|
//
|
|
if (bFound)
|
|
{
|
|
continue;
|
|
}
|
|
} // <macro>
|
|
} // LMSCH
|
|
|
|
// No matches, copy the character verbatim.
|
|
//
|
|
*pszValue++ = *pszRegResponse++;
|
|
} // for
|
|
|
|
*pszValue = 0;
|
|
if (pdwValLen)
|
|
{
|
|
*pdwValLen = pszValue - pszExpanded;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int Mystrncmp(char *dst, char *src, long count)
|
|
{
|
|
while (count) {
|
|
if (*dst != *src)
|
|
return 1;
|
|
if (*src == 0)
|
|
return 0;
|
|
dst++;
|
|
src++;
|
|
count--;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int strncmpi(char *dst, char *src, long count)
|
|
{
|
|
while (count) {
|
|
if (toupper(*dst) != toupper(*src))
|
|
return 1;
|
|
if (*src == 0)
|
|
return 0;
|
|
dst++;
|
|
src++;
|
|
count--;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
LPSTR WINAPI
|
|
NewLoadRegCommands(
|
|
HKEY hKey,
|
|
LPSTR szRegCommand,
|
|
LPSTR pszzAppend
|
|
)
|
|
{
|
|
LPSTR pszzNew, pszStr;
|
|
ULONG ulAllocSize = 0;
|
|
HKEY hKeyCommand;
|
|
DWORD dwIndex;
|
|
char szValue[MAXUINTSTRLENGTH];
|
|
DWORD dwType, dwSize;
|
|
DWORD dwRet;
|
|
|
|
// open zee key
|
|
//
|
|
if (RegOpenKeyA(hKey, szRegCommand, &hKeyCommand)
|
|
!= ERROR_SUCCESS)
|
|
{
|
|
DPRINTFA1("was unable to open the '%s' key in LoadRegCommands.", szRegCommand);
|
|
return NULL;
|
|
}
|
|
|
|
// Calculate size of the registry command, including null-terminators for each command.
|
|
//
|
|
dwIndex = CMD_INDEX_START;
|
|
do
|
|
{
|
|
wsprintfA(szValue, "%d", dwIndex);
|
|
if ((dwRet = RegQueryValueExA(hKeyCommand, szValue, NULL, &dwType, NULL,
|
|
&dwSize)) == ERROR_SUCCESS)
|
|
{
|
|
if (dwType != REG_SZ)
|
|
{
|
|
DPRINTF("command wasn't REG_SZ in LoadRegCommands.");
|
|
pszzNew = NULL;
|
|
goto Exit;
|
|
}
|
|
ulAllocSize += dwSize;
|
|
}
|
|
dwIndex++;
|
|
}
|
|
while(dwRet == ERROR_SUCCESS);
|
|
|
|
if (dwRet != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
DPRINTF("RegQueryValueEx in LoadRegCommands failed for a reason besides ERROR_FILE_NOT_FOUND.");
|
|
pszzNew = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
// ReAllocate or Allocate memory depending on whether we are appending...
|
|
//
|
|
if (pszzAppend)
|
|
{
|
|
ULONG ulAppendSize;
|
|
|
|
if (!(ulAppendSize = LocalSize(pszzAppend)))
|
|
{
|
|
DPRINTF("failed to get the size of an append heap pointer in LoadRegCommands.");
|
|
pszzNew = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
// ReAllocate
|
|
//
|
|
ulAllocSize += ulAppendSize; // double-null already accounted for
|
|
//pszzNew = (LPSTR)LocalReAlloc(pszzAppend, ulAllocSize, LMEM_ZEROINIT);
|
|
if (pszzNew = (LPSTR)LocalAlloc(LMEM_ZEROINIT, ulAllocSize))
|
|
{
|
|
CopyMemory(pszzNew, pszzAppend, ulAppendSize);
|
|
};
|
|
}
|
|
else
|
|
{
|
|
// Allocate
|
|
//
|
|
ulAllocSize++; // double-null terminator accounting
|
|
pszzNew = (LPSTR)LocalAlloc(LPTR, ulAllocSize);
|
|
}
|
|
|
|
// Check errors for either the Alloc or ReAlloc
|
|
if (!pszzNew)
|
|
{
|
|
DPRINTF1("had a failure doing an alloc or a realloc in LoadRegCommands. Heap size %d",
|
|
ulAllocSize);
|
|
goto Exit; // pszzNew already NULL
|
|
}
|
|
|
|
// Set pszStr to point to the next location to load.
|
|
pszStr = pszzNew;
|
|
while (*pszStr) // move to next open slot in buffer if need be (append only)
|
|
{
|
|
pszStr += lstrlenA(pszStr) + 1;
|
|
}
|
|
|
|
// Did we go to far?
|
|
//
|
|
ASSERT ((ULONG)(pszStr - pszzNew) < ulAllocSize);
|
|
|
|
// Read in and add strings to the (rest of the) buffer.
|
|
//
|
|
dwIndex = CMD_INDEX_START;
|
|
dwSize = ulAllocSize - (pszStr - pszzNew);
|
|
do
|
|
{
|
|
wsprintfA(szValue, "%d", dwIndex);
|
|
if ((dwRet = RegQueryValueExA(hKeyCommand, szValue,
|
|
NULL, NULL, (VOID *)pszStr, &dwSize))
|
|
== ERROR_SUCCESS)
|
|
{
|
|
pszStr += dwSize; // includes terminating null
|
|
}
|
|
dwIndex++;
|
|
dwSize = ulAllocSize - (pszStr - pszzNew);
|
|
}
|
|
while (dwRet == ERROR_SUCCESS);
|
|
|
|
if (dwRet != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
DPRINTF("2nd RegQueryValueEx in LoadRegCommands failed for a reason besides ERROR_FILE_NOT_FOUND.");
|
|
LocalFree(pszzNew);
|
|
pszzNew = NULL;
|
|
goto Exit;
|
|
}
|
|
|
|
// Did we go to far?
|
|
//
|
|
ASSERT ((ULONG)(pszStr - pszzNew) < ulAllocSize);
|
|
|
|
// no need to put in the final double-null null, size this buffer was already zerod.
|
|
|
|
Exit:
|
|
RegCloseKey(hKeyCommand);
|
|
return pszzNew;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// LPSTR LoadRegCommands(MODEMINFORMATION *pModemInfo,
|
|
// LPSTR szRegCommand, LPSTR pszzAppend)
|
|
//
|
|
// Function: Loads a registry command into memory. Memory is allocated if
|
|
// pszzAppend is NULL. Memory is re-allocated/enlarged if
|
|
// pszzAppend is not NULL. In this case, the registry command
|
|
// will be appended to the existing buffer.
|
|
//
|
|
// Returns: NULL on failure.
|
|
// A doubly-null terminated buffer of singly-null terminated strings
|
|
// on success.
|
|
//****************************************************************************
|
|
|
|
LPSTR LoadRegCommands(MODEMINFORMATION *pModemInfo,
|
|
LPSTR szRegCommand,
|
|
LPSTR pszzAppend)
|
|
{
|
|
|
|
return NewLoadRegCommands(
|
|
pModemInfo->mi_hKeyModem,
|
|
szRegCommand,
|
|
pszzAppend
|
|
);
|
|
|
|
}
|
|
|
|
char szBlindOn[] = "Blind_On"; // explicit for stack memory optimizations below
|
|
char szBlindOff[] = "Blind_Off"; // explicit for stack memory optimizations below
|
|
|
|
//****************************************************************************
|
|
// LPSTR CreateDialCommands(MODEMINFORMATION *pModemInfo, LPSTR szPhoneNumber,
|
|
// BOOL *fOriginate)
|
|
//
|
|
// Function: Create the dial strings in memory ala:
|
|
// "<prefix> <blind_on/off> <dial prefix> <phonenumber> <dial suffix> <terminator>"
|
|
// ... more dial strings for long phone numbers...
|
|
// "" <- final null of a doubly null terminated list
|
|
//
|
|
// if no dial prefix, then return NULL
|
|
// if no dial suffix, then don't do any commands after the first dial command
|
|
//
|
|
// Set fOriginate to TRUE if these dial strings will cause a connection origination.
|
|
// FALSE otherwise.
|
|
//
|
|
// break lines longer then HAYES_COMMAND_LENGTH
|
|
//
|
|
// WARNING - this function is reall cheesy and hacked. The main reason for this
|
|
// is that it attempts to be memory (read: stack) optimized.
|
|
//
|
|
// szPhoneNumber is a null terminated string of digits (0-9, $, @, W), with a possible
|
|
// ';' at the end. The semicolon can only be at the end.
|
|
//
|
|
// Examples:
|
|
//
|
|
// "" -> originate -> ATX_DT<cr> fOriginate = TRUE
|
|
// ";" -> dialtone detection -> ATX_DT;<cr> fOriginate = FALSE
|
|
// "5551212" -> dial and originate -> ATX_DT5551212<cr> fOriginate = TRUE
|
|
// "5551212;" -> dial -> ATX_DT5551212;<cr> fOriginate = FALSE
|
|
// "123456789012345678901234567890123456789012345678901234567890"
|
|
// -> dial and originate -> ATX_DT12345678901234567890123456789012;<cr>
|
|
// ATX_DT3456789012345678901234567890<cr>
|
|
// fOriginate = TRUE
|
|
// "123456789012345678901234567890123456789012345678901234567890;"
|
|
// -> dial -> ATX_DT12345678901234567890123456789012;<cr>
|
|
// ATX_DT3456789012345678901234567890;<cr>
|
|
// fOriginate = FALSE
|
|
//
|
|
// Returns: NULL on failure.
|
|
// A null terminated buffer of the dial command on success.
|
|
//****************************************************************************
|
|
|
|
LPSTR
|
|
CreateDialCommands(
|
|
MODEMINFORMATION *pModemInfo,
|
|
LPSTR szPhoneNumber,
|
|
BOOL *fOriginate,
|
|
DWORD DialOptions
|
|
)
|
|
{
|
|
// HKEY hSettingsKey;
|
|
DWORD dwSize;
|
|
DWORD dwType;
|
|
LPSTR pszTemp;
|
|
LPSTR pszDialPrefix; // ex. "ATX4DT" or "ATX3DT"
|
|
LPSTR pszDialSuffix; // ex. ";<cr>"
|
|
LPSTR pszOrigSuffix; // ex. "<cr>"
|
|
LPSTR pchDest, pchSrc;
|
|
LPSTR pszzDialCommands = NULL;
|
|
CHAR pszShortTemp[2];
|
|
#ifdef DEBUG
|
|
static char szDialPrefix[] = "DialPrefix";
|
|
static char szDialSuffix[] = "DialSuffix";
|
|
static char szTone[] = "Tone";
|
|
static char szPulse[] = "Pulse";
|
|
#endif
|
|
DWORD Length;
|
|
|
|
BOOL fHaveDialSuffix=TRUE;
|
|
|
|
// Figure out fOriginate
|
|
pchSrc = szPhoneNumber;
|
|
*fOriginate = TRUE;
|
|
while (*pchSrc)
|
|
{
|
|
if (';' == *pchSrc)
|
|
{
|
|
// make sure the string is correctly formed.
|
|
//
|
|
ASSERT(pchSrc[1] == '\0');
|
|
|
|
*fOriginate = FALSE;
|
|
}
|
|
pchSrc++;
|
|
}
|
|
|
|
// Trim the semicolon off the end, now that we know this is not an origination string.
|
|
if (!(*fOriginate))
|
|
{
|
|
ASSERT(pchSrc[-1] == ';');
|
|
pchSrc[-1] = 0;
|
|
}
|
|
|
|
// At this point, szPhoneNumber is just a string of digits to be dialed, with no semicolon at
|
|
// the end. Plus we know whether to originate or not.
|
|
|
|
// make some temp space
|
|
|
|
pszTemp = (LPSTR)LocalAlloc(LPTR,
|
|
HAYES_COMMAND_LENGTH + 1 + // pszTemp
|
|
HAYES_COMMAND_LENGTH + 1 + // pszDialPrefix
|
|
HAYES_COMMAND_LENGTH + 1 + // pszDialSuffix
|
|
HAYES_COMMAND_LENGTH + 1); // pszOrigSuffix
|
|
if (!pszTemp)
|
|
{
|
|
MCXPRINTF("out of memory.");
|
|
return NULL;
|
|
}
|
|
pszDialPrefix = pszTemp + HAYES_COMMAND_LENGTH + 1;
|
|
pszDialSuffix = pszDialPrefix + HAYES_COMMAND_LENGTH + 1;
|
|
pszOrigSuffix = pszDialSuffix + HAYES_COMMAND_LENGTH + 1;
|
|
|
|
|
|
lstrcpyA(pszDialPrefix,"");
|
|
//
|
|
// read in prefix
|
|
//
|
|
GetCommonDialComponent(
|
|
pModemInfo->mi_hCommon,
|
|
pszDialPrefix,
|
|
HAYES_COMMAND_LENGTH,
|
|
COMMON_DIAL_COMMOND_PREFIX
|
|
);
|
|
|
|
|
|
//
|
|
// do we support blind dialing and do we need to set the blind dialing state?
|
|
//
|
|
if ((MDM_BLIND_DIAL & pModemInfo->mi_dwModemOptionsCap)
|
|
&&
|
|
((DialOptions & MDM_BLIND_DIAL) != (pModemInfo->mi_dwPreferredModemOptions & MDM_BLIND_DIAL))) {
|
|
|
|
//
|
|
// read in blind options
|
|
//
|
|
Length=GetCommonDialComponent(
|
|
pModemInfo->mi_hCommon,
|
|
pszDialPrefix+lstrlenA(pszDialPrefix),
|
|
HAYES_COMMAND_LENGTH,
|
|
DialOptions & MDM_BLIND_DIAL ? COMMON_DIAL_BLIND_ON : COMMON_DIAL_BLIND_OFF
|
|
);
|
|
|
|
if (Length == 0) {
|
|
MCXPRINTF1("RegQueryValueEx failed when opening %s.",
|
|
DialOptions & MDM_BLIND_DIAL ? szBlindOn : szBlindOff);
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// read in dial prefix
|
|
|
|
Length=GetCommonDialComponent(
|
|
pModemInfo->mi_hCommon,
|
|
pszDialPrefix+lstrlenA(pszDialPrefix),
|
|
HAYES_COMMAND_LENGTH,
|
|
COMMON_DIAL_PREFIX
|
|
);
|
|
|
|
if (Length == 0) {
|
|
MCXPRINTF1("'%s' wasn't REG_SZ.", szDialPrefix);
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
// can we do tone or pulse dialing?
|
|
if (MDM_TONE_DIAL & pModemInfo->mi_dwModemOptionsCap)
|
|
{
|
|
//
|
|
// read in dial mode (tone or pulse)
|
|
//
|
|
Length=GetCommonDialComponent(
|
|
pModemInfo->mi_hCommon,
|
|
pszDialPrefix+lstrlenA(pszDialPrefix),
|
|
HAYES_COMMAND_LENGTH,
|
|
DialOptions & MDM_TONE_DIAL ? COMMON_DIAL_TONE : COMMON_DIAL_PULSE
|
|
);
|
|
|
|
if (Length == 0) {
|
|
MCXPRINTF1("'%s' wasn't REG_SZ.",
|
|
DialOptions & MDM_TONE_DIAL ? szTone : szPulse);
|
|
goto Failure;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// read in dial suffix
|
|
//
|
|
Length=GetCommonDialComponent(
|
|
pModemInfo->mi_hCommon,
|
|
pszDialSuffix,
|
|
HAYES_COMMAND_LENGTH,
|
|
COMMON_DIAL_SUFFIX
|
|
);
|
|
|
|
if (Length <= 1) {
|
|
|
|
MCXPRINTF1("RegQueryValueEx failed when opening %s.", szDialSuffix);
|
|
lstrcpyA(pszDialSuffix, "");
|
|
fHaveDialSuffix = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// read in prefix terminator
|
|
//
|
|
Length=GetCommonDialComponent(
|
|
pModemInfo->mi_hCommon,
|
|
pszOrigSuffix,
|
|
HAYES_COMMAND_LENGTH,
|
|
COMMON_DIAL_TERMINATION
|
|
);
|
|
|
|
if (Length != 0) {
|
|
|
|
lstrcatA(pszDialSuffix, pszOrigSuffix);
|
|
ASSERT(lstrlenA(pszOrigSuffix) <= lstrlenA(pszDialSuffix));
|
|
}
|
|
|
|
|
|
ASSERT ((lstrlenA(pszDialPrefix) + lstrlenA(pszDialSuffix)) <= HAYES_COMMAND_LENGTH);
|
|
|
|
// allocate space for the phone number lines
|
|
{
|
|
DWORD dwBytesAlreadyTaken = lstrlenA(pszDialPrefix) + lstrlenA(pszDialSuffix);
|
|
DWORD dwAvailBytesPerLine = (HAYES_COMMAND_LENGTH - dwBytesAlreadyTaken);
|
|
DWORD dwPhoneNumLen = lstrlenA(szPhoneNumber);
|
|
DWORD dwNumLines = dwPhoneNumLen ? (dwPhoneNumLen / dwAvailBytesPerLine +
|
|
(dwPhoneNumLen % dwAvailBytesPerLine ? 1 : 0))
|
|
: 1; // handle null string
|
|
dwSize = dwPhoneNumLen + dwNumLines * (dwBytesAlreadyTaken + 1) + 1;
|
|
}
|
|
|
|
MCXPRINTF1("HeapAllocate %d bytes for Dial Commands.", dwSize);
|
|
if (!(pszzDialCommands = (LPSTR)LocalAlloc(LPTR, dwSize)))
|
|
{
|
|
MCXPRINTF("ran out of memory and failed a HeapAllocate!");
|
|
goto Failure;
|
|
}
|
|
|
|
pchDest = pszzDialCommands; // point to the beginning of the commands
|
|
|
|
// build dial line(s):
|
|
// do we have a dial suffix
|
|
if (!fHaveDialSuffix)
|
|
{
|
|
// we can't do much except just use the whole string and pray...
|
|
// but, can we fit the dial string?
|
|
ASSERT (lstrlenA(pszDialPrefix) + lstrlenA(szPhoneNumber) +
|
|
lstrlenA(pszDialSuffix) <= HAYES_COMMAND_LENGTH);
|
|
|
|
// did we not want to originate?
|
|
ASSERT(*fOriginate);
|
|
|
|
// build it
|
|
lstrcpyA(pchDest, pszDialPrefix);
|
|
lstrcatA(pchDest, szPhoneNumber);
|
|
lstrcatA(pchDest, pszDialSuffix);
|
|
}
|
|
else
|
|
{
|
|
// we have a dial suffix.
|
|
|
|
// populate new pszzDialCommands with semi-colons as necessary.
|
|
|
|
// go through and add suffixi, making sure lines don't exceed HAYES_COMMAND_LENGTH
|
|
pchSrc = szPhoneNumber; // moves a character at a time.
|
|
pszShortTemp[1] = 0;
|
|
|
|
// prime the pump
|
|
lstrcpyA(pchDest, pszDialPrefix);
|
|
|
|
// step through the source
|
|
while (*pchSrc)
|
|
{
|
|
if (lstrlenA(pchDest) + lstrlenA(pszDialSuffix) + 1 > HAYES_COMMAND_LENGTH)
|
|
{
|
|
// finish up this string
|
|
lstrcatA(pchDest, pszDialSuffix);
|
|
|
|
// begin a new string
|
|
pchDest += lstrlenA(pchDest) + 1;
|
|
lstrcpyA(pchDest, pszDialPrefix);
|
|
}
|
|
else
|
|
{
|
|
// copy char
|
|
pszShortTemp[0] = *pchSrc;
|
|
lstrcatA(pchDest, pszShortTemp);
|
|
pchSrc++;
|
|
}
|
|
}
|
|
|
|
// conclude with the approprate Suffix.
|
|
lstrcatA(pchDest, (*fOriginate ? pszOrigSuffix : pszDialSuffix));
|
|
}
|
|
|
|
// close keys
|
|
Exit:
|
|
// RegCloseKey(hSettingsKey);
|
|
LocalFree(pszTemp);
|
|
return pszzDialCommands;
|
|
|
|
Failure:
|
|
if (pszzDialCommands)
|
|
{
|
|
LocalFree(pszzDialCommands);
|
|
pszzDialCommands = NULL;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// DWORD HandleCommErrors(MODEMINFORMATION *pModemInfo, ULONG ulError)
|
|
//
|
|
// Function: Calls ClearCommError and returns the error.
|
|
//
|
|
// ulError is passed in if the error(s) are already known and
|
|
// ClearCommError doesn't need to be called.
|
|
// If ulError is NULL then ClearCommError is called.
|
|
//****************************************************************************
|
|
|
|
DWORD HandleCommErrors(MODEMINFORMATION *pModemInfo, ULONG ulError)
|
|
{
|
|
if (!ulError)
|
|
{
|
|
// failed to read or write due to a possible communication error
|
|
// determine if this was actually an error, or just no chars (in the read case)
|
|
if (!ClearCommError(pModemInfo->mi_PortHandle, &ulError, NULL))
|
|
{
|
|
// ClearCommError failed
|
|
MCXPRINTF("ClearCommError error");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (ulError)
|
|
{
|
|
if (ulError & CE_BREAK)
|
|
{MCXPRINTF("CE_BREAK");}
|
|
if (ulError & CE_DNS)
|
|
{MCXPRINTF("CE_DNS");}
|
|
if (ulError & CE_MODE)
|
|
{MCXPRINTF("CE_MODE");}
|
|
if (ulError & CE_OOP)
|
|
{MCXPRINTF("CE_OOP");}
|
|
if (ulError & CE_PTO)
|
|
{MCXPRINTF("CE_PTO");}
|
|
if (ulError & CE_TXFULL)
|
|
{MCXPRINTF("CE_TXFULL");}
|
|
if (ulError & CE_FRAME)
|
|
{MCXPRINTF("CE_FRAME");}
|
|
if (ulError & CE_IOE)
|
|
{MCXPRINTF("CE_IOE");}
|
|
if (ulError & CE_OVERRUN)
|
|
{MCXPRINTF("CE_OVERRUN");}
|
|
if (ulError & CE_RXOVER)
|
|
{MCXPRINTF("CE_RXOVER");}
|
|
if (ulError & CE_RXPARITY)
|
|
{MCXPRINTF("CE_RXPARITY");}
|
|
}
|
|
//else
|
|
//{
|
|
// MCXPRINTF("tried to read, but nothing was there.");
|
|
//}
|
|
#endif // DEBUG
|
|
|
|
return ulError;
|
|
}
|
|
|
|
|
|
// SynchrnonizeCommConfigSettings
|
|
//
|
|
// Do a GetCommConfig from modem.sys and update our settings info.
|
|
// Check to see if we need to set fSettingsInitStringsBuilt to FALSE.
|
|
// If flagged, write down our Negotiated stuff to modem.sys using
|
|
// SetCommConfig.
|
|
//
|
|
VOID SynchronizeCommConfigSettings(MODEMINFORMATION * pModemInfo,
|
|
BOOL fUpdateModemSys)
|
|
{
|
|
#define COMMCONFIG_AND_MODEMSETTINGS_LEN (60*2)
|
|
|
|
BYTE byteTmp[COMMCONFIG_AND_MODEMSETTINGS_LEN];
|
|
LPCOMMCONFIG lpCC = (LPCOMMCONFIG)byteTmp;
|
|
DWORD dwSize = sizeof(byteTmp);
|
|
|
|
ASSERT(sizeof(byteTmp) >=
|
|
sizeof(COMMCONFIG) + sizeof(MODEMSETTINGS));
|
|
|
|
if (GetCommConfig(pModemInfo->mi_PortHandle,
|
|
lpCC,
|
|
&dwSize) == TRUE)
|
|
{
|
|
LPMODEMSETTINGS lpMS;
|
|
|
|
lpMS = (LPMODEMSETTINGS)((LPBYTE)lpCC
|
|
+ lpCC->dwProviderOffset);
|
|
|
|
MCXSetModemSettings(
|
|
pModemInfo,
|
|
lpMS
|
|
);
|
|
|
|
#if 0
|
|
// Need to rebuild init string?
|
|
//
|
|
if (pModemInfo->mi_dwCallSetupFailTimerSetting != lpMS->dwCallSetupFailTimer
|
|
|| pModemInfo->mi_dwInactivityTimeoutSetting != lpMS->dwInactivityTimeout
|
|
|| pModemInfo->mi_dwSpeakerVolumeSetting != lpMS->dwSpeakerVolume
|
|
|| pModemInfo->mi_dwSpeakerModeSetting != lpMS->dwSpeakerMode
|
|
|| pModemInfo->mi_dwPreferredModemOptions != lpMS->dwPreferredModemOptions)
|
|
{
|
|
pModemInfo->mi_fSettingsInitStringsBuilt = FALSE;
|
|
}
|
|
|
|
pModemInfo->mi_dwCallSetupFailTimerSetting =
|
|
lpMS->dwCallSetupFailTimer;
|
|
|
|
pModemInfo->mi_dwInactivityTimeoutSetting =
|
|
lpMS->dwInactivityTimeout;
|
|
|
|
pModemInfo->mi_dwSpeakerVolumeSetting =
|
|
lpMS->dwSpeakerVolume;
|
|
|
|
pModemInfo->mi_dwSpeakerModeSetting =
|
|
lpMS->dwSpeakerMode;
|
|
|
|
pModemInfo->mi_dwPreferredModemOptions =
|
|
lpMS->dwPreferredModemOptions;
|
|
#endif
|
|
|
|
|
|
if (fUpdateModemSys)
|
|
{
|
|
lpMS->dwNegotiatedModemOptions =
|
|
pModemInfo->mi_dwNegotiatedModemOptions;
|
|
|
|
lpMS->dwNegotiatedDCERate =
|
|
pModemInfo->mi_dwNegotiatedDCERate;
|
|
|
|
if (SetCommConfig(pModemInfo->mi_PortHandle,
|
|
lpCC,
|
|
sizeof(byteTmp)) != TRUE)
|
|
{
|
|
MCXPRINTF1("SetCommConfig() failed and returned %d",
|
|
GetLastError());
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MCXPRINTF1("GetCommConfig() failed and returned %d",
|
|
GetLastError());
|
|
ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void WINAPI
|
|
PrintGoodResponse(
|
|
HANDLE hLogFile,
|
|
DWORD dwID,
|
|
DWORD ResponseState
|
|
)
|
|
|
|
{
|
|
|
|
char Response[128];
|
|
char ResponseType[128];
|
|
DWORD StringID;
|
|
INT StringLength;
|
|
|
|
|
|
#ifndef DEBUG
|
|
if (!hLogFile && !TRACINGENABLED()) return;
|
|
#endif // !DEBUG
|
|
|
|
StringID=(ResponseState >= RESPONSE_START || ResponseState <= RESPONSE_END)
|
|
? (IDS_RESP_OK + ResponseState) : IDS_RESP_UNKNOWN;
|
|
|
|
|
|
StringLength=LoadStringA(
|
|
ghInstance,
|
|
IDS_MSGLOG_RESPONSE,
|
|
Response,
|
|
sizeof(Response)
|
|
);
|
|
|
|
if (StringLength == 0) {
|
|
|
|
return;
|
|
}
|
|
|
|
StringLength=LoadStringA(
|
|
ghInstance,
|
|
StringID,
|
|
ResponseType,
|
|
sizeof(ResponseType)
|
|
);
|
|
|
|
if (StringLength == 0) {
|
|
|
|
return;
|
|
}
|
|
|
|
LogPrintf(
|
|
hLogFile,
|
|
dwID,
|
|
Response,
|
|
ResponseType
|
|
);
|
|
|
|
|
|
D_TRACE(McxDpf(dwID,Response,ResponseType);)
|
|
|
|
D_TRACE(McxDpf(dwID,"Good Response");)
|
|
|
|
}
|
|
|
|
//****************************************************************************
|
|
// PrintString
|
|
// dwOption:
|
|
// PS_SEND - Send prefix used
|
|
// PS_SEND_SECURE - Send prefix used and numbers replaced with #s
|
|
// PS_RECV - Recv prefix used
|
|
// Send the response string to VxDWin and Log
|
|
// We only care about seeing 50 chars under RETAIL,
|
|
// and MAXSTRINGLENGTH * MAX_DBG_CHARS_PER_BIN_CHAR under DEBUG
|
|
// BUGBUG - any number chars on a dialing line will be changed to #.
|
|
// BUGBUG - this includes X3 -> X#. The extra code to handle this isn't
|
|
// BUGBUG - worth it.
|
|
//****************************************************************************
|
|
|
|
#define MAX_DBG_CHARS_PER_BIN_CHAR 4
|
|
#ifdef DEBUG
|
|
#define RAWRESPONSELEN 100
|
|
#else
|
|
#define RAWRESPONSELEN 50 // good number for remaining chars on a line after the time stamp
|
|
#endif
|
|
|
|
void WINAPI
|
|
PrintString(
|
|
HANDLE hLogFile,
|
|
DWORD dwID,
|
|
char *pchStr,
|
|
DWORD dwLength,
|
|
DWORD dwOption
|
|
)
|
|
{
|
|
char temp[RAWRESPONSELEN + 1];
|
|
char *src,*dest;
|
|
static const char szHex[] = "0123456789abcdef";
|
|
int i;
|
|
|
|
#ifndef DEBUG
|
|
if (!hLogFile && !TRACINGENABLED()) return;
|
|
#endif // !DEBUG
|
|
|
|
i = dwLength;
|
|
src = pchStr;
|
|
dest = temp;
|
|
|
|
while (i-- && (dest - temp < RAWRESPONSELEN - MAX_DBG_CHARS_PER_BIN_CHAR))
|
|
{
|
|
// ascii printable chars are between 0x20 and 0x7e, inclusive
|
|
if (*src >= 0x20 && *src <= 0x7e)
|
|
{
|
|
#ifdef DEBUG // only blank out digits under RETAIL
|
|
*dest++ = *src;
|
|
#else // DEBUG
|
|
// printable text
|
|
if (PS_SEND_SECURE == dwOption && isdigit(*src))
|
|
{
|
|
*dest++ = '#';
|
|
}
|
|
else
|
|
{
|
|
*dest++ = *src;
|
|
}
|
|
#endif // DEBUG
|
|
}
|
|
else
|
|
{
|
|
// binary
|
|
switch (*src)
|
|
{
|
|
case CR:
|
|
*dest++ = '<'; *dest++ = 'c'; *dest++ = 'r'; *dest++ = '>';
|
|
break;
|
|
case LF:
|
|
*dest++ = '<'; *dest++ = 'l'; *dest++ = 'f'; *dest++ = '>';
|
|
break;
|
|
default:
|
|
*dest++ = '<';
|
|
*dest++ = szHex[(*src>>4) & 0xf];
|
|
*dest++ = szHex[*src & 0xf];
|
|
*dest++ = '>';
|
|
}
|
|
}
|
|
src++;
|
|
}
|
|
*dest = 0;
|
|
|
|
switch (dwOption)
|
|
{
|
|
case PS_SEND:
|
|
case PS_SEND_SECURE:
|
|
|
|
LogString(hLogFile,dwID, IDS_MSGLOG_COMMAND, temp);
|
|
D_TRACE(McxDpf(dwID, "Send: %s\r\n", temp);)
|
|
|
|
break;
|
|
|
|
case PS_RECV:
|
|
{
|
|
|
|
char Response[128];
|
|
char EmptyResponse[128];
|
|
INT StringLength;
|
|
|
|
|
|
|
|
StringLength=LoadStringA(
|
|
ghInstance,
|
|
IDS_MSGLOG_RAWRESPONSE,
|
|
Response,
|
|
sizeof(Response)
|
|
);
|
|
|
|
if (StringLength == 0) {
|
|
|
|
return;
|
|
}
|
|
|
|
StringLength=LoadStringA(
|
|
ghInstance,
|
|
IDS_MSGLOG_EMPTYRESPONSE,
|
|
EmptyResponse,
|
|
sizeof(EmptyResponse)
|
|
);
|
|
|
|
if (StringLength == 0) {
|
|
|
|
return;
|
|
}
|
|
|
|
LogPrintf(
|
|
hLogFile,
|
|
dwID,
|
|
Response,
|
|
dwLength ? temp : EmptyResponse
|
|
);
|
|
|
|
|
|
|
|
D_TRACE(McxDpf(dwID,Response,
|
|
dwLength ? temp : EmptyResponse);)
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//****************************************************************************
|
|
// void PrintCommSettings(DCB * pDcb)
|
|
//
|
|
// Function: Dumps a portion of the Ring0 DCB.
|
|
//****************************************************************************
|
|
|
|
void WINAPI
|
|
PrintCommSettings(
|
|
HANDLE hLogFile,
|
|
DWORD dwID,
|
|
DCB * pDcb
|
|
)
|
|
{
|
|
static const char achParity[] = "NOEMS";
|
|
static const char *aszStopBits[] = { "1",
|
|
"1.5",
|
|
"2" };
|
|
|
|
#ifndef DEBUG
|
|
if (!hLogFile && !TRACINGENABLED()) return;
|
|
#endif // !DEBUG
|
|
|
|
LogPrintf(
|
|
hLogFile,
|
|
dwID,
|
|
"%d,%c,%d,%s\r\n",
|
|
pDcb->BaudRate,
|
|
achParity[pDcb->Parity],
|
|
pDcb->ByteSize,
|
|
aszStopBits[pDcb->StopBits]
|
|
);
|
|
|
|
D_TRACE(McxDpf(dwID,
|
|
"%d,%c,%d,%s, ctsfl=%d, rtsctl=%d",
|
|
pDcb->BaudRate,
|
|
achParity[pDcb->Parity],
|
|
pDcb->ByteSize,
|
|
aszStopBits[pDcb->StopBits],
|
|
pDcb->fOutxCtsFlow,
|
|
pDcb->fRtsControl
|
|
);)
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// void CancelPendingIOAndPurgeCommBuffers(PMODEMINFORMATION pModemInfo)
|
|
//
|
|
// Function: Sets CommMask to 0, waits for any I/O to complete and purges
|
|
// buffers.
|
|
//****************************************************************************
|
|
|
|
void WINAPI
|
|
CancelPendingIoAndPurgeCommBuffers(
|
|
PMODEMINFORMATION pModemInfo,
|
|
BOOL Purge
|
|
)
|
|
{
|
|
// Set these, even if there isn't some I/O currently going on. We want to
|
|
// make sure the recv and xmit buffers are empty, plus it doesn't hurt to
|
|
// have the mask set to 0.
|
|
//
|
|
#ifdef DEBUG
|
|
if (Purge) {
|
|
|
|
MCXPRINTF("CancelPendingIOAndPurgeBuffers: purging");
|
|
|
|
} else {
|
|
|
|
MCXPRINTF("CancelPendingIOAndPurgeBuffers");
|
|
}
|
|
#endif
|
|
|
|
SetCommMask(pModemInfo->mi_PortHandle, 0);
|
|
PurgeComm(pModemInfo->mi_PortHandle, PURGE_TXABORT | PURGE_RXABORT |
|
|
(Purge ? (PURGE_TXCLEAR | PURGE_RXCLEAR) : 0));
|
|
|
|
if (pModemInfo->mi_timeout)
|
|
{
|
|
MCXPRINTF("CancelPendingIOAndPurgeBuffers: killing timer");
|
|
pModemInfo->mi_timeout = 0;
|
|
if (KillMdmTimer(pModemInfo->mi_dwCompletionKey,
|
|
(LPOVERLAPPED)pModemInfo->mi_lpOverlappedEvent) == TRUE)
|
|
{
|
|
OverPoolFree((LPOVERLAPPED)pModemInfo->mi_lpOverlappedEvent);
|
|
}
|
|
}
|
|
|
|
|
|
pModemInfo->mi_lpOverlappedRW=NULL;
|
|
pModemInfo->mi_lpOverlappedEvent=NULL;
|
|
//
|
|
// mark this io as old, so it will be ignored.
|
|
//
|
|
pModemInfo->mi_dwRWIOExpected++;
|
|
pModemInfo->mi_dwEventIOExpected++;
|
|
|
|
pModemInfo->mi_dwDeferedExpected++;
|
|
}
|
|
|
|
|
|
#ifdef VOICEVIEW
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api void | VVEscapeFunc | This function handles requests
|
|
from the VoiceView DDI
|
|
|
|
@parm MODEMINFORMATION * | hPort | port handle of modem
|
|
|
|
@parm long | function | escape code
|
|
|
|
@parm long | Indata | Optional data (escape function specific)
|
|
|
|
@rdesc Returns VVR_??? return results.
|
|
|
|
Escape functions and parameters:
|
|
VVF_OPEN init - pass in callback function pointer
|
|
VVF_CLOSE your done, bye...
|
|
VVF_MONITOR swithc to fclass 80
|
|
VVF_UNMONITOR fclass 0
|
|
VVF_TAKEOVER using the port
|
|
VVF_RELEASE no longer using
|
|
|
|
This function is called from the VoiceView DDI VxD. This is called through
|
|
the VCOMM modem escape function and we hook/look for any of these functions
|
|
|
|
This whole mess is so that we can put the modem into fclass 80 (voiceview)
|
|
and look for VV responses from the modem while no one is using it
|
|
|
|
*****************************************************************************/
|
|
int _cdecl VVEscapeFunc(MODEMINFORMATION *hPort, long lFunction, long lIndata)
|
|
{
|
|
APIINFO apiInfo;
|
|
VMMHKEY hKeyCommand;
|
|
int nResult = VVS_SUCCESS;
|
|
|
|
MCXPRINTF("VVEscapeFunc");
|
|
|
|
if ( hPort == NULL ) // verify hPort
|
|
return( VVS_INVALID_FUNC ); // should try to catch others also
|
|
|
|
switch ( lFunction )
|
|
{
|
|
case VVF_OPEN:
|
|
MCXPRINTF("VVF_OPEN:");
|
|
// init all VoiceView info for this port
|
|
//--------------------------------------
|
|
if ( _RegOpenKeyA( hPort->mi_hKeyModem, szMonitorVVon, &hKeyCommand ) != ERROR_SUCCESS )
|
|
nResult = VVS_INVALID_PARM; // invalid port passed in, doesn't support VV
|
|
else if ( hPort->VVInfo.wState != VVSTATE_NONE )
|
|
nResult = VVS_BAD_STATE;
|
|
else if ( lIndata == NULL )
|
|
nResult = VVS_INVALID_PARM; // invalid func pointer
|
|
else
|
|
{
|
|
// set the state and the VV call back function pointer
|
|
hPort->VVInfo.wState = VVSTATE_INIT;
|
|
(DWORD)hPort->VVInfo.fpNotifyProc = (DWORD)lIndata;
|
|
} // end if
|
|
break;
|
|
|
|
case VVF_CLOSE:
|
|
MCXPRINTF("VVF_CLOSE:");
|
|
// shut down all the VoiceView stuff on this port
|
|
//-----------------------------------------------
|
|
if ( hPort->VVInfo.wState != VVSTATE_INIT )
|
|
nResult = VVS_BAD_STATE;
|
|
else
|
|
{
|
|
// re-init to default values
|
|
hPort->VVInfo.wState = VVSTATE_NONE;
|
|
hPort->VVInfo.wClass = VVCLASS_0;
|
|
hPort->VVInfo.dwCallBackRef = NULL;
|
|
hPort->VVInfo.fpNotifyProc = NULL;
|
|
} // end if
|
|
break;
|
|
|
|
case VVF_MONITOR:
|
|
MCXPRINTF("VVF_MONITOR:");
|
|
// start monitoring VoiceView activity, switch to FCLASS+80
|
|
//---------------------------------------------------------
|
|
if ( hPort->VVInfo.wState != VVSTATE_INIT )
|
|
nResult = VVS_BAD_STATE;
|
|
else
|
|
{
|
|
hPort->VVInfo.wState = VVSTATE_MONITOR;
|
|
hPort->VVInfo.dwCallBackRef = lIndata;
|
|
VVSetClass( hPort, VVCLASS_80 );
|
|
} // end if
|
|
break;
|
|
|
|
case VVF_UNMONITOR:
|
|
MCXPRINTF("VVF_UNMONITOR:");
|
|
// end monitoring VoiceView, switch to FCLASS+0
|
|
//---------------------------------------------
|
|
if ( hPort->VVInfo.wState != VVSTATE_MONITOR )
|
|
nResult = VVS_BAD_STATE;
|
|
else
|
|
{
|
|
hPort->VVInfo.wState = VVSTATE_INIT;
|
|
VVSetClass( hPort, VVCLASS_0 );
|
|
} // end if
|
|
break;
|
|
|
|
case VVF_TAKEOVER:
|
|
MCXPRINTF("VVF_TAKEOVER:");
|
|
// VoiceView is going to use the port
|
|
//-----------------------------------
|
|
if ( hPort->VVInfo.wState != VVSTATE_MONITOR )
|
|
nResult = VVS_BAD_STATE;
|
|
else if ( hPort->VVInfo.wClass != VVCLASS_80 )
|
|
nResult = VVS_BUSY; // not in VV mode
|
|
else
|
|
{
|
|
// setup dummy struct to call existing function
|
|
apiInfo.hPort = hPort; // set the port
|
|
apiInfo.lParam = TRUE; // turn ON takeover
|
|
apiInfo.hWnd = NULL; // dummy arg
|
|
apiInfo.msg = NULL; // dummy arg
|
|
apiInfo.szData[0] = NULL; // dummy arg
|
|
|
|
MCXSetPassthrough( &apiInfo );
|
|
} // end if
|
|
break;
|
|
|
|
case VVF_RELEASE:
|
|
MCXPRINTF("VVF_RELEASE:");
|
|
// VoiceView is done with the port
|
|
//--------------------------------
|
|
if ((hPort->VVInfo.wState != VVSTATE_MONITOR) || (hPort->VVInfo.wClass != VVCLASS_80))
|
|
nResult = VVS_BAD_STATE;
|
|
else
|
|
{
|
|
// setup dummy struct to call existing function
|
|
apiInfo.hPort = hPort; // set the port
|
|
apiInfo.lParam = FALSE; // turn OFF takeover
|
|
apiInfo.hWnd = NULL; // dummy arg
|
|
apiInfo.msg = NULL; // dummy arg
|
|
apiInfo.szData[0] = NULL; // dummy arg
|
|
|
|
MCXSetPassthrough( &apiInfo );
|
|
|
|
// reset the modem to monitor!
|
|
hPort->mi_ModemState = STATE_MONITORING;
|
|
|
|
ReadNotifyClient(hPort, MODEM_SUCCESS);
|
|
} // end if
|
|
break;
|
|
|
|
default:
|
|
MCXPRINTF("VVS_INVALID_FUNC:");
|
|
// this is an invalid escape
|
|
//--------------------------
|
|
nResult = VVS_INVALID_FUNC;
|
|
} // end switch
|
|
|
|
return( nResult );
|
|
} // end VVEscapeFunc
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api void | VVCallBackFunc | This function will call the VoiceView
|
|
DDI callback routine
|
|
|
|
@parm MODEMINFORMATION * | hPort | port handle of modem
|
|
|
|
@parm long | function | escape code
|
|
|
|
@rdesc Returns TRUE if successful, FALSE otherwise.
|
|
|
|
async events to notify about:
|
|
VVR_SSV VoiceView Data Mode Start Sequence Event
|
|
VVR_SMD Modem Data Mode Start Sequence Event
|
|
VVR_SFA Facisimile Data Mode Start Sequence Event
|
|
VVR_SRA Receive ADSI Response Event
|
|
VVR_SRQ Receive Capabilities Query Event
|
|
VVR_SRC Receive Capabilities Information Event
|
|
VVR_STO Talk-off Event (VoiceView start tone w/o a de indicator)
|
|
VVR_SVM VoiceView Message Available
|
|
|
|
VVR_LINE_GONE call has been ended
|
|
VVR_LINE_BACK call is back...
|
|
|
|
VVRS_NO_CLS80_SUPPORT can't get into fclass80
|
|
|
|
When the VxD sees some of these, he will probably take over the port
|
|
and read or write some stuff, or he will decide to go away
|
|
|
|
*****************************************************************************/
|
|
int VVCallBackFunc(MODEMINFORMATION *hPort, WORD wFunction)
|
|
{
|
|
int nResult;
|
|
|
|
MCXPRINTF("VVCallBackFunc");
|
|
|
|
if ((hPort == NULL) || // verify hPort
|
|
((wFunction < VVR_FIRST) || (wFunction > VVR_LAST))) // verify func
|
|
return( FALSE );
|
|
|
|
// go and call the VoiceView VxD
|
|
//------------------------------
|
|
nResult = (*hPort->VVInfo.fpNotifyProc)( hPort, hPort->VVInfo.dwCallBackRef, wFunction );
|
|
|
|
return( nResult );
|
|
} // end VVCallBackFunc
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api void | VVSetClass | This function will set the modem into the correct
|
|
fclass
|
|
|
|
@parm MODEMINFORMATION * | hPort | port handle of modem
|
|
|
|
@parm long | wClass | class to be set
|
|
|
|
@rdesc Returns TRUE if successful, FALSE otherwise.
|
|
|
|
We are going to put the modem into either fclass 80 (for VoiceView) or
|
|
going to put it into fclass 0 for normal stuff. The modem will spend
|
|
most if it's time in fclass80 waiting for VV stuff, but if anyone else
|
|
wants to use it, we put it back into fclass0
|
|
|
|
*****************************************************************************/
|
|
int VVSetClass(MODEMINFORMATION *hPort, WORD wClass)
|
|
{
|
|
int nResult;
|
|
APIINFO apiInfo;
|
|
|
|
if ((hPort == NULL) || // verify hPort
|
|
((hPort->VVInfo.wState != VVSTATE_INIT) && // verify state
|
|
(hPort->VVInfo.wState != VVSTATE_MONITOR)) ||
|
|
((wClass != VVCLASS_0) && (wClass != VVCLASS_80))) // verify param
|
|
return( FALSE );
|
|
|
|
if ( hPort->VVInfo.wClass == wClass )
|
|
return( TRUE ); // redundent call - ignore
|
|
|
|
// setup the voiceview states correctly
|
|
//-------------------------------------
|
|
hPort->VVInfo.wClass = wClass;
|
|
|
|
if ( hPort->mi_ModemState != STATE_MONITORING )
|
|
return( TRUE ); // we are in a invalid state, don't switch modem
|
|
|
|
// setup dummy struct and call the Monitor function
|
|
//-------------------------------------------------
|
|
apiInfo.hPort = hPort; // set the port
|
|
apiInfo.lParam = FALSE; // turn OFF takeover
|
|
apiInfo.hWnd = NULL; // dummy arg
|
|
apiInfo.msg = NULL; // dummy arg
|
|
|
|
// set the continuous monitoring state
|
|
if ( wClass == VVCLASS_80 )
|
|
{
|
|
hPort->mi_fContinuousMonitoring = TRUE;
|
|
hPort->VVInfo.fContinuousMonitoring = hPort->mi_fContinuousMonitoring;
|
|
}
|
|
else // VVCLASS_0
|
|
{
|
|
// restore old continuous monitoring state
|
|
hPort->mi_fContinuousMonitoring = hPort->VVInfo.fContinuousMonitoring;
|
|
} // end if
|
|
*((DWORD *)(&apiInfo.szData[0])) = hPort->mi_fContinuousMonitoring; // continuously monitor?
|
|
|
|
// switch the fclass!
|
|
//-------------------
|
|
nResult = RealMonitor( &apiInfo ); // implicitly sets fclass...
|
|
|
|
if ( nResult == MODEM_PENDING )
|
|
{
|
|
DWORD hTempSem;
|
|
|
|
// wait until fclass has been switched
|
|
//------------------------------------
|
|
hPort->VVInfo.hSemaphore = Create_Semaphore( 0L );
|
|
hPort->VVInfo.hTimer = Set_Global_Time_Out( VVTimerCallback, TO_FIRST_CHAR_AFTER_INIT_CMD, (ULONG)hPort );
|
|
|
|
// block until command is done or time out has occured
|
|
MCXPRINTF("waiting for the semaphore (class switch) to complete");
|
|
|
|
Wait_Semaphore( hPort->VVInfo.hSemaphore, BLOCK_ENABLE_INTS );
|
|
if( hPort->VVInfo.hTimer )
|
|
{
|
|
Cancel_Time_Out( hPort->VVInfo.hTimer );
|
|
hPort->VVInfo.hTimer = 0;
|
|
}
|
|
|
|
MCXPRINTF("returned from the semaphore!");
|
|
|
|
// get rid of this semapore
|
|
hTempSem = hPort->VVInfo.hSemaphore;
|
|
hPort->VVInfo.hSemaphore = 0;
|
|
Destroy_Semaphore( hTempSem );
|
|
} // end if
|
|
|
|
return( nResult );
|
|
} // end VVSetClass
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
@doc INTERNAL
|
|
|
|
@api void | VVTimerCallback | This function gets called when a timer
|
|
event happens. This routine is to be used to wait for the switching
|
|
into the correct fclass for a modem.
|
|
|
|
@parm hPort is passed in in edx
|
|
|
|
@rdesc Returns void
|
|
|
|
If another command comes in while we are switching the modem into
|
|
fclass 0 or 80, we will wait for the command to complete before issuing
|
|
the new command.
|
|
|
|
*****************************************************************************/
|
|
void VVTimerCallback( void )
|
|
{
|
|
MODEMINFORMATION *hPort;
|
|
|
|
_asm mov [hPort], edx
|
|
|
|
// clear timer
|
|
hPort->VVInfo.hTimer = 0L;
|
|
|
|
MCXPRINTF("Got VV semaphore timeout!");
|
|
// unblock the waiting command
|
|
if( hPort->VVInfo.hSemaphore )
|
|
{
|
|
Signal_Semaphore( hPort->VVInfo.hSemaphore );
|
|
}
|
|
|
|
return;
|
|
} // end VVTimerCallback
|
|
|
|
#endif // VOICEVIEW
|