Printer Friendly Version      Send     
Click to Rate and Give Feedback
MSDN
MSDN Library
Windows Embedded
Windows Embedded CE
Windows CE 3.0
Telephony API
 Sample TSP
Microsoft Windows CE 3.0
Sample TSP

The following code example illustrates how to write a customized TSP by using the TSPI that is supported in Windows CE 3.0.

Sample TSP Header File

*++
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.h

Abstract:
Private constants, structures, and function prototypes for the 
Windows CE sample TAPI Service Provider

Notes:


--*/

#include <Termctrl.h>
#include <Mcx.h>
#include <Tspi.h>

#define SAMPTSPI_VERSION 0x0003
#define SAMPTSPI_SUBTYPE 0xFEDCBA98

#define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION 

#ifdef DEBUG
#define ZONE_INIT      DEBUGZONE(0)
#define ZONE_CALLS      DEBUGZONE(10)
#define ZONE_MISC      DEBUGZONE(11)
#define ZONE_ALLOC      DEBUGZONE(12)
#define ZONE_FUNCTION   DEBUGZONE(13)
#define ZONE_FUNC       ZONE_FUNCTION
#define ZONE_WARN      DEBUGZONE(14)
#define ZONE_ERRORDEBUGZONE(15)
#endif

#define  WM_MDMMESSAGE      WM_USER+0x0100
#define  WM_MDMCHANGE       WM_USER+0x0101
#define  WM_MDMCANCEL       WM_USER+0x0102

#define  MDM_SUCCESS        0
#define  MDM_PENDING        1
#define  MDM_FAILURE        2
#define  MDM_HANGUP         3
#define  MDM_BUSY           4
#define  MDM_NOANSWER       5
#define  MDM_NOCARRIER      6
#define  MDM_NODIALTONE     7

#define SPI_VERSION     TAPI_CURRENT_VERSION
#define  VALIDATE_VERSION(version)  \
    {if (version != SPI_VERSION) \
        { \
        DEBUGMSG(1|ZONE_ERROR, (TEXT("Invalid SPI Version x%X\r\n"), version)); \
        return LINEERR_OPERATIONFAILED; \
        } \
    }


#define SUCCESS                             0x0

#define  MDM_ID_NULL        0xffff  // Async ID for an unexpected message

#define  SZTCHAR (sizeof(TCHAR))

#define MAXDEVICENAME       128
#define  MAXADDRESSLEN      TAPIMAXDESTADDRESSSIZE
#define MAX_CLASS_NAME_LEN  128

#define  INVALID_DEVICE     0xFFFFFFFF
#define  INVALID_PENDINGID  0xFFFFFFFF

// Check for an error code
//
#define  IS_TAPI_ERROR(err)         (BOOL)(HIWORD(err) & 0x8000)


// Device class and information
//
#define TAPILINE            0
#define COMM                1
#define COMMMODEM           2
#define NDIS                3
#define MAX_SUPPORT_CLASS   4

typedef struct  _GETIDINFO {
    LPTSTR       szClassName;
    DWORD       dwFormat;
}   GETIDINFO;

extern const GETIDINFO   aGetID[MAX_SUPPORT_CLASS];

// Pending operation type
//
#define INVALID_PENDINGOP       0
#define PENDING_LINEMAKECALL    1
#define PENDING_LINEANSWER      2
#define PENDING_LINEDROP        3
#define PENDING_LINEDIAL        4

// Flags for setting pass-through mode
#define PASSTHROUGH_ON                 1
#define PASSTHROUGH_OFF                2
#define PASSTHROUGH_OFF_BUT_CONNECTED  3

// Flags for resources
//
#define LINEDEVFLAGS_OUTOFSERVICE   0x00000001
#define LINEDEVFLAGS_REMOVING       0x00000002

// Enumerated states of the line device
typedef enum DevStates  {
    DEVST_DISCONNECTED      = 0,
    DEVST_PORTSTARTPRETERMINAL,
    DEVST_PORTPRETERMINAL,
    DEVST_PORTCONNECTINIT,
    DEVST_PORTCONNECTWAITFORLINEDIAL,  // this is a resting state.  i.e., we sit here waiting for a lineDial.
    DEVST_PORTCONNECTDIALTONEDETECT,
    DEVST_PORTCONNECTDIAL,
    DEVST_PORTCONNECTING,
    DEVST_PORTPOSTTERMINAL,
    DEVST_CONNECTED
}   DEVSTATES;

