| ..back |
// ****************************************************************************
//
// Module: Unimdm
// File: modem.c
//
// Copyright (c) 1992-2000 Microsoft Corporation. All rights reserved.
//
// Revision History
//
// Description: Intermediate modem SPI layer
//
// ****************************************************************************
#include "windows.h"
#include "types.h"
#include "memory.h"
#include "tspi.h"
#include "linklist.h"
#include "tspip.h"
#include "tapicomn.h"
#include "mcx.h"
#include "dial.h"
static const WCHAR szAnswer[] = TEXT("Answer"); // Registry value name under Settings
static const CHAR szDefaultAnswerCmd[] = "ATA\r\n";
static const WCHAR szMonitor[] = TEXT("Monitor"); // Registry value name under Settings
static const CHAR szDefaultMonitorCmd[] = "ATS0=0\r\n";
static const WCHAR szMdmLogBase[] = TEXT("mdmlog");
static const WCHAR szMdmLogExt[] = TEXT(".txt");
extern const WCHAR szSettings[];
extern DWORD SetWatchdog(PTLINEDEV pLineDev,DWORD dwTimeout);
// Unimodem thread priority
extern DWORD g_dwUnimodemThreadPriority;
extern HANDLE g_hCoreDLL;
#ifdef DEBUG
LPWSTR
GetPendingName(
DWORD dwPendingType
)
{
LPWSTR lpszType;
switch (dwPendingType) {
case PENDING_LINEACCEPT: lpszType = TEXT("LINEACCEPT"); break;
case PENDING_LINEANSWER: lpszType = TEXT("LINEANSWER"); break;
case PENDING_LINEDEVSPECIFIC: lpszType = TEXT("LINEDEVSPECIFIC"); break;
case PENDING_LINEDIAL: lpszType = TEXT("LINEDIAL"); break;
case PENDING_LINEDROP: lpszType = TEXT("LINEDROP"); break;
case PENDING_LINEMAKECALL: lpszType = TEXT("LINEMAKECALL"); break;
case PENDING_LISTEN: lpszType = TEXT("LISTEN"); break;
case PENDING_EXIT: lpszType = TEXT("EXIT"); break;
case INVALID_PENDINGOP: lpszType = TEXT("INVALIDOP"); break;
default: lpszType = TEXT("UNKNOWN!!!"); break;
}
return lpszType;
}
#endif
//
// Function to open the modem command log file for the specified line device
//
void
OpenModemLog(
PTLINEDEV pLineDev
)
{
WCHAR FileName[16];
LPWSTR lpwsz;
HANDLE hFile;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+OpenModemLog\n")));
if (pLineDev->hMdmLog != (HANDLE)INVALID_DEVICE) {
CloseHandle(pLineDev->hMdmLog);
}
//
// Format modem log file name "mdmlog<dwDeviceID>.txt"
//
wcscpy(FileName, szMdmLogBase);
lpwsz = FileName + wcslen(FileName);
wsprintf(lpwsz, TEXT("%d"), pLineDev->dwDeviceID);
wcscat(FileName, szMdmLogExt);
hFile = CreateFile(FileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
DEBUGMSG(ZONE_CALLS|ZONE_FUNC|ZONE_ERROR,
(TEXT("UNIMODEM:OpenModemLog CreateFile(%s) failed %d\n"), FileName, GetLastError()));
pLineDev->hMdmLog = (HANDLE)INVALID_DEVICE;
} else {
DEBUGMSG(ZONE_CALLS|ZONE_FUNC, (TEXT("UNIMODEM:OpenModemLog CreateFile(%s) succeeded\n"), FileName));
pLineDev->hMdmLog = hFile;
}
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-OpenModemLog\n")));
} // OpenModemLog
//
// Function to write modem commands to the line device's command history file.
//
void
WriteModemLog(
PTLINEDEV pLineDev,
UCHAR * szCommand,
DWORD dwOp
)
{
CHAR Buf[MAXSTRINGLENGTH];
LPSTR lpsz;
DWORD dwLen;
DWORD dwIO;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+WriteModemLog %a\n"), szCommand));
switch (dwOp) {
case MDMLOG_COMMAND_OK:
lpsz = "Modem Command: ";
break;
case MDMLOG_COMMAND_FAIL:
lpsz = "Failed Command: ";
break;
case MDMLOG_RESPONSE:
lpsz = "Modem Response: ";
break;
default:
lpsz = " ";
break;
}
Buf[MAXSTRINGLENGTH-2] = 0;
strcpy(Buf, lpsz);
dwLen = strlen(Buf);
strncat(Buf, szCommand, MAXSTRINGLENGTH - dwLen - 2);
dwLen += strlen(szCommand);
if (dwLen > (MAXSTRINGLENGTH - 2)) {
dwLen = MAXSTRINGLENGTH - 2;
}
Buf[dwLen] = '\n';
dwLen++;
Buf[dwLen] = '\0';
DEBUGMSG(ZONE_CALLS|ZONE_FUNC, (TEXT("UNIMODEM:WriteModemLog(%a) %d bytes\n"), Buf, dwLen));
if (!WriteFile(pLineDev->hMdmLog, (PUCHAR)Buf, dwLen, &dwIO, NULL)) {
DEBUGMSG(ZONE_CALLS|ZONE_FUNC, (TEXT("UNIMODEM:WriteModemLog WriteFile() failed %d\n"), GetLastError()));
}
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-WriteModemLog\n")));
} // WriteModemLog
void
SetDCBfromDevMiniCfg(
DCB * pDCB,
PDEVMINICFG lpDevMiniCfg
)
{
pDCB->BaudRate = lpDevMiniCfg->dwBaudRate;
pDCB->ByteSize = lpDevMiniCfg->ByteSize;
pDCB->StopBits = lpDevMiniCfg->StopBits;
pDCB->fParity = (NOPARITY == lpDevMiniCfg->Parity) ? FALSE : TRUE;
pDCB->Parity = lpDevMiniCfg->Parity;
pDCB->fDsrSensitivity = FALSE;
pDCB->fDtrControl = DTR_CONTROL_ENABLE;
if( MDM_FLOWCONTROL_HARD & lpDevMiniCfg->dwModemOptions ) {
// Enable RTS/CTS Flow Control
pDCB->fRtsControl = RTS_CONTROL_HANDSHAKE;
pDCB->fOutxCtsFlow = 1;
pDCB->fOutX = 0;
pDCB->fInX = 0;
} else if( MDM_FLOWCONTROL_SOFT & lpDevMiniCfg->dwModemOptions ) {
// Enable XON/XOFF Flow Control
pDCB->fRtsControl = RTS_CONTROL_ENABLE;
pDCB->fOutxCtsFlow = 0;
pDCB->fOutX = 1;
pDCB->fInX = 1;
} else {
pDCB->fRtsControl = RTS_CONTROL_ENABLE;
pDCB->fOutxCtsFlow = 0;
pDCB->fOutX = 0;
pDCB->fInX = 0;
}
} // SetDCBfromDevMiniCfg
void
SetDialerTimeouts(
HANDLE hComDev
)
{
COMMTIMEOUTS commTimeouts;
GetCommTimeouts (hComDev, &commTimeouts);
commTimeouts.ReadIntervalTimeout = 50;
commTimeouts.ReadTotalTimeoutMultiplier = 0;
commTimeouts.ReadTotalTimeoutConstant = 50;
commTimeouts.WriteTotalTimeoutMultiplier = 5;
commTimeouts.WriteTotalTimeoutConstant = 500;
SetCommTimeouts (hComDev, &commTimeouts);
} // SetDialerTimeouts
DWORD
OpenModem (
LPCWSTR lpszPortName,
LPDWORD lpR3handle,
LPDWORD lpR0handle,
PDEVMINICFG lpDevMiniCfg )
{
HANDLE hComDev;
HANDLE hComDev0;
WCHAR szDeviceName[16];
DEBUGMSG(ZONE_FUNC|ZONE_CALLS, (TEXT("UNIMODEM:+OpenModem('%s')\r\n"), lpszPortName));
// Compose the device name from the portname
wcscpy(szDeviceName, lpszPortName);
// Open the device
//
if ((hComDev = CreateFile(szDeviceName, GENERIC_READ | GENERIC_WRITE, 0,
NULL, OPEN_EXISTING, 0, 0))
== INVALID_HANDLE_VALUE) {
goto om_fail;
}
if ((hComDev0 = CreateFile(szDeviceName, 0, 0,
NULL, OPEN_EXISTING, 0, 0))
!= INVALID_HANDLE_VALUE) {
DCB commDCB;
DEBUGMSG(ZONE_MISC|ZONE_CALLS,
(TEXT("UNIMODEM:OpenModem - createfile OK, handle x%X\n"),
hComDev));
// ********************************************************
// Set the device configuration based on DEVMINICFG
//
GetCommState( hComDev, &commDCB );
SetDCBfromDevMiniCfg(&commDCB, lpDevMiniCfg);
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("UNIMODEM:OpenModem Setting port configuration :\n")));
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("UNIMODEM:OpenModem Baud %d, Byte Size %d, Stop bits %d, Parity %d\n"),
commDCB.BaudRate, commDCB.ByteSize, commDCB.StopBits, commDCB.Parity));
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("UNIMODEM:OpenModem RTS Control %d, CTS Out Flow %d, XON/XOFF out/in %d/%d\n"),
commDCB.fRtsControl, commDCB.fOutxCtsFlow, commDCB.fOutX, commDCB.fInX));
SetCommState( hComDev, &commDCB );
// Adjust the read/write timeouts as required by dialer thread
SetDialerTimeouts(hComDev);
*lpR3handle = (DWORD)hComDev;
*lpR0handle = (DWORD)hComDev0;
DEBUGMSG(ZONE_FUNCTION, (TEXT("UNIMODEM:-OpenModem\n")));
return ERROR_SUCCESS;
} else {
CloseHandle(hComDev);
}
om_fail:
DEBUGMSG(ZONE_FUNCTION,
(TEXT("UNIMODEM:-OpenModem : Unable to open %s, hComdev x%X\n"),
szDeviceName, hComDev));
return ERROR_OPEN_FAILED;
}
// ****************************************************************************
// lineOpen()
//
// Function: Emulate the TAPI lineOpen call.
//
// ****************************************************************************
LONG
DevlineOpen (
PTLINEDEV pLineDev
)
{
DWORD idMdm;
DWORD idMdm0;
DWORD dwRet;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+DevlineOpen\r\n")));
EnterCriticalSection(&pLineDev->OpenCS);
// The line must be closed
if (pLineDev->hDevice != (HANDLE)INVALID_DEVICE) {
DEBUGMSG(ZONE_ERROR, (TEXT("UNIMODEM:DevlineOpen - device already open\r\n")));
dwRet = LINEERR_ALLOCATED;
goto exit_Point;
}
if (0 == pLineDev->wDeviceAvail) {
DEBUGMSG(ZONE_ERROR, (TEXT("UNIMODEM:DevlineOpen - device has been removed\r\n")));
dwRet = LINEERR_NODEVICE;
goto exit_Point;
}
// Open the modem port
DEBUGMSG(ZONE_MISC, (TEXT("UNIMODEM:DevlineOpen - calling openmodem\r\n")));
dwRet = OpenModem(pLineDev->szDeviceName,
&idMdm, &idMdm0,
&pLineDev->DevMiniCfg);
if (dwRet == SUCCESS) {
// we successfully opened the modem
// Convert modem handle to ring 0 handle
pLineDev->hDevice = (HANDLE)idMdm;
pLineDev->hDevice_r0 = (HANDLE)idMdm0;
pLineDev->DevState = DEVST_DISCONNECTED;
OpenModemLog(pLineDev);
} else {
dwRet = LINEERR_RESOURCEUNAVAIL;
};
exit_Point:
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-DevlineOpen x%X\r\n"), dwRet));
return dwRet;
}
//****************************************************************************
// lineClose()
//
// Function: Emulate the TAPI lineClose call.
//
// NOTE : The fDoDrop flag is used to indicate that we should
// make sure the line gets dropped before we close it.
//
//****************************************************************************
LONG
DevlineClose (
PTLINEDEV pLineDev,
BOOL fDoDrop
)
{
DWORD dwPrevCallState;
HTAPICALL hPrevCall;
DWORD dwPrevProcPerms;
DEBUGMSG(ZONE_FUNC|ZONE_CALLS,
(TEXT("UNIMODEM:+DevlineClose, handle x%X, callstate x%X\r\n"),
pLineDev->hDevice, pLineDev->dwCallState));
// Grab a critical section to ensure the app doesn't call in and
// cause a line close while we are in the middle of doing a close
// ourselves.
EnterCriticalSection(&pLineDev->OpenCS);
dwPrevCallState = pLineDev->dwCallState;
hPrevCall = pLineDev->htCall;
if( (HANDLE)INVALID_DEVICE != pLineDev->hDevice )
{
try
{
//
// We (device.exe PSL) were the current process when the device
// was opened, so we need to be the owner for DevlineDrop and
// the CloseHandle() to work as expected.
//
dwPrevProcPerms = SetProcPermissions(-1);
SetHandleOwner((HANDLE) pLineDev->hDevice, GetCurrentProcess());
SetProcPermissions(dwPrevProcPerms);
// If a call is in progress, terminate it
if (fDoDrop && LINECALLSTATE_CONNECTED == pLineDev->dwCallState) {
DEBUGMSG(ZONE_CALLS, (L"UNIMODEM:DevlineClose - Callstate x%X, dropping call\r\n",
pLineDev->dwCallState));
DevlineDrop(pLineDev);
}
CloseHandle( pLineDev->hDevice );
CloseHandle( pLineDev->hDevice_r0);
CloseHandle( pLineDev->hMdmLog);
NullifyLineDevice(pLineDev, FALSE); // Reinit the line device
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
// Oops, we must have hit a bad handle. Fall thru & release the CS.
}
}
pLineDev->dwPendingID = INVALID_PENDINGID;
LeaveCriticalSection(&pLineDev->OpenCS);
if (dwPrevCallState != LINECALLSTATE_IDLE) {
pLineDev->htCall = hPrevCall;
NewCallState(pLineDev, LINECALLSTATE_IDLE, 0L);
pLineDev->htCall = NULL;
}
DEBUGMSG(ZONE_FUNC|ZONE_CALLS, (TEXT("UNIMODEM:-DevlineClose\r\n")));
return SUCCESS;
}
//
// Function to signal any threads stuck in WaitCommEvent
//
LONG
ToggleCommMask(
PTLINEDEV pLineDev
)
{
LONG rc = 0;
if ((HANDLE)INVALID_DEVICE != pLineDev->hDevice) {
try {
if (!SetCommMask( pLineDev->hDevice, 0)) {
DEBUGMSG(ZONE_ERROR, (TEXT("UNIMODEM:ToggleCommMask SetCommMask(0) failed %d\n"), GetLastError()));
rc = LINEERR_OPERATIONFAILED;
}
if (!SetCommMask( pLineDev->hDevice_r0, 0)) {
DEBUGMSG(ZONE_ERROR, (TEXT("UNIMODEM:ToggleCommMask SetCommMask(r0,0) failed %d\n"), GetLastError()));
rc = LINEERR_OPERATIONFAILED;
}
if (!SetCommMask( pLineDev->hDevice, EV_DEFAULT)) {
DEBUGMSG(ZONE_ERROR, (TEXT("UNIMODEM:ToggleCommMask SetCommMask() failed %d\n"), GetLastError()));
rc = LINEERR_OPERATIONFAILED;
}
if (!SetCommMask( pLineDev->hDevice_r0, EV_RLSD)) {
DEBUGMSG(ZONE_ERROR, (TEXT("UNIMODEM:ToggleCommMask SetCommMask(r0) failed %d\n"), GetLastError()));
rc = LINEERR_OPERATIONFAILED;
}
} except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
// Exception means bad handle, so we can't do anything to cancel call
rc = LINEERR_OPERATIONFAILED;
}
}
return rc;
} // ToggleCommMask
typedef LRESULT (WINAPI * PFN_SENDMESSAGE)(HWND,UINT,WPARAM,LPARAM);
void
DoHangUp(
PTLINEDEV pLineDev
)
{
DCB commDCB, origDCB;
DWORD dwWaitReturn;
DWORD dwLen;
static const UCHAR szHangup[] = "\rATH\r\nATZ\r\n";
// If a TermCtrl is displayed, get rid of it.
if( pLineDev->hwTermCtrl )
{
PFN_SENDMESSAGE pfnSendMessage;
if (pfnSendMessage = (PFN_SENDMESSAGE)GetProcAddress(g_hCoreDLL, L"SendMessageW")) {
// send a WM_CLOSE to the handle
DEBUGMSG(ZONE_CALLS,
(TEXT("UNIMODEM:DoHangUp - WM_CLOSE TermWnd\r\n")));
pfnSendMessage (pLineDev->hwTermCtrl, WM_CLOSE, 0, 0);
}
}
ToggleCommMask(pLineDev);
// We don't want to finish up before the dialer thread has
// done his thing. So wait for the CallComplete event that
// the thread sets before exiting. Lets assume he'll finish
// within 4 seconds or we just give up.
dwWaitReturn = WaitForSingleObject(pLineDev->hCallComplete, 4*1000);
#ifdef DEBUG
if( WAIT_TIMEOUT == dwWaitReturn )
DEBUGMSG(ZONE_CALLS | ZONE_ERROR,
(TEXT("UNIMODEM:DoHangUp - timeout waiting for call complete\r\n")));
#endif
// Drop the line, aborting any call in progress.
if( (HANDLE)INVALID_DEVICE != pLineDev->hDevice )
{
try
{
// Make sure the modem hangs up, etc.
if( ! IS_NULL_MODEM(pLineDev) )
{
DEBUGMSG(ZONE_CALLS,
(TEXT("UNIMODEM:DoHangUp - hanging up\r\n")));
if( (HANDLE)INVALID_DEVICE != pLineDev->hDevice )
{
GetCommState( pLineDev->hDevice, &commDCB );
origDCB = commDCB;
commDCB.fRtsControl = RTS_CONTROL_ENABLE;
commDCB.fOutxCtsFlow = 0;
SetCommState( pLineDev->hDevice, &commDCB );
dwLen = 0;
PurgeComm(pLineDev->hDevice, PURGE_RXCLEAR|PURGE_TXCLEAR);
WriteFile(pLineDev->hDevice, (LPVOID)szHangup, strlen((LPVOID)szHangup), &dwLen, 0 );
WriteModemLog(pLineDev, (PUCHAR)szHangup, MDMLOG_COMMAND_OK);
PurgeComm(pLineDev->hDevice, PURGE_RXCLEAR|PURGE_TXCLEAR);
RETAILMSG (dwLen != strlen((LPVOID)szHangup),
(TEXT("UNIMODEM:!!!DoHangUp wrote %d of %d byte dial string\r\n"),
dwLen, strlen((LPVOID)szHangup)) );
commDCB.fRtsControl = RTS_CONTROL_DISABLE;
commDCB.fDtrControl = DTR_CONTROL_DISABLE;
SetCommState( pLineDev->hDevice, &commDCB );
Sleep( 100 );
SetCommState( pLineDev->hDevice, &origDCB );
}
DEBUGMSG(ZONE_CALLS, (TEXT("UNIMODEM:DoHangUp - hangup done\r\n")));
}
}
except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
// Exception means bad handle, so we can't do anything
}
}
#ifdef DEBUG
else
{
DEBUGMSG(ZONE_CALLS | ZONE_ERROR,
(TEXT("UNIMODEM:DoHangUp - handle invalid. Can't cancel call\r\n")));
}
#endif
}
//****************************************************************************
// DevlineDrop()
//
// Function: Emulate the TAPI lineDrop call.
//
//****************************************************************************
LONG
DevlineDrop (
PTLINEDEV pLineDev
)
{
DEBUGMSG(ZONE_FUNC|ZONE_CALLS,
(TEXT("UNIMODEM:+DevlineDrop\r\n")));
EnterCriticalSection(&pLineDev->OpenCS);
// Don't unnecessarily attempt to drop
if ((LINECALLSTATE_DISCONNECTED == pLineDev->dwCallState ) || (LINECALLSTATE_IDLE == pLineDev->dwCallState )) {
switch(pLineDev->DevState) {
case DEVST_DISCONNECTED:
case DEVST_PORTLISTENINIT:
case DEVST_PORTLISTENING:
pLineDev->DevState = DEVST_DISCONNECTED;
LeaveCriticalSection(&pLineDev->OpenCS);
SetAsyncStatus(pLineDev, 0);
DEBUGMSG(ZONE_FUNC|ZONE_CALLS,
(TEXT("UNIMODEM:-DevlineDrop, line already dropped\r\n")));
return SUCCESS;
}
}
pLineDev->dwCallFlags |= CALL_DROPPING; // Flag that this call is going away
pLineDev->dwCallFlags &= ~CALL_ACTIVE;
// Do we need to do a hangup?
if (LINECALLSTATE_IDLE == pLineDev->dwCallState &&
DEVST_DISCONNECTED == pLineDev->DevState)
{
DEBUGMSG(ZONE_CALLS, (TEXT("UNIMODEM:DevlineDrop - no action required\r\n")));
SetAsyncStatus(pLineDev, 0);
}
else
{
DoHangUp(pLineDev);
};
if (pLineDev->dwCallFlags & CALL_ALLOCATED) {
DEBUGMSG(ZONE_CALLS, (TEXT("UNIMODEM:DevlineDrop - sending LINECALLSTATE_IDLE\n")));
NewCallState(pLineDev, LINECALLSTATE_IDLE, 0L);
}
pLineDev->dwNumRings = 0;
pLineDev->DevState = DEVST_DISCONNECTED;
pLineDev->dwCallState = LINECALLSTATE_IDLE;
pLineDev->dwCallFlags = 0;
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_FUNC|ZONE_CALLS, (TEXT("UNIMODEM:-DevlineDrop\r\n")));
return SUCCESS;
}
LONG
DoDevlineDrop(
PTLINEDEV pLineDev
)
{
LONG rc;
rc = DevlineDrop(pLineDev);
SetCommMask(pLineDev->hDevice, EV_DEFAULT);
SetAsyncStatus(pLineDev, 0);
return rc;
}
void
FreeNextCmd(
PTLINEDEV pLineDev
)
{
if (pLineDev->lpstrNextCmd) {
TSPIFree(pLineDev->lpstrNextCmd);
pLineDev->lpstrNextCmd = NULL;
}
}
//
// Wait for a disconnect on a connected modem. Send a LINE_CALLSTATE
// upon disconnect.
//
void
WatchForDisconnect(
PTLINEDEV pLineDev
)
{
DWORD Mask;
DWORD ModemStatus;
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS, (TEXT("UNIMODEM:+WatchForDisconnect\n")));
DEBUGMSG(ZONE_CALLS, (L"UNIMODEM:WatchForDisconnect: Call(0x%x) is %s, CallState(0x%x) is %s\n",
pLineDev->dwCallFlags, (pLineDev->dwCallFlags & CALL_ACTIVE) ? L"ACTIVE" : L"NOT ACTIVE",
pLineDev->dwCallState, (pLineDev->dwCallState == LINECALLSTATE_CONNECTED) ? L"CONNECTED" : L"NOT CONNECTED"));
SetCommMask(pLineDev->hDevice_r0, EV_RLSD);
while ((pLineDev->dwCallState == LINECALLSTATE_CONNECTED) && (pLineDev->dwCallFlags & CALL_ACTIVE)) {
if (WaitCommEvent(pLineDev->hDevice_r0, &Mask, NULL )) {
//
// Remote disconnect detection
//
if (Mask & EV_RLSD) {
if (GetCommModemStatus(pLineDev->hDevice_r0, &ModemStatus)) {
if (!(ModemStatus & MS_RLSD_ON)) {
DEBUGMSG(ZONE_CALLS, (TEXT("UNIMODEM:WatchForDisconnect RLSD went low\n")));
DoHangUp(pLineDev);
pLineDev->DevState = DEVST_DISCONNECTED;
pLineDev->dwCallFlags &= ~CALL_ACTIVE;
pLineDev->dwNumRings = 0;
NewCallState(pLineDev, LINECALLSTATE_DISCONNECTED, LINEDISCONNECTMODE_NORMAL);
NewCallState(pLineDev, LINECALLSTATE_IDLE, 0L);
break;
}
} else {
DEBUGMSG(ZONE_CALLS, (TEXT("UNIMODEM:WatchForDisconnect GetCommModemStatus failed %d\n"), GetLastError()));
break;
}
}
//
// See if we got signalled to drop via TSPI_lineDrop
//
if ((Mask == 0) || (pLineDev->dwCallFlags & CALL_DROPPING)) {
break;
}
} else {
DEBUGMSG(ZONE_CALLS, (TEXT("UNIMODEM:WatchForDisconnect GetCommEvent failed %d\n"), GetLastError()));
break;
}
}
DEBUGMSG(ZONE_ASYNC, (TEXT("UNIMODEM:WatchForDisconnect Current Op=%s\n"), GetPendingName(pLineDev->dwPendingType)));
if (pLineDev->dwPendingType != PENDING_LINEDROP) {
DoDevlineDrop(pLineDev);
}
DEBUGMSG(ZONE_CALLS|ZONE_FUNCTION, (TEXT("UNIMODEM:-WatchForDisconnect\n")));
} // WatchForDisconnect
//
// Process a RING response from the modem
//
LONG
ProcessModemRing(
PTLINEDEV pLineDev
)
{
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+ProcessModemRing\n")));
if (pLineDev->dwNumRings == 0) {
if (SetWatchdog(pLineDev, 25000)) {
DEBUGMSG(ZONE_ERROR, (TEXT("UNIMODEM:ProcessModemRing - SetWatchDog failed!!\n")));
}
pLineDev->dwCallFlags = CALL_INBOUND;
pLineDev->dwCallState = LINECALLSTATE_OFFERING;
pLineDev->DevState = DEVST_PORTLISTENOFFER;
CallLineEventProc(
pLineDev,
0,
LINE_NEWCALL,
(DWORD)pLineDev, // HDRVCALL
(DWORD)&pLineDev->htCall, // LPHTAPICALL
0);
pLineDev->dwCurMediaModes = pLineDev->dwDefaultMediaModes | LINEMEDIAMODE_UNKNOWN;
NewCallState(pLineDev, LINECALLSTATE_OFFERING, 0);
}
CallLineEventProc(
pLineDev,
0,
LINE_LINEDEVSTATE,
LINEDEVSTATE_RINGING,
0,
pLineDev->dwNumRings);
pLineDev->dwNumRings++;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ProcessModemRing\n")));
return 0;
} // ProcessModemRing
//
// Process a CARRIER, PROTOCOL or PROGRESS response from the modem
//
LONG
ProcessModemProceeding(
PTLINEDEV pLineDev
)
{
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+ProcessModemProceeding\n")));
if ((pLineDev->DevState == DEVST_PORTLISTENING) || (pLineDev->DevState == DEVST_PORTLISTENOFFER)) {
SetAsyncStatus(pLineDev, 0);
}
NewCallState(pLineDev, LINECALLSTATE_PROCEEDING, 0);
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ProcessModemProceeding\n")));
return 0;
} // ProcessModemProceeding
//
// Process a CONNECT response from the modem
//
LONG
ProcessModemConnect(
PTLINEDEV pLineDev
)
{
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+ProcessModemConnect\n")));
SetWatchdog( pLineDev, 0 ); // Cancel watchdog
pLineDev->dwCallFlags |= CALL_ACTIVE;
if ((pLineDev->DevState == DEVST_PORTLISTENING) || (pLineDev->DevState == DEVST_PORTLISTENOFFER)) {
pLineDev->DevState = DEVST_CONNECTED;
SetAsyncStatus(pLineDev, 0);
}
NewCallState(pLineDev, LINECALLSTATE_CONNECTED, 0);
//
// Now watch the comm state to notify of disconnect
//
WatchForDisconnect(pLineDev);
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ProcessModemConnect\n")));
return 0;
} // ProcessModemConnect
//
// Process a SUCCESS response from the modem
//
LONG
ProcessModemSuccess(
PTLINEDEV pLineDev
)
{
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+ProcessModemSuccess\n")));
if ((pLineDev->DevState == DEVST_PORTLISTENING) || (pLineDev->DevState == DEVST_PORTLISTENOFFER)) {
SetAsyncStatus(pLineDev, 0);
}
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ProcessModemSuccess\n")));
return 0;
} // ProcessModemSuccess
extern const UCHAR pchDCCResp[];
//
// Process a FAILURE response from the modem
//
// SetCommMask(0) is used to signal UnimodemControlThread when some new command
// has been requested. This gets interpreted by MdmGetResponse as a
// MODEM_FAILURE.
//
LONG
ProcessModemFailure(
PTLINEDEV pLineDev
)
{
WCHAR szTemp[256];
DWORD dwSize;
LONG rc = 0;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+ProcessModemFailure\n")));
pmf_next:
switch (pLineDev->dwPendingType) {
case PENDING_LINEANSWER:
{
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:ProcessModemFailure PENDING_LINEANSWER\n")));
if (SetWatchdog(pLineDev, 25000)) {
return 1;
}
pLineDev->dwCallFlags |= CALL_ALLOCATED;
if (IS_NULL_MODEM(pLineDev)) {
pLineDev->lpstrNextCmd = TSPIAlloc(strlen(pchDCCResp)+1); // answer DCC w/"CLIENTSERVER"
if (pLineDev->lpstrNextCmd) {
strcpy(pLineDev->lpstrNextCmd, pchDCCResp);
}
} else {
if (MdmRegGetValue(
pLineDev,
szSettings,
szAnswer,
REG_SZ,
(PUCHAR)szTemp,
&dwSize) == ERROR_SUCCESS) {
pLineDev->lpstrNextCmd = MdmConvertCommand(szTemp, NULL, NULL);
} else {
pLineDev->lpstrNextCmd = TSPIAlloc(sizeof(szDefaultAnswerCmd));
if (pLineDev->lpstrNextCmd) {
strcpy(pLineDev->lpstrNextCmd, szDefaultAnswerCmd);
}
}
}
}
break;
case PENDING_EXIT:
{
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:ProcessModemFailure PENDING_EXIT\n")));
return 2;
}
break;
case PENDING_LINEMAKECALL:
case PENDING_LINEDIAL:
{
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:ProcessModemFailure PENDING_LINEDIAL/PENDING_LINEMAKECALL\n")));
Dialer(pLineDev);
switch (pLineDev->dwCallState) {
case LINECALLSTATE_CONNECTED:
WatchForDisconnect(pLineDev);
goto pmf_next;
case LINECALLSTATE_DIALING:
MdmGetResponse(pLineDev, "listening", NOCS);
goto pmf_next;
}
}
break;
case PENDING_LINEDROP:
{
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:ProcessModemFailure PENDING_LINEDROP\n")));
rc = DoDevlineDrop(pLineDev);
}
break;
case PENDING_LISTEN:
{
// The WatchDog that was set after the first RING has timed out
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:ProcessModemFailure PENDING_LISTEN\n")));
rc = DevlineDrop(pLineDev);
SetCommMask(pLineDev->hDevice, EV_DEFAULT);
}
break;
default:
{
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:ProcessModemFailure default case %d\n"), pLineDev->dwPendingType));
return 3;
}
} // switch
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ProcessModemFailure %d\n"), rc));
return rc;
} // ProcessModemFailure
BOOL
GetMonitorCommand(
PTLINEDEV pLineDev
)
{
LPWSTR lpszTemp;
DWORD dwSize;
//
// For manual dial modems, don't listen for incoming calls
//
if (pLineDev->DevMiniCfg.fwOptions & MANUAL_DIAL) {
FreeNextCmd(pLineDev);
return TRUE;
}
if (MdmRegGetValue(
pLineDev,
szSettings,
szMonitor,
REG_SZ,
NULL,
&dwSize) == ERROR_SUCCESS) {
lpszTemp = TSPIAlloc(dwSize);
if (!lpszTemp) {
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:GetMonitorCommand - Monitor cmd alloc failed 1\n")));
goto gmc_DefaultMonitorCmd;
}
if (MdmRegGetValue(
pLineDev,
szSettings,
szMonitor,
REG_SZ,
(PUCHAR)lpszTemp,
&dwSize) == ERROR_SUCCESS) {
pLineDev->lpstrNextCmd = MdmConvertCommand(lpszTemp, NULL, NULL);
TSPIFree(lpszTemp);
} else {
goto gmc_DefaultMonitorCmd;
}
} else {
gmc_DefaultMonitorCmd:
pLineDev->lpstrNextCmd = TSPIAlloc(strlen(szDefaultMonitorCmd)+1);
if (!pLineDev->lpstrNextCmd) {
DEBUGMSG(ZONE_THREAD, (TEXT("UNIMODEM:GetMonitorCommand - Monitor cmd alloc failed 2\n")));
return FALSE;
}
strcpy(pLineDev->lpstrNextCmd, szDefaultMonitorCmd);
}
return TRUE;
}
BOOL
DoModemInit(
PTLINEDEV pLineDev
)
{
DCB commDCB;
BOOL bRet;
//
// Turn off hardware flow control while sending commands.
//
GetCommState( pLineDev->hDevice, &commDCB );
commDCB.fRtsControl = RTS_CONTROL_ENABLE;
commDCB.fOutxCtsFlow = 0;
SetCommState( pLineDev->hDevice, &commDCB );
bRet = ModemInit(pLineDev);
//
// Restore original settings
//
SetDCBfromDevMiniCfg(&commDCB, &(pLineDev->DevMiniCfg));
SetCommState( pLineDev->hDevice, &commDCB );
return bRet;
}
//
// Send commands and listen for responses until the line is closed
//
DWORD
UnimodemControlThread(
PTLINEDEV pLineDev
)
{
MODEMRESPCODES MdmResp;
DWORD rc;
BOOL bPendingOp;
HTAPICALL hPrevCall;
EnterCriticalSection(&pLineDev->OpenCS);
bPendingOp = !((PENDING_LISTEN == pLineDev->dwPendingType) || (INVALID_PENDINGOP == pLineDev->dwPendingType));
DEBUGMSG(ZONE_FUNC|ZONE_THREAD, (TEXT("+UnimodemControlThread(%d)\n"), pLineDev->dwDeviceID));
if (bPendingOp) {
DEBUGMSG(ZONE_THREAD, (L"UnimodemControlThread(%d) PendingOp=%s\n",
pLineDev->dwDeviceID, GetPendingName(pLineDev->dwPendingType)));
}
if (pLineDev->dwPendingType == PENDING_LINEMAKECALL) {
hPrevCall = pLineDev->htCall;
}
rc = DevlineOpen(pLineDev);
if ((rc) && (rc != LINEERR_ALLOCATED)) {
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) DevlineOpen failed\n"), pLineDev->dwDeviceID));
goto uct_endCS;
}
FreeNextCmd(pLineDev);
if (IS_NULL_MODEM(pLineDev)) {
SetCommMask(pLineDev->hDevice, EV_DEFAULT);
} else {
//
// Don't init a manual dial modem
//
if (pLineDev->DevMiniCfg.fwOptions & MANUAL_DIAL) {
SetCommMask(pLineDev->hDevice, EV_DEFAULT);
} else {
//
// If modeminit fails, we sure won't be able to anything!
//
if (!DoModemInit(pLineDev)) {
pLineDev->DevState = DEVST_DISCONNECTED;
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) ModemInit failed\n"), pLineDev->dwDeviceID));
goto uct_endCS;
}
}
}
LeaveCriticalSection(&pLineDev->OpenCS);
if (pLineDev->dwPendingType == PENDING_LINEMAKECALL) {
pLineDev->DevState = DEVST_PORTCONNECTDIAL;
ProcessModemFailure(pLineDev);
} else {
pLineDev->DevState = DEVST_PORTLISTENING;
if (!IS_NULL_MODEM(pLineDev)) {
//
// Not making a call so monitor the line for incoming calls.
//
if (!GetMonitorCommand(pLineDev)) {
goto uct_end;
}
}
}
bPendingOp = FALSE;
MdmResp = MODEM_FAILURE;
uct_monitor:
//
// Monitor modem responses
//
while (pLineDev->DevState != DEVST_DISCONNECTED) {
if ((pLineDev->DevState == DEVST_PORTLISTENING) || (pLineDev->DevState == DEVST_PORTLISTENOFFER)) {
if (pLineDev->lpstrNextCmd) {
if (!MdmSendCommand(pLineDev, pLineDev->lpstrNextCmd)) {
goto uct_end;
}
}
EnterCriticalSection(&pLineDev->OpenCS);
//
// If the user did an open/close in quick succession then check for it.
//
switch (pLineDev->dwPendingType) {
case PENDING_EXIT:
MdmResp = MODEM_EXIT;
goto uct_endCS;
break;
case PENDING_LINEMAKECALL:
pLineDev->DevState = DEVST_PORTCONNECTDIAL;
MdmResp = MODEM_FAILURE;
LeaveCriticalSection(&pLineDev->OpenCS);
goto uct_process;
break;
}
if (pLineDev->lpstrNextCmd) {
if (IS_NULL_MODEM(pLineDev)) {
//
// We only get here if we just responded with "CLIENTSERVER" to someone's "CLIENT",
// so no further responses are needed. The connection is established.
//
MdmResp = MODEM_CONNECT;
SetEvent(pLineDev->hCallComplete);
LeaveCriticalSection(&pLineDev->OpenCS);
} else {
//
// Some modems don't respond to the monitor command until an incoming call is made,
// but some modems do respond to the monitor command so we wait for a MODEM_RING response.
//
if ((MdmResp = MdmGetResponse(pLineDev, pLineDev->lpstrNextCmd, LEAVECS)) == MODEM_SUCCESS) {
if (pLineDev->dwPendingType == PENDING_LISTEN) {
MdmResp = MdmGetResponse(pLineDev, pLineDev->lpstrNextCmd, NOCS);
}
}
}
} else {
PurgeComm(pLineDev->hDevice, PURGE_RXCLEAR|PURGE_TXCLEAR);
//
// DCC server has no specific monitor command.
// Also for manual dial, this thread needs to be in a WaitCommEvent
//
MdmResp = MdmGetResponse(pLineDev, "listening", LEAVECS);
}
}
uct_process:
FreeNextCmd(pLineDev);
switch (MdmResp) {
case MODEM_RING:
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) MODEM_RING\n"), pLineDev->dwDeviceID));
ProcessModemRing(pLineDev);
break;
case MODEM_CARRIER:
case MODEM_PROTOCOL:
case MODEM_PROGRESS:
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) LINECALLSTATE_PROCEEDING\n"), pLineDev->dwDeviceID));
ProcessModemProceeding(pLineDev);
break;
case MODEM_CONNECT:
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) LINECALLSTATE_CONNECTED\n"), pLineDev->dwDeviceID));
ProcessModemConnect(pLineDev);
break;
case MODEM_SUCCESS:
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) MODEM_SUCCESS\n"), pLineDev->dwDeviceID));
ProcessModemSuccess(pLineDev);
break;
case MODEM_FAILURE:
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) MODEM_FAILURE\n"), pLineDev->dwDeviceID));
if (ProcessModemFailure(pLineDev)) {
goto uct_end;
}
break;
case MODEM_EXIT:
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) MODEM_EXIT\n"), pLineDev->dwDeviceID));
goto uct_end;
break;
} // switch
MdmResp = MODEM_FAILURE;
} // while
uct_end:
//
// Go back to monitoring for incoming calls if needed
//
if (pLineDev->dwDetMediaModes) {
//
// Make sure the handle is still valid.
//
if (SetCommMask(pLineDev->hDevice, EV_DEFAULT)) {
pLineDev->DevState = DEVST_PORTLISTENING;
if (IS_NULL_MODEM(pLineDev)) {
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) Continue monitoring\n"), pLineDev->dwDeviceID));
goto uct_monitor;
}
if (GetMonitorCommand(pLineDev)) {
pLineDev->dwNumRings = 0;
EnterCriticalSection(&pLineDev->OpenCS);
if (pLineDev->dwPendingStatus == LINEERR_ALLOCATED) {
SetAsyncStatus(pLineDev, LINEERR_OPERATIONFAILED);
}
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_THREAD, (TEXT("UnimodemControlThread(%d) Continue monitoring\n"), pLineDev->dwDeviceID));
goto uct_monitor;
}
}
}
EnterCriticalSection(&pLineDev->OpenCS);
//
// Special case for PASSTHROUGH connections
//
if (pLineDev->fTakeoverMode) {
pLineDev->bControlThreadRunning = FALSE;
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_THREAD|ZONE_FUNC, (TEXT("-UnimodemControlThread(%d) - PASSTHROUGH\n"), pLineDev->dwDeviceID));
return 0;
}
uct_endCS:
FreeNextCmd(pLineDev);
// Make sure that we do not leave anything open
SetWatchdog(pLineDev, 0 ); // Cancel watchdog
DevlineClose(pLineDev, TRUE);
pLineDev->bControlThreadRunning = FALSE;
LeaveCriticalSection(&pLineDev->OpenCS);
if (bPendingOp) {
if (pLineDev->dwPendingType == PENDING_LINEMAKECALL) {
pLineDev->htCall = hPrevCall;
NewCallState(pLineDev, LINECALLSTATE_DISCONNECTED, 0L);
pLineDev->htCall = NULL;
}
SetAsyncStatus(pLineDev, LINEERR_OPERATIONFAILED);
}
DEBUGMSG(ZONE_THREAD|ZONE_FUNC, (TEXT("-UnimodemControlThread(%d)\n"), pLineDev->dwDeviceID));
return 0;
} // UnimodemControlThread
LONG
StartControlThread(
PTLINEDEV pLineDev
)
{
HANDLE hThd;
LONG rc;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+StartControlThread\n")));
rc = 0;
if (pLineDev->bControlThreadRunning == FALSE) {
pLineDev->bControlThreadRunning = TRUE;
hThd = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)UnimodemControlThread,
pLineDev, 0, NULL );
if (!hThd) {
NKDbgPrintfW( TEXT("Unable to Create UnimodemControlThread\n") );
pLineDev->bControlThreadRunning = FALSE;
rc = LINEERR_OPERATIONFAILED;
} else {
CeSetThreadPriority(hThd, g_dwUnimodemThreadPriority);
CloseHandle(hThd);
}
}
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-StartControlThread returning %x\n"), rc));
return rc;
} // StartControlThread
LONG
SignalControlThread(
PTLINEDEV pLineDev
)
{
LONG rc;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+SignalControlThread\n")));
//
// Signal UnimodemListenThread
//
rc = ToggleCommMask(pLineDev);
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-SignalControlThread returning %x\n"), rc));
return rc;
} // SignalControlThread
//
// All TSPI_line* functions that need access to the modem go through here.
// The UnimodemControlThread sends the actual commands and processes the
// responses
//
LONG
ControlThreadCmd(
PTLINEDEV pLineDev,
DWORD dwPendingOP,
DWORD dwPendingID
)
{
LONG rc;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+ControlThreadCmd\n")));
// Fail current outstanding async request (if any) and remember this one
SetAsyncOp(pLineDev, dwPendingOP);
EnterCriticalSection(&pLineDev->OpenCS);
if (pLineDev->bControlThreadRunning) {
switch (dwPendingOP) {
case PENDING_LINEDROP:
case PENDING_EXIT:
case PENDING_LINEANSWER:
case PENDING_LINEDIAL:
case PENDING_LINEMAKECALL:
case PENDING_LISTEN:
rc = SignalControlThread(pLineDev);
break;
default:
rc = 0;
break;
}
} else {
switch (dwPendingOP) {
case PENDING_LINEMAKECALL:
case PENDING_LISTEN:
rc = StartControlThread(pLineDev);
break;
case PENDING_EXIT:
default:
rc = LINEERR_OPERATIONFAILED;
break;
}
}
if (rc == 0) {
switch (dwPendingOP) {
case PENDING_LINEANSWER:
case PENDING_LINEDIAL:
case PENDING_LINEDROP:
case PENDING_LINEMAKECALL:
rc = dwPendingID;
break;
}
}
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ControlThreadCmd returning %x\n"), rc));
return rc;
} // ControlThreadCmd
Legal Declaration: it is for studying wince(MicroSoft Windows CE) only! : http://www.vxworks6.com