Sample TSP Source File (Windows CE 5.0)
The following code example illustrates how to write a customized TSP by using the TSPI that is supported in Windows CE.
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved.
Module Name:
Samptspi.c
Abstract:
Core functions for the Windows CE sample TAPI Service Provider
Notes:
--*/
#include "Windows.h"
#include "Types.h"
#include "Tchar.h"
#include "Memory.h"
#include "Mcx.h"
#include "Tspi.h"
#include "Linklist.h"
#include "Samptspi.h"
#include "Tapicomn.h"
TSPIGLOBALS TspiGlobals;
const GETIDINFO aGetID[] ={{TEXT("tapi/line"), STRINGFORMAT_BINARY},
{TEXT("comm"), STRINGFORMAT_UNICODE},
{TEXT("comm/datamodem"),STRINGFORMAT_BINARY},
{TEXT("ndis"), STRINGFORMAT_BINARY}};
const TCHAR g_szDeviceClass[] = TEXT("com");
// Some generic strings for later
//
const TCHAR szSemicolon[] = TEXT(";");
DEVCFG DefaultDevCfg;
// Debug Zones.
#ifdef DEBUG
// Defines to ease setting of dpCurSettings.ulZoneMask
#define DEBUG_INIT 0x0001
#define DEBUG_CALLSTATE 0x0400
#define DEBUG_MISC 0x0800
#define DEBUG_ALLOC 0x1000
#define DEBUG_FUNCTION 0x2000
#define DEBUG_WARNING 0x4000
#define DEBUG_ERROR 0x8000
DBGPARAM dpCurSettings = {
TEXT("Samptspi"), {
TEXT("Init"),TEXT(""),TEXT(""),TEXT(""),
TEXT(""),TEXT(""),TEXT(""),TEXT(""),
TEXT(""),TEXT(""),TEXT("Call State"),TEXT("Misc"),
TEXT("Alloc"),TEXT("Function"),TEXT("Warning"),TEXT("Error") },
DEBUG_INIT
};
#endif
extern BOOL LineConfigEdit(HWND hParent, PDEVCFG pCfg);
// **********************************************************************
// TSPI_provider functions
// **********************************************************************
LONG TSPIAPI
TSPI_providerInit(
DWORD dwTSPIVersion, // TSPI Version - in
DWORD dwPermanentProviderID, // Permanent Provider ID - in
DWORD dwLineDeviceIDBase, // Line Base ID - in
DWORD dwPhoneDeviceIDBase, // Phone Base ID - in
DWORD dwNumLines, // Number of lines - in
DWORD dwNumPhones, // Number of phones - in
ASYNC_COMPLETION lpfnCompletionProc, // Pointer to callback - in
LPDWORD lpdwTSPIOptions // Optional Behavior Flags - out
)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("+TSPI_providerInit, dwPPID 0x%X, dwDeviceIDBase 0x%X, dwNumLines 0x%X\r\n"),
dwPermanentProviderID,
dwLineDeviceIDBase,
dwNumLines));
TspiGlobals.fnCompletionCallback = lpfnCompletionProc;
DefaultDevCfg.wWaitBong = 90;
DefaultDevCfg.dwModemOptions = MDM_FLOWCONTROL_SOFT;
DefaultDevCfg.dwCallSetupFailTimer = 180;
DefaultDevCfg.commconfig.dwSize = sizeof(COMMCONFIG);
DefaultDevCfg.commconfig.wVersion = SAMPTSPI_VERSION;
DefaultDevCfg.commconfig.wReserved = 0;
DefaultDevCfg.commconfig.dwProviderSubType = SAMPTSPI_SUBTYPE;
DefaultDevCfg.commconfig.dwProviderOffset = 0;
DefaultDevCfg.commconfig.dwProviderSize = 0;
DefaultDevCfg.commconfig.dcb.DCBlength = sizeof(DCB);
DefaultDevCfg.commconfig.dcb.BaudRate = CBR_19200;
DefaultDevCfg.commconfig.dcb.fBinary = TRUE;
DefaultDevCfg.commconfig.dcb.fParity = TRUE;
DefaultDevCfg.commconfig.dcb.fOutxCtsFlow = TRUE;
DefaultDevCfg.commconfig.dcb.fOutxDsrFlow = TRUE;
DefaultDevCfg.commconfig.dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
DefaultDevCfg.commconfig.dcb.fDsrSensitivity = TRUE;
DefaultDevCfg.commconfig.dcb.fTXContinueOnXoff = TRUE;
DefaultDevCfg.commconfig.dcb.fOutX = FALSE;
DefaultDevCfg.commconfig.dcb.fInX = FALSE;
DefaultDevCfg.commconfig.dcb.fErrorChar = FALSE;
DefaultDevCfg.commconfig.dcb.fNull = FALSE;
DefaultDevCfg.commconfig.dcb.fRtsControl = RTS_CONTROL_TOGGLE;
DefaultDevCfg.commconfig.dcb.fAbortOnError = TRUE;
DefaultDevCfg.commconfig.dcb.wReserved = 0;
DefaultDevCfg.commconfig.dcb.XonLim = 3;
DefaultDevCfg.commconfig.dcb.XoffLim = 12;
DefaultDevCfg.commconfig.dcb.ByteSize = 8;
DefaultDevCfg.commconfig.dcb.Parity = EVENPARITY;
DefaultDevCfg.commconfig.dcb.StopBits = ONESTOPBIT;
DefaultDevCfg.commconfig.dcb.XonChar = 0;
DefaultDevCfg.commconfig.dcb.XoffChar = 0;
DefaultDevCfg.commconfig.dcb.ErrorChar = 0;
DefaultDevCfg.commconfig.dcb.EofChar = 0;
DefaultDevCfg.commconfig.dcb.EvtChar = 0;
DefaultDevCfg.commconfig.dcb.wReserved1 = 0;
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("-TSPI_providerInit\r\n")));
return SUCCESS;
}
LONG TSPIAPI
TSPI_providerInstall(
HWND hwndOwner,
DWORD dwPermanentProviderID
)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("+TSPI_providerInstall, dwPPID 0x%X\r\n"),
dwPermanentProviderID ));
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("-TSPI_providerInstall\r\n")));
return SUCCESS;
}
LONG TSPIAPI
TSPI_providerShutdown(
DWORD dwTSPIVersion
)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("+TSPI_providerShutdown\r\n")));
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("-TSPI_providerShutdown\r\n")));
return SUCCESS;
}
LONG TSPIAPI TSPI_providerEnumDevices(DWORD dwPermanentProviderID,
LPDWORD lpdwNumLines,
LPDWORD lpdwNumPhones,
HPROVIDER hProvider,
LINEEVENT lpfnLineEventProc,
PHONEEVENT lpfnPhoneEventProc
)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("+TSPI_providerEnumDevices\r\n")));
*lpdwNumLines = 0;
// This should be the same event proc that gets passed in to
// lineOpen, but I need it here and now so that I can notify
// TAPI about devices that are coming and going.
TspiGlobals.fnLineEventProc = lpfnLineEventProc;
TspiGlobals.dwProviderID = dwPermanentProviderID;
TspiGlobals.hProvider = hProvider;
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("-TSPI_providerEnumDevices\r\n")));
return SUCCESS;
}
// **********************************************************************
// TSPI_line functions
// **********************************************************************
//
// This function serves as a stub in the vtbl for any of the TSPI
// functions that are chosen not to be supported.
//
LONG TSPIAPI
TSPI_Unsupported( void )
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_Unsupported\r\n")));
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_Unsupported\r\n")));
return LINEERR_OPERATIONUNAVAIL;
}
LONG TSPIAPI
TSPI_lineClose(
HDRVLINE hdLine
)
{
PTLINEDEV pLineDev;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineClose\r\n")));
if ((pLineDev = GetLineDevfromHandle ((DWORD)hdLine)) == NULL)
return LINEERR_OPERATIONFAILED;
// Make sure that nothing is left open
DevlineClose(pLineDev, TRUE);
// Reinit the line device
NullifyLineDevice(pLineDev);
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineClose\r\n")));
return SUCCESS;
}
//
// Close a specified open line device
//
LONG TSPIAPI
TSPI_lineConfigDialogEdit(
DWORD dwDeviceID,
HWND hwndOwner,
LPCWSTR lpszDeviceClass,
LPVOID const lpDeviceConfigIn,
DWORD dwSize,
LPVARSTRING lpDeviceConfigOut
)
{
PTLINEDEV pLineDev;
BYTE cbSize;
DWORD dwRet = SUCCESS;
PDEVCFG pCfg;
DEBUGMSG(ZONE_FUNCTION, (TEXT("+TSPI_lineConfigDialogEdit\r\n")));
// Validate the input/output buffer
//
if (lpDeviceConfigOut == NULL)
return LINEERR_INVALPOINTER;
if (lpDeviceConfigIn == NULL)
return LINEERR_INVALPOINTER;
if (lpDeviceConfigOut->dwTotalSize < sizeof(VARSTRING))
return LINEERR_STRUCTURETOOSMALL;
// Validate the requested device class
//
if (lpszDeviceClass != NULL)
{
if (!ValidateDevCfgClass(lpszDeviceClass))
return LINEERR_INVALDEVICECLASS;
};
// Validate the device ID
//
if ((pLineDev = GetLineDevfromID(dwDeviceID)) == NULL)
return LINEERR_NODEVICE;
// Set the output buffer size
//
cbSize = sizeof( DEVCFG );
lpDeviceConfigOut->dwUsedSize = sizeof(VARSTRING);
lpDeviceConfigOut->dwNeededSize = sizeof(VARSTRING) + cbSize;
if (dwSize < sizeof(DEVCFG)) {
return LINEERR_INVALPARAM;
}
// Validate the output buffer size
//
if (lpDeviceConfigOut->dwTotalSize >= lpDeviceConfigOut->dwNeededSize)
{
// Initialize the buffer
//
lpDeviceConfigOut->dwStringFormat = STRINGFORMAT_BINARY;
lpDeviceConfigOut->dwStringSize = cbSize;
lpDeviceConfigOut->dwStringOffset = sizeof(VARSTRING);
lpDeviceConfigOut->dwUsedSize += cbSize;
pCfg = (PDEVCFG)(lpDeviceConfigOut+1);
memcpy(pCfg, lpDeviceConfigIn, sizeof(DEVCFG));
// Bring up property sheets for modems, and get the updated commconfig
//
LineConfigEdit(hwndOwner, pCfg);
}
else
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("Insufficient space in output buffer (passed %d, needed %d)\r\n"),
lpDeviceConfigOut->dwTotalSize, lpDeviceConfigOut->dwNeededSize));
}
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineConfigDialogEdit x%X (Used %d, Need %d)\r\n"),
dwRet, lpDeviceConfigOut->dwUsedSize, lpDeviceConfigOut->dwNeededSize));
return dwRet;
}
//
// Terminate a call or abandon a call attempt that is in progress
//
LONG TSPIAPI
TSPI_lineDrop(DRV_REQUESTID dwRequestID,
HDRVCALL hdCall,
LPCSTR lpsUserUserInfo,
DWORD dwSize
)
{
PTLINEDEV pLineDev;
DWORD dwRet;
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("+TSPI_lineDrop\r\n")));
if ((pLineDev = GetLineDevfromHandle ((DWORD)hdCall)) == NULL)
return LINEERR_INVALCALLHANDLE;
if (pLineDev->fTakeoverMode)
{
// The TAPI docs state that the app is required to close the handle after
// calling line drop.
pLineDev->fTakeoverMode = FALSE;
pLineDev->DevState = DEVST_DISCONNECTED;
NewCallState(pLineDev, LINECALLSTATE_IDLE, 0L);
TspiGlobals.fnCompletionCallback(dwRequestID, 0L);
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("-TSPI_lineDrop\r\n")));
return dwRequestID;
}
else
{
// Disconnect the line. Grab the CloseCS
// to ensure that the app does not try to drop a line
// while the dialer thread is in the process of closing
// the handle.
EnterCriticalSection(&pLineDev->OpenCS);
dwRet = DevlineDrop(pLineDev);
LeaveCriticalSection(&pLineDev->OpenCS);
if (dwRet == SUCCESS)
{
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("-TSPI_lineDrop, Success\r\n")));
return dwRet;
}
else
{
// Note that Windows CE does not do asynch drops right now.
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("-TSPI_lineDrop, Asynchronous\r\n")));
pLineDev->dwPendingID = dwRequestID;
pLineDev->dwPendingType = PENDING_LINEDROP;
return dwRequestID;
}
}
}
static const WCHAR szProviderName[] = TEXT("SAMPTSPI");
//
// Determine telephony capabilites for the specified device.
//
LONG TSPIAPI
TSPI_lineGetDevCaps(
DWORD dwDeviceID,
DWORD dwTSPIVersion,
DWORD dwExtVersion,
LPLINEDEVCAPS lpLineDevCaps
)
{
PTLINEDEV pLineDev;
int cbLineNameLen = 0;
int cbDevSpecificLen = 0;
int cbProviderNameLen = 0;
int cbAvailMem = 0;
DWORD dwRet = SUCCESS;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineGetDevCaps\r\n")));
// A device caps structure must be filled in that is specific to
// the device.
if ((pLineDev = GetLineDevfromID ((DWORD)dwDeviceID)) == NULL)
return LINEERR_NODEVICE;
// Check to see how much memory is needed.
cbLineNameLen = (_tcslen(pLineDev->szFriendlyName) + 1) * sizeof(TCHAR);
cbProviderNameLen = (wcslen(szProviderName) + 1) * sizeof(WCHAR);
lpLineDevCaps->dwUsedSize = sizeof(LINEDEVCAPS);
cbAvailMem = (int) (lpLineDevCaps->dwTotalSize - lpLineDevCaps->dwUsedSize);
// Enter the size that is ideally needed.
lpLineDevCaps->dwNeededSize = lpLineDevCaps->dwUsedSize +
cbLineNameLen + // room for linename
cbProviderNameLen + // room for provider name
(2*sizeof(WORD)); // and room for DevSpecific info
// On Windows CE, there is no VCOMM available for an app
// to determine the device type (modem or DCC/NULL). So, a DevSpecific
// field is added, which is a WORD indicating the device type.
// DT_NULL, DT_PCMCIA_MODEM, and so on. This is followed by a second word that is
// 1 if the device is ready/available, or 0 if the device is not currently
// available (a removed PC card, for example).
if (cbAvailMem >= sizeof(DWORD) )
{
*(LPWORD)((LPSTR)lpLineDevCaps + lpLineDevCaps->dwUsedSize) = pLineDev->wDeviceType;
*((LPWORD)((LPSTR)lpLineDevCaps + lpLineDevCaps->dwUsedSize) + 1) = pLineDev->wDeviceAvail;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("Storing Device Type x%X, available x%X\r\n"),
pLineDev->wDeviceType,
pLineDev->wDeviceAvail));
lpLineDevCaps->dwDevSpecificSize = sizeof( DWORD );
lpLineDevCaps->dwDevSpecificOffset = lpLineDevCaps->dwUsedSize;
lpLineDevCaps->dwUsedSize += lpLineDevCaps->dwDevSpecificSize;
cbAvailMem -= lpLineDevCaps->dwDevSpecificSize;
}
else
{
lpLineDevCaps->dwDevSpecificSize = 0;
lpLineDevCaps->dwDevSpecificOffset = 0;
lpLineDevCaps->dwNeededSize += sizeof(WORD);
}
// If the provider info fits, also append the name
if (cbAvailMem >= cbLineNameLen)
{
_tcscpy((LPWSTR)((LPSTR)lpLineDevCaps + lpLineDevCaps->dwUsedSize),
pLineDev->szFriendlyName);
lpLineDevCaps->dwLineNameSize = cbLineNameLen;
lpLineDevCaps->dwLineNameOffset = lpLineDevCaps->dwUsedSize;
lpLineDevCaps->dwUsedSize += cbLineNameLen;
cbAvailMem -= cbLineNameLen;
}
else
{
lpLineDevCaps->dwLineNameSize = 0;
lpLineDevCaps->dwLineNameOffset = 0;
}
if (cbAvailMem >= cbProviderNameLen)
{
_tcscpy((LPWSTR)((LPSTR)lpLineDevCaps + lpLineDevCaps->dwUsedSize), szProviderName);
lpLineDevCaps->dwProviderInfoSize = cbProviderNameLen;
lpLineDevCaps->dwProviderInfoOffset = lpLineDevCaps->dwUsedSize;
lpLineDevCaps->dwUsedSize += cbProviderNameLen;
cbAvailMem -= cbProviderNameLen;
}
else
{
lpLineDevCaps->dwProviderInfoSize = 0;
lpLineDevCaps->dwProviderInfoOffset = 0;
}
// You do not have permanent IDs.
lpLineDevCaps->dwPermanentLineID = 0;
lpLineDevCaps->dwStringFormat = STRINGFORMAT_UNICODE;
lpLineDevCaps->dwAddressModes = LINEADDRESSMODE_ADDRESSID;
lpLineDevCaps->dwNumAddresses = 1;
// Bearer mode & information
lpLineDevCaps->dwMaxRate = pLineDev->dwMaxDCERate;
lpLineDevCaps->dwBearerModes = pLineDev->dwBearerModes;
// Media mode
lpLineDevCaps->dwMediaModes = pLineDev->dwMediaModes;
// Wait-for-bong can be simulated if the modem is not capable of
// supporting it.
lpLineDevCaps->dwDevCapFlags = pLineDev->dwDevCapFlags |
LINEDEVCAPFLAGS_DIALBILLING |
LINEDEVCAPFLAGS_CLOSEDROP;
lpLineDevCaps->dwRingModes = 1;
lpLineDevCaps->dwMaxNumActiveCalls = 1;
// Line device state to be notified
lpLineDevCaps->dwLineStates = LINEDEVSTATE_CONNECTED |
LINEDEVSTATE_DISCONNECTED |
LINEDEVSTATE_OPEN |
LINEDEVSTATE_CLOSE |
LINEDEVSTATE_INSERVICE |
LINEDEVSTATE_OUTOFSERVICE |
LINEDEVSTATE_REMOVED |
LINEDEVSTATE_RINGING |
LINEDEVSTATE_REINIT;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetDevCaps x%X\r\n"),
dwRet));
return dwRet;
}
//
// Determine device configuration for the specified device.
//
LONG TSPIAPI
TSPI_lineGetDevConfig(
DWORD dwDeviceID,
LPVARSTRING lpDeviceConfig,
LPCWSTR lpszDeviceClass
)
{
PTLINEDEV pLineDev;
DWORD dwRet = SUCCESS;
BYTE cbSize;
PDEVCFG pDevCfg;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineGetDevConfig\r\n")));
// Validate the requested device class
//
if (lpszDeviceClass != NULL)
{
if (!ValidateDevCfgClass(lpszDeviceClass))
return LINEERR_INVALDEVICECLASS;
}
// Validate the buffer
//
if (lpDeviceConfig == NULL)
return LINEERR_INVALPOINTER;
if (lpDeviceConfig->dwTotalSize < sizeof(VARSTRING))
return LINEERR_STRUCTURETOOSMALL;
// Validate the device ID
//
if ((pLineDev = GetLineDevfromID (dwDeviceID)) == NULL)
return LINEERR_NODEVICE;
// Validate the buffer size
//
cbSize = sizeof(DEVCFG);
lpDeviceConfig->dwUsedSize = sizeof(VARSTRING);
lpDeviceConfig->dwNeededSize = sizeof(VARSTRING) + cbSize;
if (lpDeviceConfig->dwTotalSize >= lpDeviceConfig->dwNeededSize)
{
// The Windows CE Networking and Dial-up Connections application does not store the DEVCFG
// that is associated with a connectoid until the user changes the settings.
// This means that it can end up using settings from a previous connection,
// instead of the defaults that it expects.
pDevCfg = (PDEVCFG)(((LPBYTE)lpDeviceConfig) + sizeof(VARSTRING));
*pDevCfg = pLineDev->DevCfg;
lpDeviceConfig->dwStringFormat = STRINGFORMAT_BINARY;
lpDeviceConfig->dwStringSize = cbSize;
lpDeviceConfig->dwStringOffset = sizeof(VARSTRING);
lpDeviceConfig->dwUsedSize += cbSize;
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT(" TSPI_lineGetDevConfig (DevID %d):\r\n"),
dwDeviceID));
}
else
{
// Not enough room
DEBUGMSG(ZONE_FUNCTION,
(TEXT(" TSPI_lineGetDevConfig needed %d bytes, had %d\r\n"),
lpDeviceConfig->dwNeededSize, lpDeviceConfig->dwTotalSize));
};
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetDevConfig x%X (Used %d, Need %d)\r\n"),
dwRet, lpDeviceConfig->dwUsedSize, lpDeviceConfig->dwNeededSize));
return dwRet;
}
//
// Get the device ID for the specified device. The caller
// can use the returned ID with the corresponding media (for example, for
// serial lines the ID can be passed to ReadFile(), WriteFile(),
// and so on).
//
LONG TSPIAPI
TSPI_lineGetID(
HDRVLINE hdLine,
DWORD dwAddressID,
HDRVCALL hdCall,
DWORD dwSelect,
LPVARSTRING lpDeviceID,
LPCWSTR lpszDeviceClass
)
{
PTLINEDEV pLineDev;
UINT cbPort;
UINT idClass;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineGetID - ProcPerm=0x%X\r\n"),
GetCurrentPermissions()));
switch (dwSelect)
{
case LINECALLSELECT_ADDRESS:
if (dwAddressID != 0)
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetID - INVALADDRESSID\r\n")));
return LINEERR_INVALADDRESSID;
}
// FALLTHROUGH
case LINECALLSELECT_LINE:
if ((pLineDev = GetLineDevfromHandle ((DWORD)hdLine)) == NULL)
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetID - INVALLINEHANDLE\r\n")));
return LINEERR_INVALLINEHANDLE;
}
break;
case LINECALLSELECT_CALL:
if ((pLineDev = GetLineDevfromHandle ((DWORD)hdCall)) == NULL)
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetID - INVALCALLHANDLE\r\n")));
return LINEERR_INVALCALLHANDLE;
}
break;
default:
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetID - Invalid dwSelect x%X\r\n"),
dwSelect));
return LINEERR_OPERATIONFAILED;
}
// Determine the device class
//
for (idClass = 0; idClass < MAX_SUPPORT_CLASS; idClass++)
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("Comparing strings %s to %s\r\n"),
lpszDeviceClass, aGetID[idClass].szClassName));
if (_tcsicmp(lpszDeviceClass, aGetID[idClass].szClassName) == 0)
break;
};
DEBUGMSG(ZONE_FUNCTION,
(TEXT("Class ID = %d (%s)\r\n"), idClass,
aGetID[idClass].szClassName));
// Calculate the required size
//
switch (idClass)
{
case TAPILINE:
cbPort = sizeof(DWORD);
break;
case COMM:
cbPort = (_tcslen(pLineDev->szFriendlyName) + 1) * sizeof(TCHAR);
break;
case COMMMODEM:
cbPort = (_tcslen(pLineDev->szFriendlyName) + 1) * sizeof(TCHAR) + sizeof(DWORD);
break;
default:
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetID - Invalid ID Class x%X\r\n"),
idClass ));
return LINEERR_OPERATIONFAILED;
};
// Calculate the required size
//
lpDeviceID->dwNeededSize = sizeof(VARSTRING) + cbPort;
lpDeviceID->dwStringFormat = aGetID[idClass].dwFormat;
ASSERT(lpDeviceID->dwUsedSize == sizeof(VARSTRING));
if ((lpDeviceID->dwTotalSize - lpDeviceID->dwUsedSize) <
cbPort)
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetID - Inusufficient space\r\n")));
return SUCCESS;
}
// Enough space exists to return valid information
//
lpDeviceID->dwStringSize = cbPort;
lpDeviceID->dwStringOffset = sizeof(VARSTRING);
lpDeviceID->dwUsedSize += cbPort;
// Return the valid information
switch (idClass)
{
case TAPILINE:
{
LPDWORD lpdwDeviceID;
lpdwDeviceID = (LPDWORD)(((LPBYTE)lpDeviceID) + sizeof(VARSTRING));
*lpdwDeviceID = (DWORD) pLineDev->dwDeviceID;
DEBUGMSG(ZONE_MISC,
(TEXT("-TSPI_lineGetID TAPILINE - Device ID x%X\r\n"),
pLineDev->dwDeviceID));
break;
}
case COMM:
{
_tcsncpy( (LPWSTR)((LPBYTE)lpDeviceID + sizeof(VARSTRING)),
pLineDev->szFriendlyName, (cbPort/sizeof(TCHAR)) );
DEBUGMSG(ZONE_MISC,
(TEXT("-TSPI_lineGetID COMM - Device name \"%s\" (len %d)\r\n"),
pLineDev->szFriendlyName, (cbPort/sizeof(TCHAR))));
break;
}
case COMMMODEM:
{
LPDWORD lpdwDeviceHandle;
lpdwDeviceHandle = (LPDWORD)(((LPBYTE)lpDeviceID) + sizeof(VARSTRING));
if (pLineDev->hDevice != (HANDLE)INVALID_DEVICE)
{
*lpdwDeviceHandle = (DWORD)pLineDev->hDevice;
SetHandleOwner((HANDLE)*lpdwDeviceHandle, GetCallerProcess());
}
else
{
*lpdwDeviceHandle = (DWORD)NULL;
};
_tcscpy((LPWSTR)(lpdwDeviceHandle+1), pLineDev->szFriendlyName );
DEBUGMSG(ZONE_MISC,
(TEXT("-TSPI_lineGetID COMMMODEM - Device Handle x%X, Device name %s\r\n"),
*lpdwDeviceHandle, pLineDev->szFriendlyName));
break;
}
};
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineGetID\r\n")));
return SUCCESS;
}
LONG TSPIAPI
TSPI_lineMakeCall(
DRV_REQUESTID dwRequestID,
HDRVLINE hdLine,
HTAPICALL htCall,
LPHDRVCALL lphdCall,
LPCWSTR lpszDestAddress,
DWORD dwCountryCode,
LPLINECALLPARAMS const lpCallParams
)
{
PTLINEDEV pLineDev;
DWORD dwRet;
BOOL fDoTakeover = FALSE;
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS, (TEXT("+TSPI_lineMakeCall\r\n")));
if (lpszDestAddress) {
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("+TSPI_lineMakeCall(%s)\r\n"), lpszDestAddress));
}
if ((pLineDev = GetLineDevfromHandle ((DWORD)hdLine)) == NULL)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR|ZONE_CALLS,
(TEXT("-TSPI_lineMakeCall ** Invalid Handle\r\n")));
return LINEERR_INVALLINEHANDLE;
}
// See if there is a free call structure.
if (pLineDev->dwCall & CALL_ALLOCATED)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR|ZONE_CALLS,
(TEXT("-TSPI_lineMakeCall ** Call already allocated\r\n")));
return LINEERR_CALLUNAVAIL;
}
// By default, do not perform blind dialing.
pLineDev->dwDialOptions &= ~MDM_BLIND_DIAL;
// Examine LINECALLPARAMS, if it is present.
if (lpCallParams)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR|ZONE_CALLS,
(TEXT(" lineMakeCall - Check CallParams\r\n")));
if (lpCallParams->dwBearerMode & LINEBEARERMODE_PASSTHROUGH)
{
fDoTakeover = TRUE;
}
else
{
// This check is to prevent G3FAX from being used without pass-through...
// (You can dial only with DATAMODEM)
if ((lpCallParams->dwMediaMode &
(LINEMEDIAMODE_DATAMODEM)) == 0)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR|ZONE_CALLS,
(TEXT("-TSPI_lineMakeCall ** Invalid Media Mode\r\n")));
return LINEERR_INVALMEDIAMODE;
}
}
pLineDev->dwCurBearerModes = lpCallParams->dwBearerMode;
pLineDev->dwCurMediaModes = lpCallParams->dwMediaMode;
DEBUGMSG(ZONE_MISC,
(TEXT(" lineMakeCall - got media & bearer modes\r\n")));
if (!(lpCallParams->dwCallParamFlags & LINECALLPARAMFLAGS_IDLE))
{
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("LINECALLPARAMFLAGS_IDLE: not set, no dialtone detect\r\n") ));
// Turn on blind dialing
pLineDev->dwDialOptions |= MDM_BLIND_DIAL;
}
}
else
{
// set the standard defaults - for peg tapi, it is DATAMODEM
ASSERT(pLineDev->dwMediaModes & LINEMEDIAMODE_DATAMODEM);
pLineDev->dwCurMediaModes = LINEMEDIAMODE_DATAMODEM;
pLineDev->dwCurBearerModes = pLineDev->dwBearerModes & ~LINEBEARERMODE_PASSTHROUGH;
}
// Is there a telephone number?
//
if (!fDoTakeover)
{
// Validate lpszDestAddress, and get the processed form of it.
DEBUGMSG(ZONE_MISC|ZONE_CALLS,
(TEXT("TSPI_lineMakeCall - validating destination address\r\n")));
dwRet = ValidateAddress(pLineDev, lpszDestAddress, pLineDev->szAddress);
if (SUCCESS != dwRet)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR|ZONE_CALLS,
(TEXT("-TSPI_lineMakeCall ** Invalid Address\r\n")));
return dwRet;
}
// if the lpszDestAddress was NULL or "", you just want to perform a
// dial-tone detection. It is expected that lineDial will be called.
// Setting the szAddress to ";" will accomplish this.
if ('\0' == pLineDev->szAddress[0])
{
_tcscpy(pLineDev->szAddress, szSemicolon);
}
}
// Record the call attributes
pLineDev->htCall = htCall;
pLineDev->dwCall = CALL_ALLOCATED;
pLineDev->dwCallState = LINECALLSTATE_UNKNOWN;
*lphdCall = (HDRVCALL)pLineDev;
// It is allowed to make a call to an already opened line if the line is monitoring
// a call. Therefore, if the line is in use, try making a call. The make-call
// routine will return error if the state is not appropriate.
//
if (((dwRet = DevlineOpen(pLineDev)) == SUCCESS) ||
(dwRet == LINEERR_ALLOCATED))
{
if (fDoTakeover)
{
DEBUGMSG(ZONE_MISC|ZONE_CALLS,
(TEXT(" lineMakeCall - Takeover\r\n")));
// For takeover, no calling is actually done. Just open the
// port, so that the application can get the device handle from
// lineGetID.
if (pLineDev->DevState == DEVST_DISCONNECTED)
{
// You can go into pass-through only if device is not in use
// OK, the device was opened above, so now you just need to
// let the apps know that the call state has changed.
pLineDev->DevState = DEVST_CONNECTED;
TspiGlobals.fnCompletionCallback(dwRequestID, 0L);
NewCallState(pLineDev, LINECALLSTATE_CONNECTED, 0L);
pLineDev->fTakeoverMode = TRUE;
dwRet = dwRequestID;
}
else
{
dwRet = LINEERR_OPERATIONFAILED;
}
}
else
{
// The call can be made here
pLineDev->dwPendingID = dwRequestID;
pLineDev->dwPendingType = PENDING_LINEMAKECALL;
DEBUGMSG(ZONE_CALLS,
(TEXT("\tTSPI_lineMakeCall, Ready to make call for ReqID x%X\r\n"),
pLineDev->dwPendingID));
if (((dwRet = DevlineMakeCall(pLineDev)) != SUCCESS) &&
(IS_TAPI_ERROR(dwRet)))
{
DEBUGMSG(ZONE_CALLS | ZONE_ERROR,
(TEXT("\tDevLineMakeCall error - dwRet x%X. Closing port\r\n"),
dwRet));
DevlineClose(pLineDev, TRUE);
}
}
};
// Check if an error occurs
//
if (IS_TAPI_ERROR(dwRet))
{
DEBUGMSG(ZONE_CALLS,
(TEXT("\tTSPI_lineMakeCall, Ret Code x%X, invalidating pending ID.\r\n"),
dwRet ));
pLineDev->dwPendingID = INVALID_PENDINGID;
pLineDev->dwPendingType = INVALID_PENDINGOP;
// Deallocate the call from this line
pLineDev->htCall = NULL;
pLineDev->dwCall = 0;
*lphdCall = NULL;
};
DEBUGMSG(ZONE_FUNCTION|ZONE_CALLS,
(TEXT("-TSPI_lineMakeCall, dwRet x%X\r\n"),
dwRet));
return dwRet;
}
LONG TSPIAPI
TSPI_lineNegotiateTSPIVersion(
DWORD dwDeviceID,
DWORD dwLowVersion,
DWORD dwHighVersion,
LPDWORD lpdwTSPIVersion
)
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineNegotiateTSPIVersion\r\n")));
// Check the range of the device ID
//
if((dwDeviceID == INITIALIZE_NEGOTIATION)
|| (GetLineDevfromID(dwDeviceID) != NULL)
)
{
// Check the version range
//
if((dwLowVersion > SPI_VERSION) || (dwHighVersion < SPI_VERSION))
{
*lpdwTSPIVersion = 0;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineNegotiateTSPIVersion - SPI Ver x%X out of TAPI range x%X..x%X\r\n"),
SPI_VERSION, dwLowVersion, dwHighVersion));
return LINEERR_INCOMPATIBLEAPIVERSION;
}
else
{
*lpdwTSPIVersion = SPI_VERSION;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineNegotiateTSPIVersion - Ver x%X\r\n"),
SPI_VERSION));
return SUCCESS;
};
};
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineNegotiateTSPIVersion - No Device\r\n")));
// The requested device does not exist.
return LINEERR_NODEVICE;
}
LONG TSPIAPI
TSPI_lineOpen(
DWORD dwDeviceID,
HTAPILINE htLine,
LPHDRVLINE lphdLine,
DWORD dwTSPIVersion,
LINEEVENT lineEventProc)
{
PTLINEDEV pLineDev;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineOpen DevID x%X, HTapiLine x%X, SPI Ver x%X\r\n"),
dwDeviceID, htLine, dwTSPIVersion));
// Validate the device ID
if ((pLineDev = GetLineDevfromID(dwDeviceID)) == NULL)
{
DEBUGMSG(ZONE_ERROR,
(TEXT("TSPI_lineOpen, could not find device for dwId x%X\r\n"),
dwDeviceID));
return LINEERR_NODEVICE;
}
// Update the line device
*lphdLine = (HDRVLINE)pLineDev;
pLineDev->lpfnEvent = lineEventProc;
pLineDev->htLine = htLine;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineOpen\r\n")));
return SUCCESS;
}
LONG TSPIAPI
TSPI_lineSetDevConfig(
DWORD dwDeviceID,
LPVOID const lpDeviceConfig,
DWORD dwSize,
LPCWSTR lpszDeviceClass
)
{
PTLINEDEV pLineDev;
PDEVCFG pDevCfg;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineSetDevConfig\r\n")));
// Validate the requested device class
//
if (!ValidateDevCfgClass(lpszDeviceClass))
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR,
(TEXT("-TSPI_lineSetDevConfig : LINEERR_INVALDEVICECLASS\r\n")));
return LINEERR_INVALDEVICECLASS;
}
// Validate the buffer
//
if (lpDeviceConfig == NULL)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR,
(TEXT("-TSPI_lineSetDevConfig : LINEERR_INVALPOINTER\r\n")));
return LINEERR_INVALPOINTER;
}
// Validate the device ID
//
if ((pLineDev = GetLineDevfromID(dwDeviceID)) == NULL)
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR,
(TEXT("-TSPI_lineSetDevConfig : LINEERR_NODEVICE\r\n")));
return LINEERR_NODEVICE;
}
// Verify the structure size
//
pDevCfg = (PDEVCFG)lpDeviceConfig;
if (dwSize != sizeof(DEVCFG))
{
DEBUGMSG(ZONE_FUNCTION|ZONE_ERROR,
(TEXT("-TSPI_lineSetDevConfig : LINEERR_INVALPARAM\r\n")));
return LINEERR_INVALPARAM;
}
// Get the new settings
pLineDev->DevCfg = *pDevCfg;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineSetDevConfig\r\n")));
return SUCCESS;
}
LONG TSPIAPI
TSPI_lineSetStatusMessages(
HDRVLINE hdLine,
DWORD dwLineStates,
DWORD dwAddressStates
)
{
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_lineSetStatusMessages\r\n")));
// Record these settings and filter the notifications, based
// on these settings.
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_lineSetStatusMessages\r\n")));
return SUCCESS;
}
//
// LONG TSPIAPI TSPI_providerCreateLineDevice()
//
// Dynamically creates a new device. This entry point will be
// called by devloader whenever it adds a new device that lists
// Samptspi.dll as the service provider.
//
LONG TSPIAPI
TSPI_providerCreateLineDevice(
HKEY hActiveKey, // Registry key for this active device
LPCWSTR lpszDevPath, // Registry path for this device
LPCWSTR lpszDeviceName // Device name
)
{
PTLINEDEV ptLineDev;
DWORD dwRet;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_providerCreateLineDevice %s.\r\n"),
lpszDeviceName));
dwRet = (DWORD) -1; // Assume failure
// It is possible that this device already exists (for example,
// if it is a PC card that was removed and reinserted.)
// So, scan the current device list to look for it, and use
// the existing entry if it is found. Otherwise, create a device.
ptLineDev = createLineDev( hActiveKey, lpszDevPath, lpszDeviceName );
if( NULL != ptLineDev )
{
dwRet = ptLineDev->dwDeviceID;
}
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_providerCreateLineDevice, return x%X.\r\n"),
dwRet));
return dwRet;
}
//
// LONG TSPIAPI TSPI_providerDeleteLineDevice()
//
// Removes a device from the system.
//
// NOTE: Devload actually does not know which devices were added,
// so it is possible that this device is not one for which
// a lineDevice entry was created. Note that a device is never removed.
// It is only marked as disconnected. It might get
// reintroduced to the system later.
//
LONG TSPIAPI TSPI_providerDeleteLineDevice(
DWORD Identifier // dwDeviceID associated with this device
)
{
DWORD dwRet;
PTLINEDEV ptLineDev;
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("+TSPI_providerDeleteLineDevice, x%X.\r\n"),
Identifier));
// Get the name of the device, and check its validity
if ( (DWORD)-1 == Identifier )
return LINEERR_BADDEVICEID;
dwRet = LINEERR_OPERATIONFAILED; // Assume failure
// See if such a device is actually in the system
if( ptLineDev = GetLineDevfromID(Identifier) )
{
// OK, send a DEVSTATE_DISCONNECTED to TAPI.
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("\tSend disconnect for ptLine x%X, htLine x%X\r\n"),
ptLineDev, ptLineDev->htLine));
TspiGlobals.fnLineEventProc( ptLineDev->htLine,
0,
LINE_LINEDEVSTATE,
LINEDEVSTATE_DISCONNECTED,
0,
0 );
// And this Settings key is no longer valid.
RegCloseKey( ptLineDev->hSettingsKey );
// And mark this as disconnected, so that GetDevCaps can return this info.
ptLineDev->wDeviceAvail = 0;
}
else
{
// Could not find the specified device
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("-TSPI_providerDeleteLineDevice, invalid device id x%X.\r\n"),
Identifier));
}
DEBUGMSG(ZONE_FUNCTION|ZONE_INIT,
(TEXT("-TSPI_providerDeleteLineDevice.\r\n")));
return dwRet;
}
BOOL
DllEntry (HANDLE hinstDLL,
DWORD Op,
LPVOID lpvReserved)
{
switch (Op) {
case DLL_PROCESS_ATTACH :
DEBUGREGISTER(hinstDLL);
DEBUGMSG (ZONE_FUNCTION, (TEXT("TSPI:DllEntry(ProcessAttach)\r\n")));
// TSPIGlobals, and so forth.
TSPIDLL_Load( );
TspiGlobals.hInstance = hinstDLL; // Instance handle needed to
// load dialog resources
break;
case DLL_PROCESS_DETACH :
case DLL_THREAD_DETACH :
case DLL_THREAD_ATTACH :
default :
break;
}
return TRUE;
}
// **********************************************************************
// Now a vtbl must be provided that can be used to access the functions.
// **********************************************************************
TSPI_PROCS tspi_procs;
LONG TSPIAPI TSPI_lineGetProcTable(
LPTSPI_PROCS *lplpTspiProcs
)
{
PDWORD pdw;
LONG i;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("+TSPI_providerGetVtbl, ptr = x%X\r\n"),
&tspi_procs));
if (tspi_procs.TSPI_lineClose != TSPI_lineClose) {
for (pdw = (PDWORD)&tspi_procs, i = 0;
i < sizeof(TSPI_PROCS)/sizeof(DWORD);
pdw++,i++) {
*pdw = (DWORD)TSPI_Unsupported;
}
tspi_procs.TSPI_lineClose = TSPI_lineClose;
tspi_procs.TSPI_lineDrop = TSPI_lineDrop;
tspi_procs.TSPI_lineGetDevCaps = TSPI_lineGetDevCaps;
tspi_procs.TSPI_lineGetDevConfig = TSPI_lineGetDevConfig;
tspi_procs.TSPI_lineGetID = TSPI_lineGetID;
tspi_procs.TSPI_lineMakeCall = TSPI_lineMakeCall;
tspi_procs.TSPI_lineNegotiateTSPIVersion = TSPI_lineNegotiateTSPIVersion;
tspi_procs.TSPI_lineOpen = TSPI_lineOpen;
tspi_procs.TSPI_lineSetDevConfig = TSPI_lineSetDevConfig;
tspi_procs.TSPI_lineSetStatusMessages = TSPI_lineSetStatusMessages;
tspi_procs.TSPI_providerInit = TSPI_providerInit;
tspi_procs.TSPI_providerInstall = TSPI_providerInstall;
tspi_procs.TSPI_providerShutdown = TSPI_providerShutdown;
tspi_procs.TSPI_providerEnumDevices = TSPI_providerEnumDevices;
tspi_procs.TSPI_providerCreateLineDevice = TSPI_providerCreateLineDevice;
tspi_procs.TSPI_providerDeleteLineDevice = TSPI_providerDeleteLineDevice;
tspi_procs.TSPI_lineConfigDialogEdit = TSPI_lineConfigDialogEdit;
}
*lplpTspiProcs = &tspi_procs;
DEBUGMSG(ZONE_FUNCTION,
(TEXT("-TSPI_providerGetVtbl, ptr = x%X\r\n"),
&tspi_procs));
return SUCCESS;
}
See Also
Sample TSP | Sample TSP Header File
Send Feedback on this topic to the authors