| ..back |
/**************************************************************************/
/** Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved. **/
/**************************************************************************/
//
// misc.c Support files for Telephony Service Provider Interface
//
// @doc EX_TSPI
//
// @topic TSPI |
//
// Tspi utilities
//
//
#include "windows.h"
#include "types.h"
#include "memory.h"
#include "mcx.h"
#include "tspi.h"
#include "tspip.h"
#include "linklist.h"
extern const WCHAR szSettings[];
extern const WCHAR szDialSuffix[];
const WCHAR szDefaultsName[] = TEXT("Drivers\\Unimodem");
// Unimodem thread priority
#define DEFAULT_THREAD_PRIORITY 150
#define REGISTRY_PRIORITY_VALUE TEXT("Priority256")
DWORD g_dwUnimodemThreadPriority;
const DEVMINICFG DefaultDevCfg =
{
DEVMINCFG_VERSION, // Version
0x00, // WaitBong
90, // CallSetupFail
0, // ModemOptions
19200, // Baud Rate
0, // Options
8, // Byte Size
0, // Stop Bits
0 // Parity
};
extern HANDLE g_hCoreDLL;
//
// Function to complete an outstanding async operation
// (unimodem can only handle one outstanding async operation per linedev)
//
// NOTE: Must be called holding the OpenCS
//
void
CompleteAsyncOp(
PTLINEDEV pLineDev
)
{
DWORD dwID;
DWORD dwStatus;
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:+CompleteAsyncOp\n")));
dwID = pLineDev->dwPendingID;
dwStatus = pLineDev->dwPendingStatus;
switch (pLineDev->dwPendingType) {
case PENDING_LINEACCEPT:
case PENDING_LINEANSWER:
case PENDING_LINEDEVSPECIFIC:
case PENDING_LINEDIAL:
case PENDING_LINEDROP:
case PENDING_LINEMAKECALL:
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_ASYNC, (TEXT("UNIMODEM:CompleteAsyncOp completing Op %s(0x%x)\n"),
GetPendingName(pLineDev->dwPendingType), dwID));
TspiGlobals.fnCompletionCallback(dwID, dwStatus);
EnterCriticalSection(&pLineDev->OpenCS);
break;
}
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
pLineDev->dwPendingStatus = 0;
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:-CompleteAsyncOp\n")));
} // CompleteAsyncOp
//
// NOTE on async operations:
// The function that invokes an async operation (TSPI_lineMakeCall, TSPI_lineAnswer, etc)
// first calls ControlThreadCmd which calls SetAsyncOp. Just before returning it calls
// SetAsyncID in order to avoid calling the TAPI completion callback before the user
// knows the async ID. The UnimodemControlThread will call SetAsyncStatus when the
// async operation has completed. If the ID has been set, then SetAsyncStatus will perform
// the callback.
//
//
// Set the async ID for a pending operation. This signals that the function which
// invoked the async operation has completed so any subsequent SetAsyncStatus call
// will cause a completion callback to TAPI. If the invoked async operation has
// already completed (SetAsyncStatus already called), then we need to kick off a
// thread to make the callback for this completed async operation.
//
// Return LINEERR_OPERATIONFAILED if this async operation has been aborted, else
// return async ID.
//
DWORD
SetAsyncID(
PTLINEDEV pLineDev,
DWORD dwID
)
{
DWORD dwRet = LINEERR_OPERATIONFAILED;
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:+SetAsyncID\n")));
EnterCriticalSection(&pLineDev->OpenCS);
if (pLineDev->dwPendingID != INVALID_PENDINGID) {
LeaveCriticalSection(&pLineDev->OpenCS);
} else {
pLineDev->dwPendingID = dwID;
dwRet = dwID;
//
// If the operation has completed then we need to do the callback on another thread
//
if (pLineDev->dwPendingStatus != LINEERR_ALLOCATED) {
LeaveCriticalSection(&pLineDev->OpenCS);
if (!StartCompleteAsyncOp(pLineDev)) {
dwRet = LINEERR_OPERATIONFAILED;
}
} else {
LeaveCriticalSection(&pLineDev->OpenCS);
}
}
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:-SetAsyncID 0x%x\n"), dwRet));
return dwRet;
} // SetAsyncID
//
// Set the status for a pending async operation and complete it if possible.
//
void
SetAsyncStatus(
PTLINEDEV pLineDev,
DWORD dwNewStatus
)
{
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:+SetAsyncStatus\n")));
EnterCriticalSection(&pLineDev->OpenCS);
//
// Is there a pending operation to complete?
//
if (pLineDev->dwPendingType != INVALID_PENDINGOP) {
pLineDev->dwPendingStatus = dwNewStatus;
if (pLineDev->dwPendingID != INVALID_PENDINGID) {
CompleteAsyncOp(pLineDev);
}
}
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:-SetAsyncStatus\n")));
} // SetAsyncStatus
//
// Abort any previously pending operation and record new pending operation
// (called by async TSPI_line* functions)
//
void
SetAsyncOp(
PTLINEDEV pLineDev,
DWORD dwNewOp
)
{
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:+SetAsyncOp\n")));
EnterCriticalSection(&pLineDev->OpenCS);
//
// Is there a pending operation that we need to abort?
//
if (pLineDev->dwPendingType != INVALID_PENDINGOP) {
LeaveCriticalSection(&pLineDev->OpenCS);
SetAsyncStatus(pLineDev, LINEERR_OPERATIONFAILED);
EnterCriticalSection(&pLineDev->OpenCS);
}
pLineDev->dwPendingType = dwNewOp;
pLineDev->dwPendingStatus = LINEERR_ALLOCATED;
pLineDev->dwPendingID = INVALID_PENDINGID;
LeaveCriticalSection(&pLineDev->OpenCS);
DEBUGMSG(ZONE_FUNC|ZONE_ASYNC, (TEXT("UNIMODEM:-SetAsyncOp\n")));
} // SetAsyncOp
typedef struct _ASYNCCMD {
DWORD dwRequestID;
DWORD dwResult;
} ASYNCCMD, * PASYNCCMD;
DWORD
AsyncCompleteThread(
PVOID lpvIn
)
{
PASYNCCMD pCmd = (PASYNCCMD)lpvIn;
DEBUGMSG(ZONE_ASYNC, (TEXT("UNIMODEM:AsyncCompleteThread completing ID(0x%x) result 0x%x\n"),
pCmd->dwRequestID, pCmd->dwResult));
TspiGlobals.fnCompletionCallback(pCmd->dwRequestID, pCmd->dwResult);
TSPIFree(pCmd);
return 0;
} // AsyncCompleteThread
BOOL
StartCompleteAsyncOp(
PTLINEDEV pLineDev
)
{
HANDLE hThd;
PASYNCCMD pCmd;
if (NULL == (pCmd = TSPIAlloc(sizeof(ASYNCCMD)))) {
DEBUGMSG(1, (TEXT("UNIMODEM:StartCompleteAsyncOp Unable to allocate ASYNCCMD!!!\n")));
return FALSE;
}
EnterCriticalSection(&pLineDev->OpenCS);
pCmd->dwRequestID = pLineDev->dwPendingID;
pCmd->dwResult = pLineDev->dwPendingStatus;
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
pLineDev->dwPendingStatus = 0;
LeaveCriticalSection(&pLineDev->OpenCS);
hThd = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)AsyncCompleteThread,
pCmd, 0, NULL );
if (!hThd) {
DEBUGMSG(1, (TEXT("UNIMODEM:StartCompleteAsyncOp Unable to Create AsyncCompleteThread!!!\n")));
TSPIFree(pCmd);
return FALSE;
}
CloseHandle(hThd);
return TRUE;
} // StartCompleteAsyncOp
//
// Read the default devconfig for this device from the registry
//
VOID
getDefaultDevConfig(
PTLINEDEV ptLineDev,
PDEVMINICFG pDevMiniCfg
)
{
static const WCHAR szDevConfig[] = TEXT("DevConfig");
DWORD dwSize, dwRetCode;
DEBUGMSG(ZONE_FUNC | ZONE_INIT,
(TEXT("UNIMODEM:+getDefaultDevConfig\r\n")));
// Get the default device config from the registry
dwSize = sizeof( DEVMINICFG );
dwRetCode = MdmRegGetValue(ptLineDev, NULL, szDevConfig, REG_BINARY,
(LPBYTE)pDevMiniCfg, &dwSize);
if( (dwRetCode != ERROR_SUCCESS) ||
(pDevMiniCfg->wVersion != DEVMINCFG_VERSION) ||
(dwSize < (sizeof(DEVMINICFG) - (DIAL_MODIFIER_LEN*sizeof(WCHAR)) )))
{
DEBUGMSG(ZONE_INIT | ZONE_ERROR,
(TEXT("UNIMODEM:Device Config error, retcode x%X, version x%X (x%X), Size x%X (x%X)\r\n"),
dwRetCode,
pDevMiniCfg->wVersion, DEVMINCFG_VERSION,
dwSize, (sizeof(DEVMINICFG) - (DIAL_MODIFIER_LEN*sizeof(WCHAR)) )) );
// Either the registry value is missing, or it is invalid.
// We need some default, use the one in ROM
*pDevMiniCfg = DefaultDevCfg;
}
pDevMiniCfg->szDialModifier[DIAL_MODIFIER_LEN] = 0; // make sure it is null terminated
DEBUGMSG(ZONE_INIT,
(TEXT("UNIMODEM:Done reading Device Config, retcode x%X\r\n"),
dwRetCode));
DEBUGMSG(ZONE_INIT,
(TEXT("UNIMODEM:Version x%X\tWait Bong x%X\tCall Setup Fail x%X\r\n"),
ptLineDev->DevMiniCfg.wVersion,
ptLineDev->DevMiniCfg.wWaitBong,
ptLineDev->DevMiniCfg.dwCallSetupFailTimer));
DEBUGMSG(ZONE_INIT,
(TEXT("UNIMODEM:Baud %d\tByte Size %d\tStop Bits %d\tParity %d\r\n"),
ptLineDev->DevMiniCfg.dwBaudRate,
ptLineDev->DevMiniCfg.ByteSize,
ptLineDev->DevMiniCfg.StopBits,
ptLineDev->DevMiniCfg.Parity));
DEBUGMSG(ZONE_FUNC | ZONE_INIT,
(TEXT("UNIMODEM:-getDefaultDevConfig\r\n")));
}
//
// Create a LineDev
//
PTLINEDEV
createLineDev(
HKEY hActiveKey,
LPCWSTR lpszDevPath,
LPCWSTR lpszDeviceName
)
{
PTLINEDEV ptLineDev;
PTLINEDEV ptExistingDev;
static const WCHAR szDeviceType[] = TEXT("DeviceType");
static const WCHAR szPortName[] = TEXT("Port");
static const WCHAR szFriendlyName[] = TEXT("FriendlyName");
static const WCHAR szPnpIdName[] = TEXT("PnpId");
static const WCHAR szMaxCmd[] = TEXT("MaxCmd");
static const WCHAR szDialBilling[] = TEXT("DialBilling");
DWORD dwSize, dwRetCode, dwTemp;
WCHAR szContinuation[16];
DEBUGMSG(ZONE_FUNC|ZONE_INIT,
(TEXT("UNIMODEM:+createLineDev\r\n")));
// We are being asked to create a new linedev - do it
if (!(ptLineDev = (PTLINEDEV) TSPIAlloc(sizeof(TLINEDEV)) )) {
return NULL;
}
// Initialize all of the fields
ptLineDev->dwPendingID = INVALID_PENDINGID;
ptLineDev->dwDefaultMediaModes = LINEMEDIAMODE_DATAMODEM; // Do this early, NullifyLine uses it
ptLineDev->bControlThreadRunning = FALSE;
NullifyLineDevice( ptLineDev, TRUE ); // also calls getDefaultDevConfig
// Get the device registry handle for later use.
dwRetCode = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
lpszDevPath,
0,
KEY_READ,
&ptLineDev->hSettingsKey);
if (ERROR_SUCCESS != dwRetCode) {
// We can't very well continue if the registry values aren't there.
DEBUGMSG(ZONE_ERROR|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev - RegOpenKeyEx %s returned %d.\r\n"),
lpszDevPath, dwRetCode));
goto abort_buffer_allocated;
} else {
DEBUGMSG(ZONE_MISC|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev - RegOpenKeyEx %s handle x%X.\r\n"),
lpszDevPath, ptLineDev->hSettingsKey));
}
// Record the port name, or read it ourselves if NULL
if( NULL == lpszDeviceName ) {
// Get the port name from registry
dwSize = MAXDEVICENAME;
dwRetCode = MdmRegGetValue(ptLineDev, NULL, szPortName, REG_SZ,
(LPBYTE)ptLineDev->szDeviceName, &dwSize);
if( dwRetCode != ERROR_SUCCESS ) {
// We can't do much without a port, abort now.
goto abort_registry_opened;
}
} else {
wcscpy( ptLineDev->szDeviceName, lpszDeviceName );
}
// Get the device type from registry
dwSize = sizeof( DWORD );
dwRetCode = MdmRegGetValue(ptLineDev, NULL, szDeviceType, REG_DWORD,
(LPBYTE)&dwTemp, &dwSize);
if( dwRetCode == ERROR_SUCCESS ) {
DEBUGMSG(ZONE_MISC|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev - Device type x%X.\r\n"), dwTemp));
ptLineDev->wDeviceType = (WORD)dwTemp;
} else {
DEBUGMSG(ZONE_MISC|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev - Device type defaulting to NULL_MODEM.\r\n")));
ptLineDev->wDeviceType = DT_NULL_MODEM; // default type is null
}
// Get the maximum command length from registry
dwSize = sizeof( DWORD );
dwRetCode = MdmRegGetValue(ptLineDev, szSettings, szMaxCmd, REG_DWORD,
(LPBYTE)&dwTemp, &dwSize);
if( dwRetCode == ERROR_SUCCESS ) {
DEBUGMSG(ZONE_MISC|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev - Max command length x%X.\r\n"), dwTemp));
ptLineDev->dwMaxCmd = (WORD)dwTemp;
} else {
DEBUGMSG(ZONE_MISC|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev - Max command length defaulting to x%X.\r\n"), MAX_CMD_LENGTH));
ptLineDev->dwMaxCmd = MAX_CMD_LENGTH;
}
// Get modem command continuation character - assume it is the first character of the dial suffix
dwSize = sizeof( szContinuation );
if (ERROR_SUCCESS != MdmRegGetValue(ptLineDev, szSettings, szDialSuffix,
REG_SZ, (PUCHAR)szContinuation, &dwSize) ) {
ptLineDev->chContinuation = ';';
} else {
ptLineDev->chContinuation = szContinuation[0];
}
//
// Get modem dial capabilities
//
ptLineDev->dwDevCapFlags = LINEDEVCAPFLAGS_DIALQUIET |
LINEDEVCAPFLAGS_DIALDIALTONE |
LINEDEVCAPFLAGS_DIALBILLING;
//
// Assume dial billing capability (wait for bong, '$' in a dial string)
//
dwSize = sizeof(DWORD);
if (ERROR_SUCCESS == MdmRegGetValue(ptLineDev, szSettings, szDialBilling,
REG_DWORD, (PUCHAR)&dwTemp, &dwSize) ) {
if (dwTemp == 0) {
ptLineDev->dwDevCapFlags &= ~LINEDEVCAPFLAGS_DIALBILLING;
}
}
// Get the friendly name from registry
dwSize = MAXDEVICENAME;
dwRetCode = MdmRegGetValue(ptLineDev, NULL, szFriendlyName, REG_SZ,
(LPBYTE)ptLineDev->szFriendlyName, &dwSize);
if( dwRetCode != ERROR_SUCCESS ) { // Friendly not found, use something else
// For PCMCIA cards, use PNP-Id. For other devices, use port name.
if( DT_PCMCIA_MODEM == ptLineDev->wDeviceType ) {
DWORD dwType;
WCHAR szPnpId[MAXDEVICENAME];
dwType = REG_SZ;
dwSize = MAXDEVICENAME;
dwRetCode = RegQueryValueEx(hActiveKey,
szPnpIdName,
NULL,
&dwType,
(PUCHAR)szPnpId,
&dwSize);
if ( dwRetCode != ERROR_SUCCESS) {
wcsncpy( ptLineDev->szFriendlyName, TEXT("Generic Hayes PCMCIA Modem"), MAXDEVICENAME );
DEBUGMSG( ZONE_INIT|ZONE_ERROR,
(TEXT("UNIMODEM:Unable to read PnpId, defaulting to %s\r\n"),
ptLineDev->szFriendlyName));
} else {
// Remove the checksum from PnpId
dwSize = wcslen(szPnpId);
if( dwSize > 5 ) {
szPnpId[dwSize - 5] = (WCHAR)0;
}
// Now create a pseudo-friendly name
#ifdef PREPEND_GENERIC_TO_NAME
wcsncpy( ptLineDev->szFriendlyName, TEXT("Generic "), MAXDEVICENAME );
wcsncat( ptLineDev->szFriendlyName, szPnpId, MAXDEVICENAME );
#else
wcsncpy( ptLineDev->szFriendlyName, szPnpId, MAXDEVICENAME );
#endif
DEBUGMSG(ZONE_INIT,
(TEXT("UNIMODEM:Generic PCMCIA, friendly name %s\r\n"),
ptLineDev->szFriendlyName));
}
} else {
// Non PCMCIA & no friendly name, just use the port name
wcsncpy( ptLineDev->szFriendlyName, ptLineDev->szDeviceName, MAXDEVICENAME );
}
}
DEBUGMSG(ZONE_INIT,
(TEXT("UNIMODEM:Done reading friendly name, retcode x%X, name %s\r\n"),
dwRetCode, ptLineDev->szFriendlyName));
//----------------------------------------------------------------------
// OK, now that we have all these names, lets see if this same device
// already exists (it may be a PCMCIA card that was removed and then
// reinserted). If it exists, then just reuse the existing entry and
// let TAPI know that the device reconnected.
//----------------------------------------------------------------------
if( ptExistingDev = LineExists( ptLineDev ) ) {
// We already know about this device
ptExistingDev->hSettingsKey = ptLineDev->hSettingsKey; // Get new handle
TSPIFree( ptLineDev ); // Free the entry we started to create.
// If card is reiniserted, mark it as available.
ptExistingDev->wDeviceAvail = 1;
CallLineEventProc(
ptExistingDev,
0,
LINE_LINEDEVSTATE,
LINEDEVSTATE_CONNECTED|LINEDEVSTATE_INSERVICE,
0,
0);
return ptExistingDev; // And return the original device
} else {
// This really is a new device. Create whatever all we need.
// If we are creating a device, it must be available.
ptLineDev->wDeviceAvail = 1;
// Create an event for timeouts, and one for call completion
ptLineDev->hTimeoutEvent = CreateEvent(0,FALSE,FALSE,NULL);
ptLineDev->hCallComplete = CreateEvent(0,FALSE,FALSE,NULL);
// Close needs a critical section
InitializeCriticalSection(&ptLineDev->OpenCS);
if( IS_NULL_MODEM(ptLineDev) )
ptLineDev->dwBearerModes = LINEBEARERMODE_DATA | LINEBEARERMODE_PASSTHROUGH;
else
ptLineDev->dwBearerModes = LINEBEARERMODE_DATA | LINEBEARERMODE_VOICE | LINEBEARERMODE_PASSTHROUGH;
// And add this new line device to list
InsertHeadLockedList(&TspiGlobals.LineDevs,
&ptLineDev->llist,
&TspiGlobals.LineDevsCS);
DEBUGMSG(ZONE_ALLOC, (TEXT("UNIMODEM:Insert Line Device in List\r\n")));
// OK, lets notify TAPI about our new device
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev, calling TAPI Line Create Proc\r\n")));
// Let TAPI know about our new device
CallLineEventProc(NULL, NULL, LINE_CREATE,
(DWORD)TspiGlobals.hProvider,
(DWORD)&ptLineDev->dwDeviceID, 0L);
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("UNIMODEM:createLineDev, TAPI assigned device ID 0x%X\r\n"), ptLineDev->dwDeviceID));
DEBUGMSG(ZONE_FUNC|ZONE_INIT,
(TEXT("UNIMODEM:-createLineDev\r\n")));
return ptLineDev;
}
abort_registry_opened:
RegCloseKey( ptLineDev->hSettingsKey );
abort_buffer_allocated:
TSPIFree( ptLineDev );
DEBUGMSG(ZONE_FUNC|ZONE_INIT,
(TEXT("UNIMODEM:-createLineDev, aborted\r\n")));
return NULL;
}
/* NOT USED
//
// Destroy a LineDev
//
void
DestroyLineDev(
PTLINEDEV ptLineDev
)
{
DEBUGMSG(ZONE_FUNC|ZONE_INIT,
(TEXT("UNIMODEM:+destroyLineDev\r\n")));
// First, we better make sure no one is still trying to use line
DevlineClose ( ptLineDev, TRUE );
// Delete the timeout event and CallComplete event.
CloseHandle( ptLineDev->hTimeoutEvent );
CloseHandle( ptLineDev->hCallComplete );
// Close any critical sections
DeleteCriticalSection( &ptLineDev->OpenCS );
// Remove this line device from the list
InsertHeadLockedList(&TspiGlobals.LineDevs,
&ptLineDev->llist,
&TspiGlobals.LineDevsCS);
DEBUGMSG(ZONE_ALLOC, (TEXT("Removed Line Device from List\r\n")));
// Let TAPI know the device went away
CallLineEventProc(NULL, NULL, LINE_REMOVE,
(DWORD)TspiGlobals.hProvider,
(DWORD)ptLineDev->dwDeviceID, 0L);
DEBUGMSG(ZONE_FUNC|ZONE_INIT,
(TEXT("UNIMODEM:-destroyLineDev\r\n")));
// Close any registry keys we held
RegCloseKey( ptLineDev->hSettingsKey );
// And free the structure
TSPIFree( ptLineDev );
DEBUGMSG(ZONE_FUNC|ZONE_INIT,
(TEXT("UNIMODEM:-destroyLineDev\r\n")));
return;
}
*/
// ****************************************************************************
// GetLineDevfromID()
//
// Function: This function looks for the LineDev associated with an ID
//
// Returns: PLINEDEV pointer (or NULL if not found)
//
// ****************************************************************************
PTLINEDEV
GetLineDevfromID(
DWORD dwDeviceID
)
{
PLIST_ENTRY ptEntry;
PTLINEDEV ptLineDev;
DEBUGMSG(ZONE_LIST,
(TEXT("UNIMODEM:+GetLineDevfromID\r\n")));
// Walk the Line List and find corresponding handle
ptEntry = TspiGlobals.LineDevs.Flink;
while( ptEntry != &TspiGlobals.LineDevs )
{
ptLineDev = CONTAINING_RECORD( ptEntry, TLINEDEV, llist);
DEBUGMSG(ZONE_LIST,
(TEXT("UNIMODEM:GetLineDevfromID ptLineDev->dwDeviceID x%X, dwID x%X\r\n"),
ptLineDev->dwDeviceID, dwDeviceID));
// See if this is the correct Line
if( ptLineDev->dwDeviceID == dwDeviceID )
{
DEBUGMSG(ZONE_LIST,
(TEXT("UNIMODEM:-GetLineDevfromID - Success\r\n")));
return ptLineDev; // Cool, we found it
}
// No match yet, advance to next link
ptEntry = ptEntry->Flink;
}
DEBUGMSG(ZONE_LIST,
(TEXT("UNIMODEM:-GetLineDevfromID - Fail\r\n")));
return NULL;
}
PTLINEDEV
GetLineDevfromName(
LPCWSTR lpszDeviceName,
LPCWSTR lpszFriendlyName
)
{
PLIST_ENTRY ptEntry;
PTLINEDEV ptLineDev;
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:+GetLineDevfromName\r\n")));
// Walk the Line List and find corresponding handle
ptEntry = TspiGlobals.LineDevs.Flink;
while( ptEntry != &TspiGlobals.LineDevs )
{
ptLineDev = CONTAINING_RECORD( ptEntry, TLINEDEV, llist);
// See if this is the correct Line
if (lpszDeviceName && lpszFriendlyName) {
if (!wcscmp(lpszDeviceName, ptLineDev->szDeviceName)) {
if (!wcscmp(lpszFriendlyName, ptLineDev->szFriendlyName)) {
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:-GetLineDevfromName - Success both\r\n")));
return ptLineDev;
}
}
} else if (lpszDeviceName) {
//
// When going by device name, only look at active devices since the removal code
// is calling us and there may be multiple line devices on the same COMn: device.
//
if (ptLineDev->wDeviceAvail) {
if (!wcscmp(lpszDeviceName, ptLineDev->szDeviceName)) {
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:-GetLineDevfromName - Success device name\r\n")));
return ptLineDev;
}
}
} else if (lpszFriendlyName) {
if (!wcscmp(lpszFriendlyName, ptLineDev->szFriendlyName)) {
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:-GetLineDevfromName - Success friendly name\r\n")));
return ptLineDev;
}
}
// Advance to next link.
ptEntry = ptEntry->Flink;
}
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:-GetLineDevfromName - Fail\r\n")));
return NULL;
} // GetLineDevfromName
// ****************************************************************************
//
// Function: This function looks for the LineDev which matches the names and
// device types of the passed in LineDev. This is so that we can
// determine if a lineCreate is really just the reinsertion of a
// PCMCIA card that we already know about.
//
// Returns: PLINEDEV pointer (or NULL if not found)
//
// ****************************************************************************
PTLINEDEV
LineExists(
PTLINEDEV ptNewLine
)
{
PLIST_ENTRY ptEntry;
PTLINEDEV ptOldLine;
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:+LineExists - Device type x%X\r\n"), ptNewLine->wDeviceType));
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:LineExists\tDevice Name : %s\r\n"), ptNewLine->szDeviceName));
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:LineExists\tFriendly Name : %s\r\n"), ptNewLine->szFriendlyName));
// Walk the Line List and find corresponding handle
ptEntry = TspiGlobals.LineDevs.Flink;
while( ptEntry != &TspiGlobals.LineDevs )
{
ptOldLine = CONTAINING_RECORD( ptEntry, TLINEDEV, llist);
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:LineExists - checking old dwDeviceID x%X\r\n"), ptOldLine->dwDeviceID));
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:LineExists\tDevice Type : %x\r\n"), ptOldLine->wDeviceType));
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:LineExists\tDevice Name : %s\r\n"), ptOldLine->szDeviceName));
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:LineExists\tFriendly Name : %s\r\n"), ptOldLine->szFriendlyName));
// See if this is the correct Line
if( (ptOldLine->wDeviceType == ptNewLine->wDeviceType) &&
(! wcscmp(ptOldLine->szDeviceName, ptNewLine->szDeviceName)) &&
(! wcscmp(ptOldLine->szFriendlyName, ptNewLine->szFriendlyName)) )
{
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:-LineExists - previous was ID x%X\r\n"), ptOldLine->dwDeviceID ));
return ptOldLine; // this card has been reinserted
}
// No match yet, advance to next link
ptEntry = ptEntry->Flink;
}
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:-LineExists - Fail\r\n")));
return NULL;
}
// ****************************************************************************
// GetLineDevfromHandle()
//
// Function: This function gets the Line Dev Pointer from a handle
//
// Returns: a pointer to PLINEDEV structure if the handle is valid, or
// NULL otherwise
//
// ****************************************************************************
PTLINEDEV
GetLineDevfromHandle (
DWORD handle
)
{
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:+GetLineDevfromHandle\r\n")));
// The handle IS the pointer.
// However in the future we may need to provide some abstraction (as well
// potential validity checks) so this function prepares for that,
DEBUGMSG(ZONE_LIST, (TEXT("UNIMODEM:-GetLineDevfromHandle\r\n")));
return (PTLINEDEV)handle;
}
//
//
void
InitVarData(
LPVOID lpData,
DWORD dwSize
)
{
LPVARSTRING pVarData = (LPVARSTRING)lpData; // Cast the pointer to a varstring
memset(&(pVarData->dwNeededSize), 0, pVarData->dwTotalSize - sizeof(DWORD));
pVarData->dwNeededSize = dwSize;
pVarData->dwUsedSize = dwSize;
}
//
// This routine is called when the TSPI DLL is loaded. It is primarily
// responsible for initializing TspiGlobals.
//
// Return : void
//
void
TSPIDLL_Load(
void
)
{
DWORD dwRet;
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("+TSPIDLL_Load\r\n")));
TspiGlobals.hInstance = 0;
TspiGlobals.dwProviderID = 0;
TspiGlobals.hProvider = 0;
TspiGlobals.fnLineEventProc =0 ;
TspiGlobals.fnCompletionCallback = 0;
// Let's init our linked lists to empty
InitializeListHead( &TspiGlobals.LineDevs );
// And init the linked list critical sections
InitializeCriticalSection( &TspiGlobals.LineDevsCS );
// Go ahead and open the unimodem defaults registry key.
dwRet = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
szDefaultsName,
0,
KEY_READ,
&TspiGlobals.hDefaultsKey);
if (ERROR_SUCCESS != dwRet)
{
DEBUGMSG(ZONE_ERROR|ZONE_INIT,
(TEXT("TSPIDLL_LOAD - RegOpenKeyEx %s returned %d.\r\n"),
szDefaultsName, dwRet));
}
else
{
DWORD dwValType;
DWORD dwValLen;
DEBUGMSG(ZONE_MISC|ZONE_INIT,
(TEXT("TSPIDLL_LOAD - RegOpenKeyEx %s handle x%X.\r\n"),
szDefaultsName, TspiGlobals.hDefaultsKey));
// Get thread priority
g_dwUnimodemThreadPriority = DEFAULT_THREAD_PRIORITY;
dwValLen = sizeof(DWORD);
RegQueryValueEx(
TspiGlobals.hDefaultsKey,
REGISTRY_PRIORITY_VALUE,
NULL,
&dwValType,
(PUCHAR)&g_dwUnimodemThreadPriority,
&dwValLen);
DEBUGMSG(ZONE_MISC|ZONE_INIT,
(TEXT("TSPIDLL_LOAD - Priority %d\r\n"),
g_dwUnimodemThreadPriority));
}
g_hCoreDLL = LoadLibrary(L"coredll.dll");
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("-TSPIDLL_Load\r\n")));
}
//****************************************************************************
//
// NullifyLineDevice()
//
//****************************************************************************
DWORD
NullifyLineDevice (
PTLINEDEV pLineDev,
BOOL bDefaultConfig
)
{
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+NullifyLineDevice\r\n")));
// Turn the line device back to its initial state
pLineDev->hDevice = (HANDLE)INVALID_DEVICE;
pLineDev->hDevice_r0 = (HANDLE)INVALID_DEVICE;
pLineDev->hMdmLog = (HANDLE)INVALID_DEVICE;
pLineDev->pidDevice = 0L;
pLineDev->lpfnEvent = NULL;
pLineDev->DevState = DEVST_DISCONNECTED;
pLineDev->hwndLine = NULL;
pLineDev->szAddress[0] = '\0';
pLineDev->hwTermCtrl = NULL;
pLineDev->dwCallFlags = 0L;
pLineDev->dwCallState = LINECALLSTATE_IDLE;
pLineDev->dwCurMediaModes = 0L;
pLineDev->dwDetMediaModes = 0L;
pLineDev->fTakeoverMode = FALSE;
pLineDev->dwMediaModes = pLineDev->dwDefaultMediaModes;
pLineDev->dwDialOptions = 0L;
pLineDev->dwNumRings = 0;
if (bDefaultConfig) {
getDefaultDevConfig( pLineDev, &pLineDev->DevMiniCfg ); // reset the devconfig
}
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-NullifyLineDevice\r\n")));
return SUCCESS;
}
//****************************************************************************
//
// ValidateDevCfgClass()
//
//****************************************************************************
BOOL
ValidateDevCfgClass (
LPCWSTR lpszDeviceClass
)
{
UINT idClass;
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:+ValidateDevCfgClass\r\n")));
// 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:
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ValidateDevCfgClass (Valid)\r\n")));
return TRUE;
default:
DEBUGMSG(ZONE_FUNC, (TEXT("UNIMODEM:-ValidateDevCfgClass (Invalid)\r\n")));
return FALSE;
};
}
//****************************************************************************
// ValidateAddress()
//
// Function: This function validates a tapi address and creates a version of
// it to pass to the VxD.
//
// Returns: SUCCESS or LINEERR_xxx depending on the failure reason
//
//****************************************************************************
LONG
ValidateAddress(
PTLINEDEV pLineDev,
LPCWSTR lpszInAddress,
LPWSTR lpszOutAddress)
{
LPCWSTR lpszSrc;
int cbOutLen = MAXADDRESSLEN;
WCHAR chContinuation = pLineDev->chContinuation;
ASSERT(lpszOutAddress);
// is lpszInAddress NULL?
if (lpszInAddress == NULL || *lpszInAddress == 0)
{
*lpszOutAddress = 0;
return SUCCESS;
}
// Verify that the first char is a valid single-byte char.
if (AnsiNext(lpszInAddress) - lpszInAddress != 1)
{
return LINEERR_INVALADDRESS;
}
// tone or pulse? set dwDialOptions appropriately
// also, set lpszSrc
switch (*lpszInAddress) {
case 'T': // tone
case 't':
lpszSrc = lpszInAddress + 1;
pLineDev->dwDialOptions |= MDM_TONE_DIAL;
break;
case 'P': // pulse
case 'p':
lpszSrc = lpszInAddress + 1;
pLineDev->dwDialOptions &= ~MDM_TONE_DIAL;
break;
default:
lpszSrc = lpszInAddress;
pLineDev->dwDialOptions |= MDM_TONE_DIAL;
break;
}
// 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 = pLineDev->DevMiniCfg.wWaitBong;
// 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))
{
return LINEERR_DIALQUIET;
}
break;
case 'W':
case 'w':
if (!(pLineDev->dwDevCapFlags & LINEDEVCAPFLAGS_DIALDIALTONE))
{
return LINEERR_DIALDIALTONE;
}
break;
case '?':
return LINEERR_DIALPROMPT;
case '|': // subaddress
case '^': // name field
goto Skip_The_Rest;
case ' ':
case '-':
// skip these characters
goto Skip_This_Character;
}
if (*lpszSrc == chContinuation) {
// This signifies the end of a dialable address.
// Use it and skip the rest.
*lpszOutAddress++ = *lpszSrc;
goto Skip_The_Rest;
}
// Copy this character
*lpszOutAddress++ = *lpszSrc;
cbOutLen--;
Skip_This_Character:
// Verify that the next char is a valid single-byte char.
if (AnsiNext(lpszSrc) - lpszSrc != 1)
{
return LINEERR_INVALADDRESS;
}
lpszSrc++;
}
// Did we run out of space in the outgoing buffer?
if (*lpszSrc && cbOutLen == 0)
{
// yes
DEBUGMSG(ZONE_ERROR,
(TEXT("\t*** Address too large.\r\n")));
return LINEERR_INVALADDRESS;
}
Skip_The_Rest:
*lpszOutAddress = 0;
return SUCCESS;
}
//
// Call TAPI's Line Event Proc with status message filtering
//
void
CallLineEventProc(
PTLINEDEV ptLine,
HTAPICALL htCall,
DWORD dwMsg,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3
)
{
HTAPILINE htLine;
htLine = (ptLine) ? ptLine->htLine : NULL;
if (dwMsg == LINE_LINEDEVSTATE) {
//
// Honor the lineSetStatusMessages request
//
if ((dwParam1 & ptLine->dwLineStatesMask) == 0) {
return;
}
}
TspiGlobals.fnLineEventProc(
htLine,
htCall,
dwMsg,
dwParam1,
dwParam2,
dwParam3
);
} // CallLineEventProc
#ifdef DEBUG
LPWSTR
CallStateName(
DWORD dwCallState
)
{
LPWSTR lpszRet;
switch (dwCallState) {
case LINECALLSTATE_IDLE: lpszRet = L"LINECALLSTATE_IDLE"; break;
case LINECALLSTATE_OFFERING: lpszRet = L"LINECALLSTATE_OFFERING"; break;
case LINECALLSTATE_ACCEPTED: lpszRet = L"LINECALLSTATE_ACCEPTED"; break;
case LINECALLSTATE_DIALTONE: lpszRet = L"LINECALLSTATE_DIALTONE"; break;
case LINECALLSTATE_DIALING: lpszRet = L"LINECALLSTATE_DIALING"; break;
case LINECALLSTATE_RINGBACK: lpszRet = L"LINECALLSTATE_RINGBACK"; break;
case LINECALLSTATE_BUSY: lpszRet = L"LINECALLSTATE_BUSY"; break;
case LINECALLSTATE_SPECIALINFO: lpszRet = L"LINECALLSTATE_SPECIALINFO"; break;
case LINECALLSTATE_CONNECTED: lpszRet = L"LINECALLSTATE_CONNECTED"; break;
case LINECALLSTATE_PROCEEDING: lpszRet = L"LINECALLSTATE_PROCEEDING"; break;
case LINECALLSTATE_ONHOLD: lpszRet = L"LINECALLSTATE_ONHOLD"; break;
case LINECALLSTATE_CONFERENCED: lpszRet = L"LINECALLSTATE_CONFERENCED"; break;
case LINECALLSTATE_ONHOLDPENDCONF: lpszRet = L"LINECALLSTATE_ONHOLDPENDCONF"; break;
case LINECALLSTATE_ONHOLDPENDTRANSFER: lpszRet = L"LINECALLSTATE_ONHOLDPENDTRANSFER"; break;
case LINECALLSTATE_DISCONNECTED: lpszRet = L"LINECALLSTATE_DISCONNECTED"; break;
case LINECALLSTATE_UNKNOWN: lpszRet = L"LINECALLSTATE_UNKNOWN"; break;
default: lpszRet = L"UNDEFINED!!!"; break;
}
return lpszRet;
}
#endif
void
NewCallState(
PTLINEDEV pLineDev,
DWORD dwCallState,
DWORD dwCallState2
)
{
DEBUGMSG(ZONE_CALLS, (L"UNIMODEM:NewCallState for Device %d = %s\n", pLineDev->dwDeviceID, CallStateName(dwCallState)));
pLineDev->dwCallState = dwCallState;
pLineDev->dwCallStateMode = dwCallState2;
CallLineEventProc(
pLineDev,
pLineDev->htCall,
LINE_CALLSTATE,
dwCallState,
dwCallState2,
pLineDev->dwCurMediaModes
);
} // NewCallState
typedef int (WINAPI * PFN_LOADSTRING)(HINSTANCE,UINT,LPWSTR,int);
//
// Wrapper to load the termctrl DLL and then call TerminalWindow
//
BOOL
DisplayTerminalWindow(
PTLINEDEV pLineDev,
DWORD dwTitle
)
{
PFNTERMINALWINDOW pfnTerminalWindow;
HANDLE hTermCtrl;
WCHAR szWindowTitle[128];
BOOL bRet;
PFN_LOADSTRING pfnLoadString;
bRet = FALSE;
// Load the termctrl dll, and get the entrypoint
if ((hTermCtrl = LoadLibrary(TEXT("termctrl.dll"))) == NULL) {
DEBUGMSG(ZONE_ERROR|ZONE_INIT,
(TEXT("UNIMODEM:DisplayTerminalWindow *** Unable to load termctrl dll\r\n")));
goto dtw_exit;
}
// OK, lets get the function pointer.
if ((pfnTerminalWindow = (PFNTERMINALWINDOW)GetProcAddress(hTermCtrl, TEXT("TerminalWindow"))) == NULL) {
DEBUGMSG(ZONE_ERROR|ZONE_INIT,
(TEXT("UNIMODEM:DisplayTerminalWindow *** GetProcAddress(TerminalWindow) failed\r\n")));
goto dtw_exit;
}
if ((pfnLoadString = (PFN_LOADSTRING)GetProcAddress(g_hCoreDLL, L"LoadStringW")) == NULL) {
DEBUGMSG(ZONE_ERROR|ZONE_INIT,
(TEXT("UNIMODEM:DisplayTerminalWindow *** GetProcAddress(LoadStringW) failed\r\n")));
goto dtw_exit;
}
pfnLoadString(TspiGlobals.hInstance, dwTitle, szWindowTitle, sizeof(szWindowTitle)/sizeof(WCHAR));
//
// Make UI run in non-realtime thread priority
//
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
pLineDev->hwTermCtrl = NULL; // make sure handle is NULL before calling
if (TW_SUCCESS == pfnTerminalWindow(pLineDev->hDevice, szWindowTitle, &pLineDev->hwTermCtrl)) {
bRet = TRUE;
}
pLineDev->hwTermCtrl = NULL; // Zero Window handle now that we are done
CeSetThreadPriority(GetCurrentThread(), g_dwUnimodemThreadPriority);
dtw_exit:
if(hTermCtrl) {
FreeLibrary(hTermCtrl);
}
return bRet;
}
Legal Declaration: it is for studying wince(MicroSoft Windows CE) only! : http://www.vxworks6.com