typedef enum _MDMSTATE
{
    MDMST_UNKNOWN       = 1,
    MDMST_INITIALIZING,       
    MDMST_DISCONNECTED,       
    MDMST_DIALING,                    
    MDMST_CONNECTED,                  
    MDMST_DIALED,             
    MDMST_ORIGINATING,        
    MDMST_HANGING_UP_REMOTE,  // This is when the remote side hangs up.
                              // modem: Wait for response and then:
                              //        - send MODEM_HANGUP
                              //        - set MDMSTATE to MDMSTATE_DISCONNECTED
    MDMST_HANGING_UP_DTR,     // After dropping DTR and waiting for 1200ms, check RLSD:
                              //   If RLSD is low, raise DTR and set state to
                              //     modem: MDMSTATE_HANGING_UP_NON_CMD
                              //     null-modem: MDMSTATE_DISCONNECTED
                              //   Else set state to:
                              //     modem: MDMSTATE_HANGING_UP_NON_COMMAND and send "+++"
                              //     null-modem: same, wait another 200ms (keeping count, stop at 3 or so)
    MDMST_HANGING_UP_NON_CMD, // After sending a \r to hangup or sending +++ or getting RLSD low:
                              // Wait for any response or time-out and then:
                              // - send ATH<cr>
                              // - set state to MDMSTATE_HANGING_UP_CMD
    MDMST_HANGING_UP_CMD,     // Wait for a response to ATH<cr>
                              // If you get one, you are hung up, raise DTR, set state to
                              //   MDMSTATE_DISCONNECTED, and return MODEM_SUCCESS.
                              // Else if you do not get one, consider dropping DTR, waiting 200ms more
                              //   and setting state to MDMSTATE_HANGING_UP_DTR. (keep track of
                              //   how many times you do this, max out at 3 or so.)
} MDMSTATE;


// Flags for the call attributes
//
#define CALL_ALLOCATED   0x00000001
#define CALL_ACTIVE      0x00000002
#define CALL_INBOUND     0x00000004
#define CALL_DROPPING    0x00000008

#define CALL_NOT_DROPPED( dwCall ) ( (dwCall & CALL_ALLOCATED) && !(dwCall & CALL_DROPPING) )

#define AnsiNext(x)         ((x)+1)
#define AnsiPrev(y,x)       ((x)-1)

// Flags for the fwOptions field of DEVCFGHDR
//
#define TERMINAL_NONE       0x0000
#define TERMINAL_PRE        0x0001
#define TERMINAL_POST       0x0002
#define MANUAL_DIAL         0x0004
#define LAUNCH_LIGHTS       0x0008

#define  MIN_WAIT_BONG      0
#define  MAX_WAIT_BONG      60
#define  DEF_WAIT_BONG      8
#define  INC_WAIT_BONG      2

// Device setting information
//
typedef struct  tagDEVCFG  {
    WORD        wWaitBong;
    DWORD       dwModemOptions;
    DWORD       dwCallSetupFailTimer;
    COMMCONFIG  commconfig;
}   DEVCFG, *PDEVCFG, FAR* LPDEVCFG;


// Line device data structure
//
typedef struct __LineDev   {
    LIST_ENTRY  llist;        // pointer to next LineDev
    DWORD       dwVersion;             // Version stamp
    DWORD       dwDeviceID;            // Local device ID
    TCHAR       szDeviceName[MAXDEVICENAME+1]; // Actual device name
    TCHAR       szFriendlyName[MAXDEVICENAME+1]; // Friendly device name

    HKEY        hSettingsKey;          // Registry handle for settings key
    
    WORD        wDeviceType;           // The modem type
    WORD        wDeviceAvail;          // Is the modem currently available?

    DWORD       dwDefaultMediaModes;   // Default supported media modes
    DWORD       dwBearerModes;         // Supported bearer modes
    DWORD       dwCurBearerModes;      // The current media bearer modes. Plural because
                                        // we keep track of PASSTHROUGH _and_ the real b-mode
                                        // at the same time.
    DWORD       dwMediaModes;          // Current supported media modes
    DWORD       dwCurMediaModes;       // The current media modes
    DWORD       dwDetMediaModes;       // The current detection media modes

    HANDLE      hDevice;               // Device handle
    DWORD       pidDevice;             // Device owner pid
    HTAPILINE   htLine;                // TAPI line handle
    LINEEVENT   lpfnEvent;             // Line event callback function
    HWND        hwndLine;              // TAPI emulation
    TCHAR       szAddress[MAXADDRESSLEN+1];
    DWORD       dwPendingID;           // Async pending ID
    DWORD       dwPendingType;         // Pending operation
    DWORD       dwCall;                // Call attributes
    HTAPICALL   htCall;                // TAPI call handle

    HWND        hwTermCtrl;            // TermCtrl Window Handle
    
    CRITICAL_SECTION OpenCS;            // Critical Section for DevLineClose

    HANDLE      hTimeoutEvent;         // Event handle for call time-out event
    DWORD       dwTimeout;             // Time-out parameter passed to watchdog task
    HANDLE      hCallComplete;         // Event handle for call completions
    DWORD       dwCallState;           // Current call state

    DEVSTATES   DevState;              // Intermediate TAPI device state
    
    MDMSTATE    MdmState;              // What state is the modem in?
    
    DWORD       dwDialOptions;         // Options set in a lineMakeCall

    BOOL        fTakeoverMode;         // True if samptspi is in takeover mode
    TCHAR       szDriverKey[MAX_CLASS_NAME_LEN+10];  // ex. "Modem\0000"

    DWORD       dwDevCapFlags;         // LINEDEVCAPSFLAGS (ie. DIALBILLING, DIALQUIET, DIALDIALTONE)
    DWORD       dwMaxDCERate;          // Max DCE as stored in the Properties line of the registry
    DEVCFG      DevCfg;
}   TLINEDEV, *PTLINEDEV;

