1582 lines
40 KiB
C
1582 lines
40 KiB
C
//****************************************************************************
|
|
//
|
|
// Module: Unimdm
|
|
// File: mdmutil.c
|
|
//
|
|
// Copyright (c) 1992-1993, Microsoft Corporation, all rights reserved
|
|
//
|
|
// Revision History
|
|
//
|
|
//
|
|
// 6/15/93 Nick Manson Revised OpenModem and CloseModem calls
|
|
// 1/6/93 Viroon Touranachun Revised for RNA
|
|
//
|
|
//
|
|
// Description: All Initialization code for rasman component lives here.
|
|
//
|
|
//****************************************************************************
|
|
|
|
#include "unimdm.h"
|
|
#include "umdmspi.h"
|
|
#include <devioctl.h>
|
|
#include <ntddser.h>
|
|
#include <slot.h>
|
|
|
|
#define STOP_TIMER_EVENT 0
|
|
#define RECALC_TIMER_EVENT 1
|
|
#define TSP_NOTIFICATION_EVENT 2
|
|
#define MAX_TIMER_EVENTS 3
|
|
#define NUM_TIMER_EVENTS(_tlist) (((_tlist).hN)?3:2)
|
|
|
|
// Timer list
|
|
//
|
|
typedef struct tagMdmTimer {
|
|
struct tagMdmTimer *pNext; // pointer to next CB
|
|
DWORD dwCompletionKey; // for PostQueuedCompletionStatus
|
|
LPOVERLAPPED lpOverlapped; // for PostQueuedCompletionStatus
|
|
DWORD dwWakeup; // wake-up time
|
|
} MDMTIMER, *PMDMTIMER;
|
|
|
|
typedef struct tagMdmTimerList {
|
|
PMDMTIMER pList;
|
|
HANDLE hEvent[MAX_TIMER_EVENTS];
|
|
CRITICAL_SECTION hSem;
|
|
HNOTIFICATION hN;
|
|
|
|
} TIMERLIST, *PTIMERLIST;
|
|
|
|
// LIGHTS application name
|
|
//
|
|
#define LIGHTSAPP_EXE_NAME TEXT("lights.exe")
|
|
|
|
/*****************************************************************************
|
|
* Global Parameters
|
|
*****************************************************************************/
|
|
|
|
MDMLIST gMdmList;
|
|
TIMERLIST gTimerList;
|
|
HANDLE ghtdTimer;
|
|
DWORD gtidTimerMdm;
|
|
|
|
// ******* SOME PRIVATES *************
|
|
void ProcessNotification(HNOTIFICATION hN);
|
|
BOOL ValidateFrame(PNOTIFICATION_FRAME pnf, DWORD dwTrueSize);
|
|
void ProcessFrame(PNOTIFICATION_FRAME pnf);
|
|
|
|
//****************************************************************************
|
|
// BOOL InitCBList()
|
|
//
|
|
// Function: This function initilaizes the CB list
|
|
//
|
|
// Returns: TRUE always
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
BOOL InitCBList (HINSTANCE hInstance)
|
|
{
|
|
// Initialize the modem list
|
|
//
|
|
INITCRITICALSECTION(gMdmList.hSem);
|
|
gMdmList.pList = NULL;
|
|
gMdmList.cModems = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// void DeinitCBList()
|
|
//
|
|
// Function: This function deinitilaizes the CB list
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Fri 14-Apr-1995 12:47:26 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
void DeinitCBList (HINSTANCE hInstance)
|
|
{
|
|
// Do nothing
|
|
//
|
|
DELETECRITICALSECTION(gMdmList.hSem);
|
|
return;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// BOOL MdmInitTracing()
|
|
//
|
|
// Function: Performs tracing-related initialization.
|
|
//
|
|
// Returns: None
|
|
//
|
|
// 3/29/96 JosephJ Created
|
|
//****************************************************************************
|
|
void MdmInitTracing(void)
|
|
{
|
|
traceRegisterObject(
|
|
&gMdmList,
|
|
TSP_MODEM_LIST_GUID,
|
|
TSP_MODEM_LIST_VERSION,
|
|
0,
|
|
0
|
|
);
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// BOOL MdmDeinitTracing()
|
|
//
|
|
// Function: Performs tracing-related de-initialization.
|
|
//
|
|
// Returns: None
|
|
//
|
|
// 3/29/96 JosephJ Created
|
|
//****************************************************************************
|
|
void MdmDeinitTracing(void)
|
|
{
|
|
traceUnRegisterObject(&gMdmList, 0, 0);
|
|
}
|
|
|
|
//****************************************************************************
|
|
// PLINEDEV AllocateCB (UINT cbSize)
|
|
//
|
|
// Function: Allocates a line device control block
|
|
//
|
|
// Returns: The pointer to the control block if successful, otherwise NULL.
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
PLINEDEV AllocateCB(UINT cbSize)
|
|
{
|
|
PLINEDEV pLineDev;
|
|
|
|
// Allocate from the process heap
|
|
//
|
|
pLineDev = (PLINEDEV)LocalAlloc(LPTR, cbSize);
|
|
|
|
if (pLineDev == NULL)
|
|
return NULL;
|
|
|
|
// Ininitialize the initial contents
|
|
//
|
|
pLineDev->pNext = (PLINEDEV)NULL;
|
|
pLineDev->dwVersion = UMDM_VERSION;
|
|
INITCRITICALSECTION(pLineDev->hSem);
|
|
|
|
return pLineDev;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// DWORD AddCBToList(PLINEDEV)
|
|
//
|
|
// Function: Inserts a line control block to the global modem list
|
|
//
|
|
// Returns: SUCCESS or an error code
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
DWORD AddCBToList(PLINEDEV pLineDev)
|
|
{
|
|
// Validate the structure
|
|
//
|
|
if (!ISLINEDEV(pLineDev))
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
|
|
// Insert the new node into the global list
|
|
//
|
|
pLineDev->pNext = gMdmList.pList;
|
|
gMdmList.pList = pLineDev;
|
|
gMdmList.cModems++;
|
|
|
|
// Release the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// DWORD DeleteCB(PLINEDEV pLineDev )
|
|
//
|
|
// Function: Removes a line control block to the global modem list and
|
|
// deallocate the buffer.
|
|
//
|
|
// Returns: SUCCESS or an error code
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
DWORD DeleteCB(PLINEDEV pLineDev)
|
|
{
|
|
PLINEDEV pCurCB, pPrevCB;
|
|
|
|
// Validate the structure
|
|
//
|
|
if (!ISLINEDEV(pLineDev))
|
|
return ERROR_INVALID_HANDLE;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
|
|
// Start from the head of the CB list
|
|
//
|
|
pPrevCB = NULL;
|
|
pCurCB = gMdmList.pList;
|
|
|
|
// traverse the list to find the specified CB
|
|
//
|
|
while (pCurCB != NULL)
|
|
{
|
|
if (pCurCB == pLineDev)
|
|
{
|
|
// Decrement the modem count
|
|
//
|
|
gMdmList.cModems--;
|
|
|
|
// Is there a previous control block?
|
|
//
|
|
if (pPrevCB == NULL)
|
|
{
|
|
// head of the list
|
|
//
|
|
gMdmList.pList = pCurCB->pNext;
|
|
}
|
|
else
|
|
{
|
|
pPrevCB->pNext = pCurCB->pNext;
|
|
};
|
|
break;
|
|
};
|
|
|
|
pPrevCB = pCurCB;
|
|
pCurCB = pCurCB->pNext;
|
|
};
|
|
|
|
// Finish accessing the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
|
|
// Wait until no one else is using the line
|
|
//
|
|
CLAIM_LINEDEV(pLineDev);
|
|
DELETECRITICALSECTION(pLineDev->hSem);
|
|
LocalFree(pLineDev);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// PLINEDEV GetFirstCB()
|
|
//
|
|
// Function: Get the first modem device in the list
|
|
//
|
|
// Returns: SUCCESS or an error code
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
PLINEDEV GetFirstCB()
|
|
{
|
|
PLINEDEV pLineDev;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
|
|
// Get the next head of the CB list
|
|
//
|
|
if ((pLineDev = gMdmList.pList) != NULL)
|
|
{
|
|
CLAIM_LINEDEV(pLineDev);
|
|
};
|
|
|
|
// Finish accessing the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
|
|
return pLineDev;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// PLINEDEV GetCBfromHandle()
|
|
//
|
|
// Function: This function gets the CB from a handle
|
|
//
|
|
// Returns: a pointer to PLINEDEV structure if the handle is valid, or
|
|
// NULL otherwise
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
PLINEDEV GetCBfromHandle (DWORD handle)
|
|
{
|
|
#if 0
|
|
PLINEDEV pLineDev;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
pLineDev = gMdmList.pList;
|
|
|
|
// Walk the modem list to find the line
|
|
//
|
|
while (pLineDev != NULL)
|
|
{
|
|
// BUGBUG: Chris Caputo - 1/24/96
|
|
// BUGBUG: pLineDev could be modified as we are scanning. The possibility
|
|
// BUGBUG: is that pLineDev->dwVersion gets changed.
|
|
if ((pLineDev == (PLINEDEV)handle) && ISLINEDEV(pLineDev))
|
|
{
|
|
// Exclusively accessing the line CB
|
|
//
|
|
CLAIM_LINEDEV(pLineDev);
|
|
ASSERT((pLineDev == (PLINEDEV)handle) && ISLINEDEV(pLineDev));
|
|
break;
|
|
}
|
|
|
|
pLineDev = pLineDev->pNext;
|
|
};
|
|
|
|
|
|
// Finish accessing the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
|
|
return pLineDev;
|
|
|
|
#endif
|
|
|
|
__try {
|
|
|
|
PLINEDEV pLineDev;
|
|
|
|
pLineDev=(PLINEDEV)handle;
|
|
|
|
if (pLineDev->dwVersion == UMDM_VERSION) {
|
|
|
|
CLAIM_LINEDEV(pLineDev);
|
|
|
|
return pLineDev;
|
|
|
|
}
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
//****************************************************************************
|
|
// PLINEDEV GetCBfromID()
|
|
//
|
|
// Function: This function looks for the CB owning the device
|
|
//
|
|
// Returns: TRUE (if valid)
|
|
// FALSE
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
PLINEDEV GetCBfromID (DWORD dwDeviceID)
|
|
{
|
|
PLINEDEV pLineDev;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
pLineDev = gMdmList.pList;
|
|
|
|
// Walk the modem list to find the line
|
|
//
|
|
while (pLineDev != NULL)
|
|
{
|
|
// BUGBUG: Chris Caputo - 1/24/96
|
|
// BUGBUG: pLineDev could be modified as we are scanning. The possibility
|
|
// BUGBUG: is that pLineDev->dwID gets changed.
|
|
if (pLineDev->dwID == dwDeviceID)
|
|
{
|
|
// Exclusively accessing the line CB
|
|
//
|
|
CLAIM_LINEDEV(pLineDev);
|
|
ASSERT(pLineDev->dwID == dwDeviceID);
|
|
break;
|
|
}
|
|
|
|
pLineDev = pLineDev->pNext;
|
|
};
|
|
|
|
// Finish accessing the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
return pLineDev;
|
|
}
|
|
|
|
#if 0
|
|
|
|
//****************************************************************************
|
|
// PLINEDEV GetCBfromDeviceHandle()
|
|
//
|
|
// Function: This function looks for the CB owning the device
|
|
//
|
|
// Returns: TRUE (if valid)
|
|
// FALSE
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
PLINEDEV GetCBfromDeviceHandle (DWORD hDevice)
|
|
{
|
|
PLINEDEV pLineDev;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
pLineDev = gMdmList.pList;
|
|
|
|
// Trace the list of modem port control block
|
|
//
|
|
while (pLineDev != NULL)
|
|
{
|
|
// BUGBUG: Chris Caputo - 1/24/96
|
|
// BUGBUG: pLineDev could be modified as we are scanning. The possibility
|
|
// BUGBUG: is that pLineDev->hDevice gets changed.
|
|
if (pLineDev->hDevice == (HANDLE)hDevice)
|
|
{
|
|
// Exclusively accessing the line CB
|
|
//
|
|
CLAIM_LINEDEV(pLineDev);
|
|
ASSERT(pLineDev->hDevice == (HANDLE)hDevice);
|
|
break;
|
|
}
|
|
|
|
pLineDev = pLineDev->pNext;
|
|
};
|
|
|
|
// Finish accessing the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
return pLineDev;
|
|
}
|
|
|
|
#endif
|
|
|
|
//****************************************************************************
|
|
// PLINEDEV GetCBfromName()
|
|
//
|
|
// Function: This function looks for the CB owning the device
|
|
//
|
|
// Returns: TRUE (if valid)
|
|
// FALSE
|
|
//
|
|
// Fri 14-Apr-1995 12:47:57 -by- Viroon Touranachun [viroont]
|
|
// created
|
|
//****************************************************************************
|
|
|
|
PLINEDEV GetCBfromName (LPTSTR pszName)
|
|
{
|
|
PLINEDEV pLineDev;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
pLineDev = gMdmList.pList;
|
|
|
|
// Trace the list of modem port control block
|
|
//
|
|
while (pLineDev != NULL)
|
|
{
|
|
// Exclusively accessing the line CB
|
|
//
|
|
CLAIM_LINEDEV(pLineDev);
|
|
if (!lstrcmp(pLineDev->szDeviceName, pszName))
|
|
break;
|
|
|
|
RELEASE_LINEDEV(pLineDev);
|
|
pLineDev = pLineDev->pNext;
|
|
};
|
|
|
|
// Finish accessing the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
return pLineDev;
|
|
}
|
|
|
|
#ifdef DYNA_ADDREMOVE
|
|
//****************************************************************************
|
|
// void DisableStaleModems(void)
|
|
//
|
|
// Function: Disable all modems that do not have the fReinit flag set.
|
|
//
|
|
// Returns: TRUE (if valid)
|
|
// FALSE
|
|
//
|
|
// 4/24/96 JosephJ Created
|
|
//****************************************************************************
|
|
void DisableStaleModems(void)
|
|
{
|
|
PLINEDEV pLineDev;
|
|
|
|
// Exclusively access the modem list
|
|
//
|
|
ENTERCRITICALSECTION(gMdmList.hSem);
|
|
pLineDev = gMdmList.pList;
|
|
|
|
// Trace the list of modem port control block
|
|
//
|
|
while (pLineDev != NULL)
|
|
{
|
|
// Exclusively accessing the line CB
|
|
//
|
|
CLAIM_LINEDEV(pLineDev);
|
|
|
|
if (!(pLineDev->fdwResources&LINEDEVFLAGS_REINIT))
|
|
{
|
|
DPRINTF1("WARNING: MARKING MODEM OUT-OF-SERVICE: [%s]",
|
|
pLineDev->szDeviceName);
|
|
pLineDev->fdwResources|= LINEDEVFLAGS_OUTOFSERVICE;
|
|
}
|
|
else
|
|
{
|
|
pLineDev->fdwResources&=~LINEDEVFLAGS_REINIT;
|
|
pLineDev->fdwResources&=~LINEDEVFLAGS_OUTOFSERVICE;
|
|
}
|
|
|
|
RELEASE_LINEDEV(pLineDev);
|
|
pLineDev = pLineDev->pNext;
|
|
};
|
|
|
|
// Finish accessing the modem list
|
|
//
|
|
LEAVECRITICALSECTION(gMdmList.hSem);
|
|
|
|
}
|
|
#endif // DYNA_ADDREMOVE
|
|
|
|
//****************************************************************************
|
|
// DWORD NullifyLineDevice(PLINEDEV pLineDev)
|
|
//
|
|
// Functions: Clean up the contents of the modem CB
|
|
//
|
|
// Return: ERROR_SUCCESS always
|
|
//****************************************************************************
|
|
|
|
DWORD NullifyLineDevice (PLINEDEV pLineDev)
|
|
{
|
|
// Turn the line device back to its initiali state
|
|
//
|
|
pLineDev->fdwResources = 0L;
|
|
pLineDev->hDevice = INVALID_DEVICE;
|
|
pLineDev->htLine = NULL;
|
|
pLineDev->lpfnEvent = NULL;
|
|
pLineDev->DevState = DEVST_DISCONNECTED;
|
|
pLineDev->szAddress[0] = '\0';
|
|
pLineDev->htCall = NULL;
|
|
pLineDev->dwCall = 0L;
|
|
pLineDev->dwCallState = LINECALLSTATE_IDLE;
|
|
pLineDev->dwCallStateMode = 0L;
|
|
pLineDev->dwCurMediaModes = 0L;
|
|
pLineDev->dwDetMediaModes = 0L;
|
|
pLineDev->fTakeoverMode = FALSE;
|
|
pLineDev->dwMediaModes = pLineDev->dwDefaultMediaModes;
|
|
pLineDev->dwRingCount = 0L;
|
|
pLineDev->dwRingTick = 0L;
|
|
pLineDev->dwNegotiatedRate = 0L;
|
|
|
|
// Async operation
|
|
//
|
|
pLineDev->dwPendingID = INVALID_PENDINGID;
|
|
pLineDev->dwPendingType = INVALID_PENDINGOP;
|
|
pLineDev->dwVxdPendingID = MDM_ID_NULL;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// BOOL ValidateDevCfgClass(LPCSTR lpszDeviceClass)
|
|
//
|
|
// Functions: Validate the supported device class
|
|
//
|
|
// Return: TRUE if the device class is supported
|
|
// FALSE otherwise
|
|
//****************************************************************************
|
|
|
|
BOOL ValidateDevCfgClass (LPCTSTR lpszDeviceClass)
|
|
{
|
|
UINT idClass;
|
|
|
|
// Need the device class
|
|
//
|
|
if (lpszDeviceClass == NULL)
|
|
return FALSE;
|
|
|
|
// Determine the device class
|
|
//
|
|
for (idClass = 0; idClass < MAX_SUPPORT_CLASS; idClass++)
|
|
{
|
|
if (lstrcmpi(lpszDeviceClass, aGetID[idClass].szClassName) == 0)
|
|
break;
|
|
};
|
|
|
|
// Do we support the requested class?
|
|
//
|
|
switch (idClass)
|
|
{
|
|
case TAPILINE:
|
|
case COMM:
|
|
case COMMMODEM:
|
|
case COMMMODEMPORTNAME:
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
};
|
|
}
|
|
|
|
//****************************************************************************
|
|
// ValidateAddress()
|
|
//
|
|
// Function: This function validates a tapi address and creates a version of
|
|
// it to pass to the VxD. In addition, it returns the address in
|
|
// ANSI form, rather than Unicode.
|
|
//
|
|
// Returns: SUCCESS or LINEERR_xxx depending on the failure reason
|
|
//
|
|
//****************************************************************************
|
|
|
|
LONG ValidateAddress(PLINEDEV pLineDev,
|
|
#ifdef UNICODE
|
|
LPCTSTR lpszUnicodeInAddress,
|
|
#else // UNICODE
|
|
LPCSTR lpszInAddress,
|
|
#endif // UNICODE
|
|
LPSTR lpszOutAddress)
|
|
{
|
|
LPCSTR lpszSrc;
|
|
int cbOutLen = MAXADDRESSLEN;
|
|
#ifdef UNICODE
|
|
LPSTR lpszInAddress; // ANSI version of lpszUnicodeInAddress
|
|
DWORD dwInAddressLen; // in bytes
|
|
#endif // UNICODE
|
|
|
|
ASSERT(lpszOutAddress);
|
|
|
|
#ifdef UNICODE
|
|
// is lpszUnicodeInAddress NULL?
|
|
//
|
|
if (lpszUnicodeInAddress == NULL || *lpszUnicodeInAddress == 0)
|
|
{
|
|
*lpszOutAddress = 0;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
// Convert lpszUnicodeInAddress to lpszInAddress (ANSI)
|
|
dwInAddressLen = WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
lpszUnicodeInAddress,
|
|
-1,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (dwInAddressLen == 0)
|
|
{
|
|
TSPPRINTF1("ValidateAddress:WideCharToMultiByte returned %d",
|
|
GetLastError());
|
|
return LINEERR_INVALADDRESS;
|
|
}
|
|
|
|
lpszInAddress = (LPSTR)LocalAlloc(LPTR, dwInAddressLen);
|
|
|
|
if (lpszInAddress == NULL)
|
|
{
|
|
TSPPRINTF1("ValidateAddress:WideCharToMultiByte returned %d",
|
|
GetLastError());
|
|
return LINEERR_NOMEM;
|
|
}
|
|
|
|
dwInAddressLen = WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
lpszUnicodeInAddress,
|
|
-1,
|
|
lpszInAddress,
|
|
dwInAddressLen,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (dwInAddressLen == 0)
|
|
{
|
|
TSPPRINTF1("ValidateAddress:WideCharToMultiByte returned %d",
|
|
GetLastError());
|
|
LocalFree(lpszInAddress);
|
|
return LINEERR_INVALADDRESS;
|
|
}
|
|
#endif // UNICODE
|
|
|
|
// Verify that the first char is a valid single-byte char.
|
|
//
|
|
if (CharNextA(lpszInAddress) - lpszInAddress != 1)
|
|
{
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return LINEERR_INVALADDRESS;
|
|
}
|
|
|
|
// tone or pulse? set dwDialOptions appropriately
|
|
// also, set lpszSrc
|
|
//
|
|
if (*lpszInAddress == 'T' || *lpszInAddress == 't') // tone
|
|
{
|
|
lpszSrc = lpszInAddress + 1;
|
|
pLineDev->dwDialOptions |= MDM_TONE_DIAL;
|
|
}
|
|
else
|
|
{
|
|
if (*lpszInAddress == 'P' || *lpszInAddress == 'p') // pulse
|
|
{
|
|
lpszSrc = lpszInAddress + 1;
|
|
pLineDev->dwDialOptions &= ~MDM_TONE_DIAL;
|
|
}
|
|
else
|
|
{
|
|
lpszSrc = lpszInAddress;
|
|
}
|
|
}
|
|
|
|
// copy In to Out scanning for various dialoptions, returning error if we
|
|
// don't support something.
|
|
//
|
|
while (*lpszSrc && cbOutLen)
|
|
{
|
|
switch (*lpszSrc)
|
|
{
|
|
case '$':
|
|
if (!(pLineDev->dwDevCapFlags & LINEDEVCAPFLAGS_DIALBILLING))
|
|
{
|
|
UINT cCommas;
|
|
|
|
// Get the wait-for-bong period
|
|
//
|
|
cCommas = GETWAITBONG(pLineDev->pDevCfg);
|
|
|
|
// Calculate the number of commas we need to insert
|
|
//
|
|
cCommas = (cCommas/INC_WAIT_BONG) +
|
|
(cCommas%INC_WAIT_BONG ? 1 : 0);
|
|
|
|
// Insert the strings of commas
|
|
//
|
|
while (cbOutLen && cCommas)
|
|
{
|
|
*lpszOutAddress++ = ',';
|
|
cbOutLen--;
|
|
cCommas--;
|
|
};
|
|
goto Skip_This_Character;
|
|
}
|
|
break;
|
|
|
|
case '@':
|
|
if (!(pLineDev->dwDevCapFlags & LINEDEVCAPFLAGS_DIALQUIET))
|
|
{
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return LINEERR_DIALQUIET;
|
|
}
|
|
break;
|
|
|
|
case 'W':
|
|
case 'w':
|
|
if (!(pLineDev->dwDevCapFlags & LINEDEVCAPFLAGS_DIALDIALTONE))
|
|
{
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return LINEERR_DIALDIALTONE;
|
|
}
|
|
break;
|
|
|
|
case '?':
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return LINEERR_DIALPROMPT;
|
|
|
|
case '|': // subaddress
|
|
case '^': // name field
|
|
goto Skip_The_Rest;
|
|
|
|
case ';':
|
|
if (!pLineDev->fPartialDialing)
|
|
{
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return LINEERR_INVALADDRESS;
|
|
}
|
|
|
|
// This signifies the end of a dialable address.
|
|
// Use it and skip the rest.
|
|
//
|
|
*lpszOutAddress++ = *lpszSrc;
|
|
goto Skip_The_Rest;
|
|
|
|
case ' ':
|
|
case '-':
|
|
// skip these characters
|
|
//
|
|
goto Skip_This_Character;
|
|
}
|
|
|
|
// Copy this character
|
|
//
|
|
*lpszOutAddress++ = *lpszSrc;
|
|
cbOutLen--;
|
|
|
|
Skip_This_Character:
|
|
// Verify that the next char is a valid single-byte char.
|
|
//
|
|
if (CharNextA(lpszSrc) - lpszSrc != 1)
|
|
{
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return LINEERR_INVALADDRESS;
|
|
}
|
|
lpszSrc++;
|
|
}
|
|
|
|
// Did we run out of space in the outgoing buffer?
|
|
//
|
|
if (*lpszSrc && cbOutLen == 0)
|
|
{
|
|
// yes
|
|
//
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return LINEERR_INVALADDRESS;
|
|
}
|
|
|
|
Skip_The_Rest:
|
|
*lpszOutAddress = 0;
|
|
#ifdef UNICODE
|
|
LocalFree(lpszInAddress);
|
|
#endif // UNICODE
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// IsOriginateAddress()
|
|
//
|
|
// Function: Figures out whether a string is an originate address or not.
|
|
// An originate address is one that doesn't have a semi-colon at
|
|
// the end.
|
|
//
|
|
// Note: lpszAddress is not a DBCS string. AnsiNext is not used.
|
|
//
|
|
// Returns: TRUE if it is an originate address.
|
|
// FALSE if it is not. (ie. semi-colon at the end of the address)
|
|
//
|
|
//****************************************************************************
|
|
|
|
BOOL IsOriginateAddress(LPCSTR lpszAddress)
|
|
{
|
|
BOOL fRet = TRUE; // assume this is an originate string
|
|
|
|
// try to prove this isn't an originate string by finding a semi-colon
|
|
//
|
|
while (*lpszAddress)
|
|
{
|
|
if (';' == *lpszAddress)
|
|
{
|
|
fRet = FALSE;
|
|
break;
|
|
}
|
|
lpszAddress++;
|
|
};
|
|
return fRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// SetMdmTimer(LPOVERLAPPED, DWORD, DWORD)
|
|
//
|
|
// Function: Set a timer to post to the completion port after the specified
|
|
// time elapsed.
|
|
//
|
|
// Returns: ERROR_SUCCESS if success
|
|
// other error code for failure
|
|
//
|
|
//****************************************************************************
|
|
|
|
DWORD SetMdmTimer (DWORD dwCompletionKey,
|
|
LPOVERLAPPED lpOverlapped,
|
|
DWORD dwTime)
|
|
{
|
|
PMDMTIMER pTimer, pPrev, pNext;
|
|
DWORD tcNow = GETTICKCOUNT();
|
|
|
|
ASSERT(dwTime<GTC_MAXDELTA);
|
|
|
|
// Allocate a timer block
|
|
//
|
|
if ((pTimer = (PMDMTIMER)LocalAlloc(LMEM_FIXED, sizeof(*pTimer))) == NULL)
|
|
{
|
|
return ERROR_OUTOFMEMORY;
|
|
};
|
|
|
|
// Calculate the wake-up time
|
|
//
|
|
pTimer->pNext = NULL;
|
|
pTimer->dwCompletionKey = dwCompletionKey;
|
|
pTimer->lpOverlapped = lpOverlapped;
|
|
GTC_AequalsBplusC(pTimer->dwWakeup, tcNow, dwTime);
|
|
|
|
// Insert the timer block into the timer list
|
|
//
|
|
// DPRINTF1("before SetMdmTimer crit sect (%d/%d)", dwCompletionKey, lpOverlapped);
|
|
ENTERCRITICALSECTION(gTimerList.hSem);
|
|
// DPRINTF1("in SetMdmTimer crit sect (%d/%d)", dwCompletionKey, lpOverlapped);
|
|
|
|
#ifdef DEBUG
|
|
|
|
pNext = gTimerList.pList;
|
|
|
|
while (pNext != NULL) {
|
|
|
|
ASSERT(!(pNext->dwCompletionKey == dwCompletionKey &&
|
|
pNext->lpOverlapped == lpOverlapped));
|
|
|
|
pNext=pNext->pNext;
|
|
}
|
|
|
|
#endif //DEBUG
|
|
|
|
pPrev = NULL;
|
|
pNext = gTimerList.pList;
|
|
|
|
while(pNext != NULL)
|
|
{
|
|
if (GTC_AleB(pTimer->dwWakeup, pNext->dwWakeup))
|
|
{
|
|
// Found a place to insert
|
|
//
|
|
pTimer->pNext = pNext;
|
|
if (pPrev == NULL)
|
|
{
|
|
// Head of the list
|
|
//
|
|
gTimerList.pList = pTimer;
|
|
}
|
|
else
|
|
{
|
|
pPrev->pNext = pTimer;
|
|
};
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Next timer block
|
|
//
|
|
pPrev = pNext;
|
|
pNext = pNext->pNext;
|
|
};
|
|
};
|
|
|
|
// If we are at the end of the list, append the new timer to the end
|
|
//
|
|
if (pNext == NULL)
|
|
{
|
|
if (pPrev == NULL)
|
|
{
|
|
gTimerList.pList = pTimer;
|
|
}
|
|
else
|
|
{
|
|
pPrev->pNext = pTimer;
|
|
};
|
|
};
|
|
|
|
// If we insert it in front of the list
|
|
// Wake up the timer thread to recalculate the sleep time
|
|
//
|
|
if (gTimerList.pList == pTimer)
|
|
{
|
|
SetEvent(gTimerList.hEvent[RECALC_TIMER_EVENT]);
|
|
};
|
|
|
|
LEAVECRITICALSECTION(gTimerList.hSem);
|
|
// DPRINTF1("after SetMdmTimer crit sect (%d/%d)", dwCompletionKey, lpOverlapped);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// KillMdmTimer(DWORD, LPOVERLAPPED)
|
|
//
|
|
// Function: Kill a timer
|
|
//
|
|
// Returns: TRUE is timeout was found and deleted.
|
|
// FLASE if timeout was not found (maybe because it alread fired).
|
|
//
|
|
//****************************************************************************
|
|
|
|
BOOL KillMdmTimer (DWORD dwCompletionKey,
|
|
LPOVERLAPPED lpOverlapped)
|
|
{
|
|
PMDMTIMER pCurCB, pPrevCB;
|
|
BOOL bRet = FALSE;
|
|
|
|
// DPRINTF1("KillMdmTimer entered (%d/%d)", dwCompletionKey, lpOverlapped);
|
|
|
|
// Exclusively access the timer list
|
|
//
|
|
ENTERCRITICALSECTION(gTimerList.hSem);
|
|
// DPRINTF1("KillMdmTimer in crit sect (%d/%d)", dwCompletionKey, lpOverlapped);
|
|
|
|
// Start from the head of the CB list
|
|
//
|
|
pPrevCB = NULL;
|
|
pCurCB = gTimerList.pList;
|
|
|
|
// traverse the list to find the specified CB
|
|
//
|
|
while (pCurCB != NULL)
|
|
{
|
|
if (pCurCB->dwCompletionKey == dwCompletionKey &&
|
|
pCurCB->lpOverlapped == lpOverlapped)
|
|
{
|
|
bRet = TRUE;
|
|
|
|
// Is there a previous control block?
|
|
//
|
|
if (pPrevCB == NULL)
|
|
{
|
|
// head of the list
|
|
//
|
|
gTimerList.pList = pCurCB->pNext;
|
|
}
|
|
else
|
|
{
|
|
pPrevCB->pNext = pCurCB->pNext;
|
|
};
|
|
LocalFree(pCurCB);
|
|
break;
|
|
};
|
|
|
|
pPrevCB = pCurCB;
|
|
pCurCB = pCurCB->pNext;
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
if (pCurCB == NULL)
|
|
{
|
|
D_TRACE(TspDpf(666,TEXT("KillMdmTimer: Did not find event on list.\n"));)
|
|
// DPRINTF("KillMdmTimer() did not fine event on its list.");
|
|
}
|
|
#endif // DEBUG
|
|
|
|
// Finish accessing the timer list
|
|
//
|
|
LEAVECRITICALSECTION(gTimerList.hSem);
|
|
|
|
// DPRINTF1("KillMdmTimer exit (%d/%d)", dwCompletionKey/lpOverlapped);
|
|
return bRet;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// DWORD InitializeMdmTimer()
|
|
//
|
|
// Function: Initialize a timer utility
|
|
//
|
|
// Returns: ERROR_SUCCESS if success
|
|
// other error code for failure
|
|
//
|
|
//****************************************************************************
|
|
|
|
DWORD InitializeMdmTimer()
|
|
{
|
|
// Initialize the timer list critical section
|
|
//
|
|
INITCRITICALSECTION(gTimerList.hSem);
|
|
gTimerList.pList = NULL;
|
|
|
|
// Create the recalc event
|
|
//
|
|
if (gTimerList.hEvent[RECALC_TIMER_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL))
|
|
{
|
|
// Create the stop event
|
|
//
|
|
if (gTimerList.hEvent[STOP_TIMER_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL))
|
|
{
|
|
// Create the notification handle and event...
|
|
gTimerList.hN = notifCreate(TRUE, SLOTNAME_UNIMODEM_NOTIFY_TSP,
|
|
MAX_NOTIFICATION_FRAME_SIZE, 10);
|
|
if (!gTimerList.hN)
|
|
{
|
|
DPRINTF3("WARNING: notifServerCreate(\"%s\", %lu) failed. GetLastError=0x%lx.\n",
|
|
(LPCTSTR) SLOTNAME_UNIMODEM_NOTIFY_TSP,
|
|
(unsigned long) MAX_NOTIFICATION_FRAME_SIZE,
|
|
(unsigned long) GetLastError());
|
|
// Well, we go on, not a fatal error...
|
|
}
|
|
else
|
|
{
|
|
gTimerList.hEvent[TSP_NOTIFICATION_EVENT] =
|
|
notifGetObj(gTimerList.hN);
|
|
ASSERT(gTimerList.hEvent[TSP_NOTIFICATION_EVENT]);
|
|
}
|
|
// Start the timer thread
|
|
//
|
|
ghtdTimer = CreateThread(
|
|
NULL, // default security
|
|
0, // default stack size
|
|
(LPTHREAD_START_ROUTINE)MdmTimerThread, // thread entry point
|
|
NULL, // no parameter
|
|
0, // Start immediately
|
|
>idTimerMdm); // thread id
|
|
|
|
if (ghtdTimer)
|
|
{
|
|
// We started the timer services
|
|
//
|
|
|
|
// Register the timer list with the
|
|
// tracing system.
|
|
traceRegisterObject(
|
|
&gTimerList,
|
|
TSP_TIMER_LIST_GUID,
|
|
TSP_TIMER_LIST_VERSION,
|
|
0,
|
|
0
|
|
);
|
|
return ERROR_SUCCESS;
|
|
};
|
|
};
|
|
};
|
|
|
|
// Cannot start the timer, clean up resources
|
|
//
|
|
|
|
if (gTimerList.hN)
|
|
{
|
|
// the notification event is owned by the notif object, hN, so we don't
|
|
// CloseHandle it here.
|
|
gTimerList.hEvent[TSP_NOTIFICATION_EVENT]=NULL;
|
|
notifFree(gTimerList.hN);
|
|
gTimerList.hN=0;
|
|
}
|
|
|
|
if (gTimerList.hEvent[STOP_TIMER_EVENT])
|
|
{
|
|
CloseHandle(gTimerList.hEvent[STOP_TIMER_EVENT]);
|
|
};
|
|
|
|
if (gTimerList.hEvent[RECALC_TIMER_EVENT])
|
|
{
|
|
CloseHandle(gTimerList.hEvent[RECALC_TIMER_EVENT]);
|
|
};
|
|
|
|
DELETECRITICALSECTION(gTimerList.hSem);
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// DWORD DeinitializeMdmTimer()
|
|
//
|
|
// Function: Deinitialize a timer utility
|
|
//
|
|
// Returns: ERROR_SUCCESS if success
|
|
// other error code for failure
|
|
//
|
|
//****************************************************************************
|
|
|
|
DWORD DeinitializeMdmTimer()
|
|
{
|
|
// Un-register the timer list with the
|
|
// tracing system.
|
|
traceUnRegisterObject(&gTimerList, 0, 0);
|
|
|
|
// Signal the stop event
|
|
//
|
|
SetEvent(gTimerList.hEvent[STOP_TIMER_EVENT]);
|
|
|
|
// Wait until the the timer thread terminates
|
|
//
|
|
WaitForSingleObject(ghtdTimer, INFINITE);
|
|
|
|
//
|
|
// close thread handle
|
|
//
|
|
CloseHandle(ghtdTimer);
|
|
|
|
// Destroy the notification object, if we allocated it...
|
|
if (gTimerList.hN)
|
|
{
|
|
// the notification event is owned by the notif object, hN.
|
|
gTimerList.hEvent[TSP_NOTIFICATION_EVENT]=NULL;
|
|
notifFree(gTimerList.hN);
|
|
gTimerList.hN=0;
|
|
}
|
|
// Destroy the recalc and the stop events
|
|
//
|
|
CloseHandle(gTimerList.hEvent[STOP_TIMER_EVENT]);
|
|
CloseHandle(gTimerList.hEvent[RECALC_TIMER_EVENT]);
|
|
|
|
// Deinitialize the timer list critical section
|
|
//
|
|
DELETECRITICALSECTION(gTimerList.hSem);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// DWORD APIENTRY MdmTimerThread(DWORD)
|
|
//
|
|
// Function: timer thread
|
|
//
|
|
// Returns: None
|
|
//
|
|
//****************************************************************************
|
|
|
|
DWORD APIENTRY MdmTimerThread(DWORD dwParam)
|
|
{
|
|
DWORD dwWait;
|
|
|
|
// Start waiting for the new timer infinitely
|
|
//
|
|
dwWait = INFINITE;
|
|
|
|
// Wait for the recalc event for the specified time
|
|
//
|
|
while (TRUE)
|
|
{
|
|
switch (WaitForMultipleObjects(NUM_TIMER_EVENTS(gTimerList),
|
|
gTimerList.hEvent, FALSE, dwWait))
|
|
{
|
|
// If the waittime is expired, some timer block needs to wake up
|
|
//
|
|
case WAIT_TIMEOUT:
|
|
{
|
|
PMDMTIMER pTimer, pNext;
|
|
DWORD dwCurrent;
|
|
|
|
ENTERCRITICALSECTION(gTimerList.hSem);
|
|
|
|
dwCurrent = GETTICKCOUNT();
|
|
|
|
// Start signalling from the head of the list
|
|
//
|
|
pNext = gTimerList.pList;
|
|
|
|
while(pNext && GTC_AleB(pNext->dwWakeup, dwCurrent))
|
|
{
|
|
// DPRINTF1("MdmTimerThread queuing %d/%d", pNext->dwCompletionKey, pNext->lpOverlapped);
|
|
PostQueuedCompletionStatus(ghCompletionPort,
|
|
1,
|
|
pNext->dwCompletionKey,
|
|
pNext->lpOverlapped);
|
|
pTimer = pNext;
|
|
pNext = pTimer->pNext;
|
|
LocalFree(pTimer);
|
|
};
|
|
|
|
// Recalculate the wait time
|
|
// If nothing is in the list, the wake time is infinite
|
|
//
|
|
if (pNext)
|
|
{
|
|
dwWait = GTC_DELTA(dwCurrent, pNext->dwWakeup);
|
|
}
|
|
else
|
|
{
|
|
dwWait = INFINITE;
|
|
};
|
|
|
|
gTimerList.pList = pNext;
|
|
LEAVECRITICALSECTION(gTimerList.hSem);
|
|
break;
|
|
}
|
|
|
|
// If it is the recalc event
|
|
// we need to recalc the wait time from the head of the list
|
|
//
|
|
case WAIT_OBJECT_0+RECALC_TIMER_EVENT:
|
|
{
|
|
DWORD dwCurrent;
|
|
|
|
ENTERCRITICALSECTION(gTimerList.hSem);
|
|
|
|
dwCurrent = GETTICKCOUNT();
|
|
|
|
if (gTimerList.pList
|
|
&& GTC_AleB(dwCurrent, gTimerList.pList->dwWakeup))
|
|
{
|
|
dwWait = GTC_DELTA(dwCurrent,gTimerList.pList->dwWakeup);
|
|
}
|
|
else
|
|
{
|
|
dwWait = 0;
|
|
};
|
|
LEAVECRITICALSECTION(gTimerList.hSem);
|
|
break;
|
|
}
|
|
|
|
case WAIT_OBJECT_0+TSP_NOTIFICATION_EVENT:
|
|
{
|
|
|
|
ENTERCRITICALSECTION(gTimerList.hSem);
|
|
ProcessNotification(gTimerList.hN);
|
|
LEAVECRITICALSECTION(gTimerList.hSem);
|
|
break;
|
|
}
|
|
|
|
// Otherwise terminate the timer thread
|
|
//
|
|
case WAIT_OBJECT_0+STOP_TIMER_EVENT:
|
|
{
|
|
PMDMTIMER pNextTimer, pTimer;
|
|
|
|
// Free all the timer block
|
|
//
|
|
pNextTimer = gTimerList.pList;
|
|
while(pNextTimer)
|
|
{
|
|
pTimer = pNextTimer;
|
|
pNextTimer = pTimer->pNext;
|
|
LocalFree(pTimer);
|
|
};
|
|
gTimerList.pList = NULL;
|
|
|
|
ExitThread(ERROR_SUCCESS);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
default:
|
|
DPRINTF("Got unknown notification!\n");
|
|
break;
|
|
};
|
|
};
|
|
}
|
|
|
|
//****************************************************************************
|
|
// DWORD LaunchModemLight (LPTSTR szModemName, HANDLE hModem, LPHANDLE lphLight)
|
|
//
|
|
// Function: Lauch the modem lights applet
|
|
//
|
|
// Returns: ERROR_SUCCESS if success otherwise ERROR_OPEN_FAILED
|
|
//
|
|
//****************************************************************************/
|
|
|
|
DWORD LaunchModemLight (LPTSTR szModemName, HANDLE hModem, LPHANDLE lphLight)
|
|
{
|
|
HANDLE hEvent;
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFO sti;
|
|
TCHAR szCmdline[256];
|
|
SERIALPERF_STATS serialstats;
|
|
DWORD dwBytes;
|
|
DWORD dwRet;
|
|
OVERLAPPED ov;
|
|
|
|
// Check to see if any bytes have been transferred or receive. If none
|
|
// has, there is no need to launch lights because this is probably a
|
|
// port driver that doesn't support this ioctl.
|
|
//
|
|
ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (ov.hEvent == NULL)
|
|
{
|
|
return ERROR_OPEN_FAILED;
|
|
}
|
|
|
|
ov.hEvent = (HANDLE)((DWORD)ov.hEvent | 1);
|
|
|
|
dwRet = DeviceIoControl(hModem,
|
|
IOCTL_SERIAL_GET_STATS,
|
|
&serialstats,
|
|
sizeof(SERIALPERF_STATS),
|
|
&serialstats,
|
|
sizeof(SERIALPERF_STATS),
|
|
&dwBytes,
|
|
&ov);
|
|
|
|
if (!dwRet)
|
|
{
|
|
if (ERROR_IO_PENDING == GetLastError())
|
|
{
|
|
dwRet = GetOverlappedResult(hModem,
|
|
&ov,
|
|
&dwBytes,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
ov.hEvent = (HANDLE)((DWORD)ov.hEvent & 0xfffffffe);
|
|
CloseHandle(ov.hEvent);
|
|
|
|
|
|
|
|
if (!dwRet ||
|
|
(serialstats.ReceivedCount == 0 &&
|
|
serialstats.TransmittedCount == 0))
|
|
{
|
|
return ERROR_OPEN_FAILED;
|
|
}
|
|
|
|
|
|
// OK, the GET_STATS ioctl seems to work, so let's really launch lights.
|
|
|
|
|
|
// Create the lights shutdown event handle.
|
|
if ((hEvent = CreateEvent( NULL, FALSE, FALSE, NULL )) != NULL)
|
|
{
|
|
// Create a global handle for use in other processes and close the
|
|
// local handle.
|
|
*lphLight = hEvent;
|
|
|
|
// Compose a modem lights process command line
|
|
//
|
|
wsprintf( szCmdline, LIGHTSAPP_EXE_NAME TEXT(" %lu %lu %lu %s"),
|
|
GetCurrentProcessId(), hEvent, hModem, szModemName );
|
|
|
|
// Create the modem lights process and store ID for use in CloseModem.
|
|
ZeroMemory(&sti, sizeof(sti));
|
|
sti.cb = sizeof(STARTUPINFO);
|
|
if ( !CreateProcess(NULL, szCmdline, // Start up command line
|
|
NULL, NULL, FALSE, 0, NULL, NULL, &sti, &pi) )
|
|
{
|
|
DPRINTF1("LaunchModemLight: CreateProcess failed (%d).",
|
|
GetLastError());
|
|
|
|
CloseHandle(hEvent);
|
|
*lphLight = (DWORD)NULL;
|
|
|
|
return ERROR_OPEN_FAILED;
|
|
}
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
DPRINTF("LaunchModemLight: Succeeded.");
|
|
return ERROR_SUCCESS;
|
|
}
|
|
DPRINTF1("LaunchModemLight: CreateEvent failed (%d).",
|
|
GetLastError());
|
|
return ERROR_OPEN_FAILED;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// DWORD TerminateModemLight (HANDLE hLight)
|
|
//
|
|
// Function: Terminate the modem lights applet
|
|
//
|
|
// Returns: ERROR_SUCCESS always
|
|
//
|
|
//****************************************************************************
|
|
|
|
DWORD TerminateModemLight (HANDLE hLight)
|
|
{
|
|
SetEvent(hLight);
|
|
CloseHandle(hLight);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//****************************************************************************
|
|
// Function: Processes an external TSP notification
|
|
// (produced as a result of some process loading unimdm.tsp and
|
|
// calling UnimodemNotifyTSP(...))
|
|
// WARNING: This function is called with the timer critical section still
|
|
// held -- better return quickly!
|
|
//****************************************************************************
|
|
void ProcessNotification(HNOTIFICATION hN)
|
|
{
|
|
BOOL fRet;
|
|
struct {
|
|
DWORD dw0;
|
|
BYTE rgb[MAX_NOTIFICATION_FRAME_SIZE];
|
|
} EmptyFr;
|
|
PNOTIFICATION_FRAME pnf = (PNOTIFICATION_FRAME) &EmptyFr;
|
|
DWORD dwcbMax=sizeof(EmptyFr);
|
|
DWORD dwcbRead=0;
|
|
|
|
pnf->dwSig=pnf->dwSize=0;
|
|
|
|
fRet=notifReadMsg(hN, (LPBYTE) pnf, dwcbMax, &dwcbRead);
|
|
if (!fRet)
|
|
{
|
|
DPRINTF1("notifReadFrame(...) failed. GetLastError=0x%lx.\n",
|
|
(unsigned long) GetLastError());
|
|
goto end;
|
|
}
|
|
|
|
// Verify validity of msg...
|
|
if (!ValidateFrame(pnf, dwcbRead))
|
|
{
|
|
DPRINTF("Invalid frame\n");
|
|
goto end;
|
|
}
|
|
ProcessFrame(pnf);
|
|
|
|
end:
|
|
return;
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// Function: Validates a frame -- checks signature, etc...
|
|
//****************************************************************************
|
|
BOOL ValidateFrame(PNOTIFICATION_FRAME pnf, DWORD dwTrueSize)
|
|
{
|
|
return (pnf && pnf->dwSig==dwNFRAME_SIG && pnf->dwSize>=sizeof(*pnf) &&
|
|
pnf->dwSize==dwTrueSize &&
|
|
pnf->dwSize<=MAX_NOTIFICATION_FRAME_SIZE);
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
// Function: Processes a received notification frame
|
|
// (received as a result of some process loading unimdm.tsp and
|
|
// calling UnimodemNotifyTSP(...))
|
|
// WARNING: This function is called with the timer critical section still
|
|
// held -- better return quickly!
|
|
//****************************************************************************
|
|
void ProcessFrame(PNOTIFICATION_FRAME pnf)
|
|
{
|
|
void cplProcessNotification(PNOTIFICATION_FRAME pnf);
|
|
|
|
switch(pnf->dwType)
|
|
{
|
|
|
|
case TSPNOTIF_TYPE_CPL:
|
|
DPRINTF("ProcessFrame: Got CPL notification!\n");
|
|
cplProcessNotification(pnf);
|
|
break;
|
|
case TSPNOTIF_TYPE_DEBUG:
|
|
DPRINTF("ProcessFrame: Got DEBUG notifcation.\n");
|
|
traceProcessNotification(pnf);
|
|
break;
|
|
default:
|
|
DPRINTF1("WARNING:Got unknown notif type 0x%lu.\n", pnf->dwType);
|
|
break;
|
|
}
|
|
}
|