Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1325 lines
38 KiB

//****************************************************************************
//
// Module: Unimdm
// File: mdmasyn.c
//
// Copyright (c) 1992-1995, Microsoft Corporation, all rights reserved
//
// Revision History
//
//
// 5/4/95 Viroon Touranachun Moved from modem.c
//
//
// Description: Asynchronous thread entry and state machine
//
//****************************************************************************
#include "unimdm.h"
#include "umdmspi.h"
typedef struct tagMdmThrd {
struct tagMdmThrd* pNext;
HANDLE hThrd;
DWORD tid;
} MDMTHRD, *PMDMTHRD;
// Global asynchronous elements
//
PMDMTHRD gpMdmThrdList = NULL;
HANDLE ghCompletionPort = NULL;
extern MDMLIST gMdmList;
void MdmCompleteAsync (PLINEDEV pLineDev, DWORD dwStatus, DWORD dwAsyncID);
void HandleMdmError (PLINEDEV pLineDev, DWORD dwStatus);
DWORD DetectDialtone (PLINEDEV pLineDev);
VOID WINAPI
ProcessRings(
PLINEDEV pLineDev,
DWORD Type
);
//****************************************************************************
// DWORD InitializeMdmThreads()
//
// Function: Initialize threads to handle modem's asynchronous operations.
//
// History:
// Mon 17-Apr-1995 11:49:53 -by- Viroon Touranachun [viroont]
// Ported from Win95.
//****************************************************************************/
DWORD InitializeMdmThreads()
{
PMDMTHRD pMdmThrd;
DWORD dwRet = ERROR_SUCCESS; // assume success
SYSTEM_INFO systeminfo;
DWORD dwNumThreadsRunning = 0;
// We are going to create a thread per processor, so get the system info
// which contains the number of processors in the system.
//
GetSystemInfo(&systeminfo);
// Create the completion port. This starts without any file handles being
// associated with it.
//
ASSERT(ghCompletionPort == NULL);
ghCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
NULL,
0,
0);
if (ghCompletionPort == NULL)
{
dwRet = GetLastError();
DPRINTF1("CreateIoCompletionPort failed with %d", dwRet);
}
else
{
// Create the threads.
//
while (dwNumThreadsRunning < systeminfo.dwNumberOfProcessors)
{
if ((pMdmThrd = (PMDMTHRD)LocalAlloc(LPTR, sizeof(*pMdmThrd))) != NULL)
{
// Create thread
//
pMdmThrd->hThrd = CreateThread(NULL, // default security
0, // default stack size
MdmAsyncThread, // thread entry point
pMdmThrd, // thread info
CREATE_SUSPENDED, // Start suspended
&pMdmThrd->tid); // thread id
if (pMdmThrd->hThrd != NULL)
{
dwNumThreadsRunning++;
// Put it in the thread list
//
pMdmThrd->pNext = gpMdmThrdList;
gpMdmThrdList = pMdmThrd;
DPRINTF1("Async Thread id: %x was created but suspended", pMdmThrd->tid);
// Send thread on its way...
//
ResumeThread(pMdmThrd->hThrd);
DPRINTF1("Async Thread id: %x is in operation", pMdmThrd->tid);
}
else
{
LocalFree(pMdmThrd);
// If we were able to get at least one thread going, indicate success.
//
if (dwNumThreadsRunning == 0)
{
DPRINTF("InitializeMdmThreads was unable to create all of the threads! (CreateThread failed)");
dwRet = LINEERR_OPERATIONFAILED;
}
break;
}
}
else
{
// If we were able to get at least one thread going, indicate success.
//
if (dwNumThreadsRunning == 0)
{
DPRINTF("InitializeMdmThreads was unable to create all of the threads! (LocalAlloc failed)");
dwRet = ERROR_OUTOFMEMORY;
}
break;
}
}
}
// If we failed in some way, delete the completion port, it isn't being used.
//
if (dwRet != ERROR_SUCCESS)
{
ASSERT(dwNumThreadsRunning == 0);
if (ghCompletionPort != NULL)
{
CloseHandle(ghCompletionPort);
ghCompletionPort = NULL;
}
}
return dwRet;
}
//****************************************************************************
// DWORD DeinitializeMdmThreads()
//
// Function: Deinitialize threads to handle modem's asynchronous operations.
//
// History:
// Mon 17-Apr-1995 11:49:53 -by- Viroon Touranachun [viroont]
// Ported from Win95.
//****************************************************************************/
DWORD DeinitializeMdmThreads()
{
PMDMTHRD pMdmThrd;
DWORD dwCount = 0;
HANDLE WaitHandles[MAXIMUM_WAIT_OBJECTS];
DPRINTF("DeinitializeMdmThreads() called.");
// Post a termination request to the completion port.
// Do one for each thread running. Keep count for later in this routine.
//
pMdmThrd = gpMdmThrdList;
while (pMdmThrd)
{
PostQueuedCompletionStatus(ghCompletionPort,
CP_BYTES_WRITTEN(0), // indicates nothing
0, // indicates this is a termination message
NULL); // indicates nothing
dwCount++;
pMdmThrd = pMdmThrd->pNext;
}
ASSERT(dwCount <= MAXIMUM_WAIT_OBJECTS); // Make sure the world hasn't turned upside down.
// Do a WaitForMultipleObjects on all of the thread handles to wait for
// them to complete.
//
// Build array to wait on.
//
dwCount = 0;
pMdmThrd = gpMdmThrdList;
while (pMdmThrd)
{
WaitHandles[dwCount++] = pMdmThrd->hThrd;
pMdmThrd = pMdmThrd->pNext;
}
WaitForMultipleObjects(dwCount, // number of threads to wait to end
WaitHandles, // array of threads
TRUE, // wait until all have been terminated
INFINITE); // wait forever, if necessary
while (pMdmThrd = gpMdmThrdList)
{
CloseHandle(pMdmThrd->hThrd);
gpMdmThrdList = pMdmThrd->pNext;
LocalFree(pMdmThrd);
}
//
// close completion port handle
//
CloseHandle(ghCompletionPort);
DPRINTF("Async Threads terminated succesfully.");
return ERROR_SUCCESS;
}
//****************************************************************************
// DWORD APIENTRY MdmAsyncThread (PMDMTHRD)
//
// Function: An entry point to the asynchronous modem thread.
//
// Returns: None
//
//****************************************************************************/
DWORD APIENTRY MdmAsyncThread (PMDMTHRD pMdmThrd)
{
DWORD dwNumberOfBytesTransferred;
DWORD dwCompletionKey;
LPOVERLAPPED lpOverlapped;
PLINEDEV pLineDev;
DPRINTF1("Async thread id: %x is running", pMdmThrd->tid);
ASSERT(ghCompletionPort != NULL);
// Read from the completion port until we get signalled to exit the thread.
// This is done by having a completion posted that has a dwCompletionKey of 0.
// dwCompletionKey is normally the pLineDev associated with an operation.
//
for (;;)
{
if (GetQueuedCompletionStatus(ghCompletionPort,
&dwNumberOfBytesTransferred,
&dwCompletionKey,
&lpOverlapped,
(DWORD)-1) == FALSE &&
lpOverlapped == NULL)
{
DPRINTF1("GetQueuedCompletionStatus() returned FALSE and lpOverlapped was NULL (GetLastError() = %d)",
GetLastError());
ASSERT(0);
continue;
}
TRACE4(
IDEVENT_CP_GET,
dwNumberOfBytesTransferred,
dwCompletionKey,
lpOverlapped
);
if (dwCompletionKey == 0)
{
DPRINTF1("Async Thread id: %d being asked to exit", pMdmThrd->tid);
break; // NULL dwCompletionKey indicates we were asked to terminate.
}
else
{
pLineDev = (PLINEDEV)dwCompletionKey;
CLAIM_LINEDEV(pLineDev);
// BUGBUG: CCaputo 2/20/96: pLineDev could have disappeard.
// BUGBUG: Might want to do something here to verify existence.
// Is this completion for the upper layer state machine (unimdm) or the
// lower layer state machine (mcx)?
//
if (lpOverlapped == NULL)
{
DWORD dwType = CP_TYPE(dwNumberOfBytesTransferred);
if (dwType != 0) {
//
// it is a ring related event
//
ProcessRings(
pLineDev,
dwType
);
} else {
//
// Is the purpose of this completion to signal an event or
// continue in the normal unimdm state machine?
//
if (pLineDev->hSynchronizeEvent == NULL)
{
DWORD dwPendingID;
dwPendingID = pLineDev->McxOut.dwReqID;
pLineDev->McxOut.dwReqID = MDM_ID_NULL;
// Call the operation handler
//
MdmCompleteAsync(pLineDev, pLineDev->McxOut.dwResult, dwPendingID);
}
else
{
SetEvent(pLineDev->hSynchronizeEvent);
}
}
}
else
{
// non-NULL lpOverlapped indicates this is for MCXAsyncComplete()
//
if (pLineDev->hModem)
{
MCXAsyncComplete (pLineDev->hModem, lpOverlapped);
}
OverPoolFree(lpOverlapped);
}
RELEASE_LINEDEV(pLineDev);
}
}
// Exit the thread properly
//
DPRINTF1("Async Thread id: %d exits", pMdmThrd->tid);
ExitThread(ERROR_SUCCESS);
return ERROR_SUCCESS;
}
//
// Rings are handle here instead of the main async handler beacuse they get built
// up in the completion port and extra one mess up the state machine
//
VOID WINAPI
ProcessRings(
PLINEDEV pLineDev,
DWORD Type
)
{
switch (pLineDev->DevState) {
case DEVST_PORTLISTENING:
//
// The line is being monitored and the first ring is coming in.
//
// Make sure call hasn't already been allocated.
// If this isn't the first set of rings, then make
// sure the previous "hdcall" has been deallocated.
// In other words, ignore rings until this happens!
//
if (pLineDev->dwCall & CALL_ALLOCATED)
{
TSPPRINTF("RING ignored because IDLE call hasn't been deallocated!");
break;
};
// We need to notify a new call to TAPI
//
(*(pLineDev->lpfnEvent))(pLineDev->htLine, NULL, LINE_NEWCALL,
(DWORD)pLineDev,
(DWORD)((LPHANDLE)&(pLineDev->htCall)),
0);
// Allocate the call
//
pLineDev->dwCall = CALL_ALLOCATED | CALL_INBOUND | CALL_ACTIVE;
// Then offer the call to TAPI
//
pLineDev->DevState = DEVST_PORTLISTENOFFER;
// OR in UNKNOWN since we don't know what kind of media mode this call is
//
pLineDev->dwCurMediaModes = pLineDev->dwDetMediaModes | LINEMEDIAMODE_UNKNOWN;
// default our bearermode to be what we support, excluding the passthrough bit
//
pLineDev->dwCurBearerModes = pLineDev->dwBearerModes & ~LINEBEARERMODE_PASSTHROUGH;
// Notify TAPI
//
NEW_CALLSTATE(pLineDev, LINECALLSTATE_OFFERING, 0);
//
// fall through
//
case DEVST_PORTLISTENOFFER:
{
//
// A ring is coming in (either the first ring or a subsequent one.)
// Handle the ring count
//
DWORD dwCurrent = GETTICKCOUNT();
// If this is the second or greater ring,
// then we may have a timer to kill.
//
if (pLineDev->dwRingCount)
{
KillMdmTimer((DWORD)pLineDev, NULL);
// Check whether the timeout expired
//
if (GTC_DELTA(pLineDev->dwRingTick, dwCurrent) >= TO_MS_RING_SEPARATION)
{
// Timeout has expired, indicating call has stopped ringing
//
pLineDev->dwRingTick = 0;
pLineDev->dwRingCount = 0;
pLineDev->DevState = DEVST_PORTLISTENING;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_IDLE, 0);
break;
};
};
pLineDev->dwRingCount++;
(*pLineDev->lpfnEvent)(pLineDev->htLine, NULL, LINE_LINEDEVSTATE,
LINEDEVSTATE_RINGING, 1L, pLineDev->dwRingCount);
TSPPRINTF1("RING#%d notfied", pLineDev->dwRingCount);
pLineDev->dwRingTick = dwCurrent;
if (SetMdmTimer((DWORD)pLineDev, NULL, TO_MS_RING_SEPARATION)
!= ERROR_SUCCESS)
{
TSPPRINTF("SetTimer failed!");
}
break;
}
default:
TSPPRINTF("ProcessRings: extra ring queued!");
break;
} // switch
return;
}
//****************************************************************************
// void MdmCompleteAsync(PLINEDEV, DWORD, DWORD)
//
// Function: A caller invokes this function when it receives WM_COMNOTIFY
// message in order to complete the pending asynchronous operation
// or asynchronous event.
//
// Note: The dialing process is pretty complex. See dialing.txt for more info.
//
// Returns: nothing
//
//****************************************************************************
void MdmCompleteAsync (PLINEDEV pLineDev, DWORD dwStatus, DWORD dwAsyncID)
{
DWORD dwRet;
ASSERT(pLineDev->pDevCfg != NULL);
TSPPRINTF2("MdmCompleteAsync services id: %d status: %d", dwAsyncID, dwStatus);
// We only care about messages we are expecting or
// MDM_ID_NULL (unexpected) messages
//
if (dwAsyncID != MDM_ID_NULL)
{
if (pLineDev->dwVxdPendingID == dwAsyncID)
{
// Reset the pending ID, except for when we are doing
// continuous monitoring from the VxD.
//
if (DEVST_PORTLISTENING != pLineDev->DevState &&
DEVST_PORTLISTENOFFER != pLineDev->DevState)
{
pLineDev->dwVxdPendingID = MDM_ID_NULL;
};
}
else
{
TSPPRINTF1("rejecting obsolete async id: %d", dwAsyncID);
return;
};
};
// Messages from the VxD when we are in takeover mode are used to do the
// async completion for the switch to takeover mode.
//
if (pLineDev->fTakeoverMode)
{
if (pLineDev->dwPendingID != INVALID_PENDINGID)
{
UnimodemSetPassthrough(pLineDev, PASSTHROUGH_ON);
pLineDev->DevState = DEVST_CONNECTED;
(*gfnCompletionCallback)(pLineDev->dwPendingID, ERROR_SUCCESS);
pLineDev->dwPendingID = INVALID_PENDINGID;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_CONNECTED, 0);
};
return;
};
// If it is the success notification, we continue the state machine
//
if (MDM_SUCCESS == dwStatus)
{
do
{
// Yes, it is. Determine the current line state
//
dwRet = ERROR_IO_PENDING;
switch (pLineDev->DevState)
{
case DEVST_PORTLISTENINIT:
//
// Put modem to non-continuous Monitor
//
pLineDev->DevState = DEVST_PORTLISTENING;
pLineDev->dwRingCount = 0;
pLineDev->dwRingTick = 0;
// Start monitoring the line
//
dwRet = UnimodemMonitor(pLineDev, MONITOR_CONTINUOUS);
break;
case DEVST_PORTSTARTPRETERMINAL:
//
// Start the terminal screen
//
pLineDev->DevState = DEVST_PORTCONNECTINIT;
dwRet = ERROR_SUCCESS;
// Turn-on passthrough mode
//
if (UnimodemSetPassthrough(pLineDev, PASSTHROUGH_ON) == ERROR_SUCCESS)
{
// Put the terminal screen up here
//
pLineDev->DevState = DEVST_PORTPRETERMINAL;
if (TerminalDialog(pLineDev) == ERROR_SUCCESS)
{
// Wait until the terminal screen completes
//
dwRet = ERROR_IO_PENDING;
};
};
break;
case DEVST_PORTPRETERMINAL:
//
// Destroy the terminal window
//
DestroyTerminalDialog(pLineDev);
// Turn-off passthrough mode
//
UnimodemSetPassthrough(pLineDev, PASSTHROUGH_OFF);
// Put it to init mode
//
pLineDev->DevState = DEVST_PORTCONNECTINIT;
dwRet = UnimodemInit(pLineDev);
break;
case DEVST_PORTCONNECTINIT:
//
// The modem was sucessfully initialized for dialing out.
//
if (!pLineDev->InitStringsAreValid) {
//
// some one call lineSetDevConfig, redo the init
//
pLineDev->DevState = DEVST_PORTCONNECTINIT;
dwRet = UnimodemInit(pLineDev);
break;
}
pLineDev->DevState = DEVST_PORTCONNECTDIALTONEDETECT;
pLineDev->dwCall |= CALL_ACTIVE;
// Detect dialtone
//
dwRet = DetectDialtone(pLineDev);
break;
case DEVST_PORTCONNECTWAITFORLINEDIAL:
case DEVST_PORTCONNECTDIALTONEDETECT:
//
// The dialtone was detected or we did not need to detect it. Now we
// are ready to dial.
//
// Note: The dialing process is pretty complex.
// (See dialing.txt for more info.)
//
pLineDev->dwCall |= CALL_ACTIVE;
// Check for needed async completion.
// (for the lineMakeCall)
//
if (pLineDev->dwPendingID != INVALID_PENDINGID &&
pLineDev->dwPendingType == PENDING_LINEMAKECALL)
{
(*gfnCompletionCallback)(pLineDev->dwPendingID, 0L);
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_DIALTONE, LINEDIALTONEMODE_UNAVAIL);
};
// Fall through to common code path
//
case DEVST_MANUALDIALING:
//
// If it is an originate address (no semi-colone at the end,) this is
// the last string we are dialing before connecting.
//
if (IsOriginateAddress(pLineDev->szAddress))
{
// If we re-enter after manual dialing,
// do not repeat manual dialing
//
if (pLineDev->DevState != DEVST_MANUALDIALING)
{
// Don't optimize this and the one below!!! (see lineGetCallStatus)
//
pLineDev->DevState = DEVST_PORTCONNECTING;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_DIALING, 0);
// Handle Manual Dial
//
if (GETOPTIONS(pLineDev->pDevCfg) & MANUAL_DIAL)
{
// bring up modal manual dial dialog,
// it will return ERROR_SUCCESS or a non-pending error code
// when it is done...
//
dwRet = ManualDialog(pLineDev);
// If there is no error, wait for dialog to finish
//
if (ERROR_SUCCESS == dwRet)
{
dwRet = ERROR_IO_PENDING;
pLineDev->DevState = DEVST_MANUALDIALING;
};
break;
};
}
else
{
*pLineDev->szAddress = '\0';
// We finish manual dialing, continue
//
pLineDev->DevState = DEVST_PORTCONNECTING;
};
NEW_CALLSTATE(pLineDev, LINECALLSTATE_PROCEEDING, 0);
// Handle INTERACTIVEVOICE
//
if (LINEMEDIAMODE_INTERACTIVEVOICE == pLineDev->dwCurMediaModes)
{
// if we have partial dialing capability and enough room to do it,
// then make it so we wait indefinitely.
//
if (pLineDev->fPartialDialing &&
lstrlenA(pLineDev->szAddress) + lstrlenA(szSemicolon)
< sizeof(pLineDev->szAddress))
{
lstrcatA(pLineDev->szAddress, szSemicolon);
};
// bring up talk drop dialog,
//
if ((dwRet = TalkDropDialog(pLineDev)) == ERROR_SUCCESS)
{
// Don't dial if we are MANUAL dialing.
//
if (!(GETOPTIONS(pLineDev->pDevCfg) & MANUAL_DIAL))
{
// Dial the number and either:
// 1) wait indefinitely, if we support partial dialing, or
// 2) begin busy monitoring for several seconds
// (register S7, dwCallSetupFailTimer)
//
if ((dwRet = UnimodemDial(pLineDev, pLineDev->szAddress, pLineDev->dwDialOptions))
== ERROR_IO_PENDING)
{
pLineDev->DevState = DEVST_TALKDROPDIALING;
};
}
else
{
// Wait until the user takes an action
//
dwRet = ERROR_IO_PENDING;
};
};
};
}
else
{
// Don't optimize this and the one above!!! (see lineGetCallStatus)
//
pLineDev->DevState = DEVST_PORTCONNECTDIAL;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_DIALING, 0);
};
// INTERACTIVEVOICE Originate's are handled above!
//
if (LINEMEDIAMODE_INTERACTIVEVOICE != pLineDev->dwCurMediaModes ||
DEVST_PORTCONNECTDIAL == pLineDev->DevState)
{
// Do we have anything useful to dial? (more than just ";"?)
//
if (lstrcmpA(pLineDev->szAddress, szSemicolon))
{
// Start the dialing process
//
dwRet = UnimodemDial(pLineDev, pLineDev->szAddress, pLineDev->dwDialOptions);
}
else
{
// just skip to the next stage
//
dwRet = ERROR_SUCCESS;
};
}
// Check for needed async completion.
// (for the lineDial)
//
if (pLineDev->dwPendingID != INVALID_PENDINGID &&
pLineDev->dwPendingType == PENDING_LINEDIAL)
{
(*gfnCompletionCallback)(pLineDev->dwPendingID, 0L);
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
};
break;
case DEVST_PORTCONNECTDIAL:
//
// The line was previously dialed with a non-originate address
// Wait for another lineDial
//
pLineDev->DevState = DEVST_PORTCONNECTWAITFORLINEDIAL;
pLineDev->dwCall |= CALL_ACTIVE;
// Send up another LINECALLSTATE_DIALING so that app knows
// to check the call status to see that lineDial is usable again.
//
NEW_CALLSTATE(pLineDev, LINECALLSTATE_DIALING, 0);
break;
case DEVST_TALKDROPDIALING:
//
// Result from UnimodemDial
//
pLineDev->DevState = DEVST_PORTCONNECTING;
// Only pay attention to these messages if we are actually originating
// (monitoring) or if it is an error from the partial dial.
// (ignore MDM_SUCCESS from partial dial)
//
if (IsOriginateAddress(pLineDev->szAddress))
{
DestroyTalkDropDialog(pLineDev);
dwRet = ERROR_SUCCESS;
}
break;
case DEVST_PORTCONNECTING:
//
// The modem sucessfully dial the originate address.
// The modem is connected.
//
pLineDev->DevState = DEVST_PORTLISTENANSWER;
dwRet = ERROR_SUCCESS;
// Post-dial Terminal Mode
//
if (GETOPTIONS(pLineDev->pDevCfg) & TERMINAL_POST)
{
pLineDev->DevState = DEVST_PORTPOSTTERMINAL;
if (TerminalDialog(pLineDev) == ERROR_SUCCESS)
{
dwRet = ERROR_IO_PENDING;
};
};
break;
case DEVST_PORTPOSTTERMINAL:
//
// Destroy the termnal window
//
DestroyTerminalDialog(pLineDev);
pLineDev->DevState = DEVST_PORTLISTENANSWER;
dwRet = ERROR_SUCCESS;
break;
case DEVST_PORTLISTENANSWER:
//
// The modem is connected (with either incoming or outgoing call.)
// Ready to notify TAPI of the connected line.
//
// Treat INTERACTIVEVOICE connections differently.
//
if (LINEMEDIAMODE_INTERACTIVEVOICE != pLineDev->dwCurMediaModes)
{
// Get the call information
//
UnimodemGetNegotiatedRate(pLineDev, (LPDWORD)&pLineDev->dwNegotiatedRate);
//
// Start monitoring the remote disconnection here
//
UnimodemMonitorDisconnect(pLineDev);
// Do we need to lauch modem light?
// We launch the light when the light was selected.
//
if (!IS_NULL_MODEM(pLineDev) &&
(GETOPTIONS(pLineDev->pDevCfg) & LAUNCH_LIGHTS))
{
HANDLE hLight;
if (LaunchModemLight(pLineDev->szDeviceName,
pLineDev->hDevice,
&hLight) == ERROR_SUCCESS)
pLineDev->hLights = hLight;
};
};
// Notify TAPI of the connected line
//
pLineDev->DevState = DEVST_CONNECTED;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_CONNECTED, 0);
break;
case DEVST_DISCONNECTING:
TSPPRINTF("Setting Drop Event");
SetEvent(
pLineDev->DroppingEvent
);
// case DEVST_DISCONNECTED:
//
// The modem was hung up successfully.
//
if (pLineDev->dwPendingID != INVALID_PENDINGID)
{
// Notify TAPI of the idle line
//
NEW_CALLSTATE(pLineDev, LINECALLSTATE_IDLE, 0);
(*(gfnCompletionCallback))(pLineDev->dwPendingID, 0L);
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
};
pLineDev->DevState = DEVST_DISCONNECTED;
break;
default:
pLineDev->DevState = DEVST_DISCONNECTED;
break;
};
// We may have a new failure
//
if ((dwRet != ERROR_IO_PENDING) && (dwRet != ERROR_SUCCESS))
{
dwStatus = MDM_FAILURE;
};
} while (dwRet == ERROR_SUCCESS);
};
// Handle failure
//
if ((dwStatus != MDM_SUCCESS) && (dwStatus != MDM_PENDING))
{
HandleMdmError(pLineDev, dwStatus);
};
return;
}
//****************************************************************************
// DWORD MdmAsyncContinue(PLINEDEV, DWORD)
//
// Function: continues the next state for the modem device
//
// Returns: ERROR_SUCCESS
//
//****************************************************************************
DWORD MdmAsyncContinue (PLINEDEV pLineDev, DWORD dwStatus)
{
pLineDev->dwVxdPendingID++;
pLineDev->McxOut.dwReqID = pLineDev->dwVxdPendingID;
pLineDev->McxOut.dwResult = dwStatus;
PostQueuedCompletionStatus(ghCompletionPort,
CP_BYTES_WRITTEN(0),
(DWORD)pLineDev,
NULL);
return ERROR_SUCCESS;
}
//****************************************************************************
// void HandleMdmError(PLINEDEV, DWORD)
//
// Function: Handles the modem line when the status is not successful.
//
// Returns: nothing
//
//****************************************************************************
void HandleMdmError(PLINEDEV pLineDev, DWORD dwStatus)
{
// Do we have a call?
//
if ((pLineDev->dwCall & CALL_ALLOCATED) &&
(pLineDev->dwCallState != LINECALLSTATE_DISCONNECTED))
{
DWORD dwDisconnectMode = 0;
LONG lAsyncResult = 0;
// Terminate all UI windows
//
DestroyTalkDropDialog(pLineDev);
DestroyManualDialog(pLineDev);
// Determine the failure
//
switch (dwStatus)
{
case MDM_HANGUP:
//
// The line is disconnected remotely, or the user cancel the line
// Destroy the terminal window
//
if ((DEVST_PORTPRETERMINAL == pLineDev->DevState)
||
(DEVST_PORTPOSTTERMINAL == pLineDev->DevState)) {
DestroyTerminalDialog(pLineDev);
lAsyncResult = LINEERR_OPERATIONFAILED;
}
dwDisconnectMode = LINEDISCONNECTMODE_NORMAL;
break;
case MDM_BUSY:
//
// We dialed out and the line is busy
//
dwDisconnectMode = LINEDISCONNECTMODE_BUSY;
break;
case MDM_NOANSWER:
case MDM_NOCARRIER:
//
// We dialed out and nobody answered the phone
//
dwDisconnectMode = LINEDISCONNECTMODE_NOANSWER;
break;
case MDM_NODIALTONE:
//
// We were dialing out but no dial tone on the line
// were we checking for a dialtone?
//
if (DEVST_PORTCONNECTDIALTONEDETECT == pLineDev->DevState &&
!(pLineDev->dwDialOptions & MDM_BLIND_DIAL))
{
lAsyncResult = LINEERR_CALLUNAVAIL;
};
dwDisconnectMode = LINEDISCONNECTMODE_NODIALTONE;
break;
case MDM_FAILURE:
default:
//
// The pending operation failed
//
if (DEVST_MANUALDIALING == pLineDev->DevState) {
//
// cancel was pressed on the manual dial dialog
//
dwDisconnectMode = LINEDISCONNECTMODE_CANCELLED;
} else {
dwDisconnectMode = LINEDISCONNECTMODE_UNAVAIL;
}
// If it is a failed makecall, need to return a failure
//
if (pLineDev->dwPendingType == PENDING_LINEMAKECALL)
{
lAsyncResult = LINEERR_OPERATIONFAILED;
};
break;
};
// No need for further UI
//
DestroyMdmDlgInstance(pLineDev);
// In any case, we need to notify TAPI to clean up the line
//
NEW_CALLSTATE(pLineDev, LINECALLSTATE_DISCONNECTED, dwDisconnectMode);
NEW_CALLSTATE(pLineDev, LINECALLSTATE_IDLE, 0);
pLineDev->DevState = DEVST_DISCONNECTED;
// Notify the caller async completion
//
if (pLineDev->dwPendingID != INVALID_PENDINGID)
{
(*gfnCompletionCallback)(pLineDev->dwPendingID, lAsyncResult);
pLineDev->dwPendingID = INVALID_PENDINGID;
// If it is a failed makecall, we need to close the line
//
if (pLineDev->dwPendingType == PENDING_LINEMAKECALL)
{
if ((pLineDev->dwDetMediaModes) && !(pLineDev->fdwResources & LINEDEVFLAGS_OUTOFSERVICE)) {
if ((DevlineDetectCall(pLineDev)) != ERROR_SUCCESS) {
//
// init failed, tell the app
//
pLineDev->LineClosed=TRUE;
(*pLineDev->lpfnEvent)(pLineDev->htLine, NULL, LINE_CLOSE,
0L, 0L, 0L);
}
} else {
//
// No need to detect the line, just close it.
//
DevlineClose(pLineDev);
}
#ifdef UNDER_CONSTRUCTION
if (pLineDev->fdwResources & LINEDEVFLAGS_OUTOFSERVICE)
{
DevlineDisabled(pLineDev);
}
else
#endif // UNDER_CONSTRUCTION
{
pLineDev->dwPendingType = INVALID_PENDINGOP;
// Clean up the call state of this line
//
// pLineDev->htCall = NULL; will be cleaned up TSPI_lineClosecall
pLineDev->dwCall = 0L;
};
}
else
{
pLineDev->dwPendingType = INVALID_PENDINGOP;
};
};
}
else
{
// A call has not been allocated but
// We may fail while start monitoring
//
if ((pLineDev->DevState == DEVST_PORTLISTENINIT) ||
(pLineDev->DevState == DEVST_PORTLISTENING))
{
// Notify the monitoring application
//
pLineDev->LineClosed=TRUE;
(*pLineDev->lpfnEvent)(pLineDev->htLine, NULL, LINE_CLOSE,
0L, 0L, 0L);
}
}
return;
}
//****************************************************************************
// DWORD DetectDialtone(PLINEDEV)
//
// Function: detects the dialtone on the phone line if the modem is capable.
// Otherwise assume there is dialtone.
//
// Returns: ERROR_SUCCESS, ERROR_IO_PENDING or an error code
//
//****************************************************************************
DWORD DetectDialtone(PLINEDEV pLineDev)
{
DWORD dwRet;
// Skip dialtone detection if Manual Dial or Blind Dial or null modem
//
if (!(GETOPTIONS(pLineDev->pDevCfg) & MANUAL_DIAL ||
pLineDev->dwDialOptions & MDM_BLIND_DIAL ||
IS_NULL_MODEM(pLineDev) ||
pLineDev->fPartialDialing == FALSE))
{
// Start the dialtone detection process
//
dwRet = UnimodemDial(pLineDev, szSemicolon, pLineDev->dwDialOptions);
}
else
{
dwRet = ERROR_SUCCESS;
};
return dwRet;
}
#ifdef UNDER_CONSTRUCTION
//****************************************************************************
// linWindowProc()
//
// Function: Private message handling proc
//
//****************************************************************************
long FAR PASCAL _loadds MdmWindowProc(hWnd, message, wParam, lParam)
HWND hWnd; /* window handle */
unsigned message; /* type of message */
WPARAM wParam; /* additional information */
LPARAM lParam; /* additional information */
{
PLINEDEV pLineDev;
switch (message)
{
//**********************************************************************
// State machine driven notification
//**********************************************************************
case WM_MDMMESSAGE:
pLineDev = (PLINEDEV)LOWORD(lParam);
MdmCompleteAsync(pLineDev, wParam, HIWORD(lParam));
break;
//**********************************************************************
// line cancellation
//**********************************************************************
case WM_MDMCANCEL:
pLineDev = (PLINEDEV)LOWORD(lParam);
// Destroy the termnal window
//
if (IS_UI_DLG_UP(pLineDev, UI_DLG_TERMINAL))
{
CloseTerminalDlg(pLineDev);
};
// Notify the caller async completion
//
if (pLineDev->dwPendingID != INVALID_PENDINGID)
{
(*gfnCompletionCallback)(pLineDev->dwPendingID, 0L);
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
};
// Turn off passthrough for the preterminal.
//
if (DEVST_PORTPRETERMINAL == pLineDev->DevState)
{
UnimodemSetPassthrough(pLineDev, PASSTHROUGH_OFF);
}
UnimodemHangup(pLineDev, TRUE);
pLineDev->DevState = DEVST_DISCONNECTED;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_IDLE, 0);
break;
//**********************************************************************
// Notification from PnP for a modem enabling/disabling
//**********************************************************************
case WM_DEVICECHANGE:
MdmDeviceServiceChanged((UINT)wParam, lParam);
return (DefWindowProc(hWnd, message, wParam, lParam));
//**********************************************************************
// Notification from Modem CPL for a modem installation/removal
//**********************************************************************
case WM_DEVICEINSTALL:
MdmDeviceChangeNotify((UINT)wParam, (LPSTR)lParam);
break;
//**********************************************************************
// Handle the changes in the modem status
//**********************************************************************
case WM_MDMCHANGE:
MdmDeviceChanged(wParam, lParam);
break;
//**********************************************************************
// Ring count timer
//**********************************************************************
case WM_TIMER:
pLineDev = GetCBfromHandle(MAKELONG(wParam, 0)); // Timer ID is the pLineDev
if(pLineDev)
{
DWORD tcNow = GETTICKCOUNT();
KillTimer(pLineDev->hwndLine, (UINT)pLineDev);
if (DEVST_PORTLISTENOFFER == pLineDev->DevState &&
GTC_DELTA(pLineDev->dwRingTick, tcNow) >= TO_MS_RING_SEPARATION)
{
// Timeout has expired, indicating call has stopped ringing
pLineDev->dwRingTick = 0;
pLineDev->dwRingCount = 0;
pLineDev->DevState = DEVST_PORTLISTENING;
NEW_CALLSTATE(pLineDev, LINECALLSTATE_IDLE, 0);
}
else
{
DPRINTF("WM_TIMER!");
}
break;
}
// Fall through
//
default: /* Passes it on if unproccessed */
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return (NULL);
}
//****************************************************************************
// MdmDeviceServiceChanged()
//
// Function: Handle a device change notification.
//
// Returns: SUCCESS
// PENDING
// ERROR_PORT_DISCONNECTED
//
//****************************************************************************
DWORD NEAR PASCAL MdmDeviceServiceChanged (UINT uEvent, LPARAM lParam)
{
PDEV_BROADCAST_HDR pdbHdr = (PDEV_BROADCAST_HDR)lParam;
PDEV_BROADCAST_PORT pdbp;
// Determine the event type
//
switch(uEvent)
{
case DBT_DEVICEARRIVAL:
case DBT_DEVICEREMOVECOMPLETE:
//
// Check the device type, must be a port type
//
if (pdbHdr->dbch_devicetype == DBT_DEVTYP_PORT)
{
pdbp = (PDEV_BROADCAST_PORT)pdbHdr;
MdmDeviceChangeNotify( uEvent == DBT_DEVICEARRIVAL ?
UMDM_ENABLE : UMDM_DISABLE,
(LPSTR)pdbp->dbcp_name);
};
break;
default:
break;
};
return SUCCESS;
}
#endif // UNDER_CONSTRUCTION