// Default mask to MDM_ options
//
#define MDM_MASK (MDM_TONE_DIAL | MDM_BLIND_DIAL)

typedef struct _TSPIGLOBALS {
    HINSTANCE          hInstance;
    DWORD              dwProviderID;
    HPROVIDER          hProvider;
    HKEY               hDefaultsKey;
    LINEEVENT          fnLineEventProc;      // Line event callback in TAPI
    ASYNC_COMPLETION   fnCompletionCallback; // Completion create callback in TAPI
    
    LIST_ENTRY         LineDevs;             // Linked List of TLINEDEV
    CRITICAL_SECTION   LineDevsCS;           // Critical section for above list
} TSPIGLOBALS, *PTSPIGLOBALS;
extern TSPIGLOBALS TspiGlobals;

// DeviceType defines
//
#define DT_NULL_MODEM       0
#define DT_EXTERNAL_MODEM   1
#define DT_INTERNAL_MODEM   2
#define DT_PCMCIA_MODEM     3
#define DT_PARALLEL_PORT    4
#define DT_PARALLEL_MODEM   5
#define DT_IRCOMM_MODEM     6


// The following info is returned from the GetDevCaps call as the
// device-specific data. The wDeviceType field indicates the type of device on
// the port, and the wActive field indicates if the port is currently active.
typedef struct  _EXT_INFO {
    WORD        wDeviceType;
    WORD        wActive;
}   EXT_INFO;


// Some quick macros until we actually implement MemTracking
#define TSPIAlloc( Size )  LocalAlloc( LPTR, Size )
#define TSPIFree( Ptr )    LocalFree( Ptr )

// Helper routines defined in Sampmisc.c
void TSPIDLL_Load(void);
PTLINEDEV createLineDev(HKEY hActiveKey, LPCTSTR lpszDevPath, LPCTSTR lpszDeviceName);
PTLINEDEV GetLineDevfromID(DWORD dwDeviceID);
PTLINEDEV GetLineDevfromName(LPCTSTR lpszDeviceName,LPCTSTR lpszFriendlyName);
PTLINEDEV GetLineDevfromHandle (DWORD handle);
PTLINEDEV LineExists( PTLINEDEV ptNewLine);
BOOL ValidateDevCfgClass (LPCTSTR lpszDeviceClass);
LONG DevlineClose (PTLINEDEV pLineDev, BOOL fDoDrop);
DWORD NullifyLineDevice (PTLINEDEV pLineDev);
LONG DevlineDrop(PTLINEDEV pLineDev);
LONG DevlineOpen(PTLINEDEV pLineDev);
LONG ValidateAddress( PTLINEDEV pLineDev, LPCTSTR lpszInAddress, LPTSTR lpszOutAddress);
LONG DevlineMakeCall( PTLINEDEV pLineDev );
void NewCallState(PTLINEDEV pLineDev, DWORD dwNewState, DWORD dwParm2);

// Routines from Sampdial.c
void DialerThread(PTLINEDEV pLineDev);

Sample TSP Source File

/*++
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 we choose not to support.
//
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 we do not leave anything 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")));

     // We must fill in a device caps structure that is specific to
     // the device.
    if ((pLineDev = GetLineDevfromID ((DWORD)dwDeviceID)) == NULL)
        return LINEERR_NODEVICE;

    // Check to see how much memory we will need.
    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 we ideally need.
    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;
    }

     // We 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;

    // We can simulate wait-for-bong 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 remote networking 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 (i.e., 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;
    }
    
     // We have enough space 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 we have 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...
             // (We 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;
    }

     // Do we have 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 "", we just want to perform a
         // dial-tone detection. We expect 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;

     // We allow to make 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, we actually do not do any calling.  Just open the
            // port, so that the application can get the device handle from
            // lineGetID.
            if (pLineDev->DevState == DEVST_DISCONNECTED)
            {
                // We can go into pass-through only if device is not in use

                // OK, the device was opened above, so now we 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
        {
             // We can make a call 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 we added,
// so it is possible that this device is not one for which we
// have created a lineDevice entry. Note that we never remove a
// device really. We only mark it 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 we actually have such a device in our 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 we must provide a vtbl that can be used to access our 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;
}  

 Last updated on Friday, April 02, 2004

© 1992-2000 Microsoft Corporation. All rights reserved.

© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Page view tracker