// WaveTsp.cpp

#include "pch.h"
#include "wavetsp.h"
#include "tspline.h"
#include "tspcall.h"
#include "resource.h"

HINSTANCE           g_hinst = 0;
HPROVIDER           g_hProvider = 0;
DWORD               g_dwLineDeviceIDBase = 0;
DWORD               g_dwPermanentProviderID = 0;
ASYNC_COMPLETION    g_pfnCompletionProc = 0;

// { dwDialPause, dwDialSpeed, dwDigitDuration, dwWaitForDialtone }
LINEDIALPARAMS      g_dpMin = { 100,  50, 100,  100 };
LINEDIALPARAMS      g_dpDef = { 250,  50, 250,  500 };
LINEDIALPARAMS      g_dpMax = { 1000, 50, 1000, 1000 };

BOOL WINAPI DllMain(
    HINSTANCE   hinst,
    DWORD       dwReason,
    void*      /*pReserved*/)
{
    if( dwReason == DLL_PROCESS_ATTACH )
    {
        g_hinst = hinst;
    }
    
    return TRUE;
}

// TSPI_providerXxx functions /////////////////////////////////////////

LONG TSPIAPI TSPI_providerEnumDevices(
    DWORD       dwPermanentProviderID,
    LPDWORD     pdwNumLines,
    LPDWORD     pdwNumPhones,
    HPROVIDER   hProvider,
    LINEEVENT   pfnLineCreateProc,
    PHONEEVENT  pfnPhoneCreateProc)
{
    BEGIN_PARAM_TABLE("TSPI_providerEnumDevices")
        DWORD_IN_ENTRY(dwPermanentProviderID)
        DWORD_OUT_ENTRY(pdwNumLines)
        DWORD_OUT_ENTRY(pdwNumPhones)
        DWORD_IN_ENTRY(hProvider)
        DWORD_IN_ENTRY(pfnLineCreateProc)
        DWORD_IN_ENTRY(pfnPhoneCreateProc)
    END_PARAM_TABLE()

    g_hProvider = hProvider;
    *pdwNumLines = 1;
    *pdwNumPhones = 0;

    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerInit(
    DWORD               dwTSPIVersion,
    DWORD               dwPermanentProviderID,
    DWORD               dwLineDeviceIDBase,
    DWORD               dwPhoneDeviceIDBase,
    DWORD               dwNumLines,
    DWORD               dwNumPhones,
    ASYNC_COMPLETION    pfnCompletionProc,
    LPDWORD             pdwTSPIOptions)
{
    BEGIN_PARAM_TABLE("TSPI_providerInit")
        DWORD_IN_ENTRY(dwTSPIVersion)
        DWORD_IN_ENTRY(dwPermanentProviderID)
        DWORD_IN_ENTRY(dwLineDeviceIDBase)
        DWORD_IN_ENTRY(dwPhoneDeviceIDBase)
        DWORD_IN_ENTRY(dwNumLines)
        DWORD_IN_ENTRY(dwNumPhones)
        DWORD_IN_ENTRY(pfnCompletionProc)
        DWORD_OUT_ENTRY(pdwTSPIOptions)
    END_PARAM_TABLE()

    g_dwPermanentProviderID = dwPermanentProviderID;
    g_dwLineDeviceIDBase = dwLineDeviceIDBase;
    g_pfnCompletionProc  = pfnCompletionProc;
    *pdwTSPIOptions = LINETSPIOPTION_NONREENTRANT;

    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerShutdown(
    DWORD   dwTSPIVersion,
    DWORD   dwPermanentProviderID)
{
    BEGIN_PARAM_TABLE("TSPI_providerShutdown")
        DWORD_IN_ENTRY(dwTSPIVersion)
        DWORD_IN_ENTRY(dwPermanentProviderID)
    END_PARAM_TABLE()

    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerConfig(
    HWND    hwndOwner,
    DWORD   dwPermanentProviderID)
{
    // Although this func is never called by TAPI v2.0, we export
    // it so that the Telephony Control Panel Applet knows that it
    // can configure this provider via lineConfigProvider(),
    // otherwise Telephon.cpl will not consider it configurable
    BEGIN_PARAM_TABLE("TSPI_providerConfig")
        DWORD_IN_ENTRY(hwndOwner)
        DWORD_IN_ENTRY(dwPermanentProviderID)
    END_PARAM_TABLE()

    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerGenericDialogData(
    DWORD   dwObjectID,
    DWORD   dwObjectType,
    LPVOID  pParams,
    DWORD   dwSize)
{
    BEGIN_PARAM_TABLE("TSPI_providerGenericDialogData")
        DWORD_IN_ENTRY(dwObjectID)
        DWORD_IN_ENTRY(dwObjectType)
        DWORD_IN_ENTRY(pParams)
        DWORD_IN_ENTRY(dwSize)
    END_PARAM_TABLE()

    if( dwObjectType == TUISPIDLL_OBJECT_DIALOGINSTANCE )
    {
        // Handle requests from the UI for dialog-related data
        TSPUIDATA*  pData = (TSPUIDATA*)pParams;
        if( pData->dwRequestID )
        {
            // Complete asynch operation
            g_pfnCompletionProc(pData->dwRequestID, pData->tr);
        }

        // Update call state
        if( pData->tr == 0 )
        {
            pData->pCall->SetCallState(pData->nCallState);
        }
    }

    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerFreeDialogInstance(
    HDRVDIALOGINSTANCE hdDlgInst)
{
    BEGIN_PARAM_TABLE("TSPI_providerFreeDialogInstance")
        DWORD_IN_ENTRY(hdDlgInst)
    END_PARAM_TABLE()

    // W/o this function, TAPISVR.EXE crashes
    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerInstall(
    HWND    hwndOwner,
    DWORD   dwPermanentProviderID)
{
    // Although this func is never called by TAPI v2.0, we export
    // it so that the Telephony Control Panel Applet knows that it
    // can add this provider via lineAddProvider(), otherwise
    // Telephon.cpl will not consider it installable
    BEGIN_PARAM_TABLE("TSPI_providerInstall")
        DWORD_IN_ENTRY(hwndOwner)
        DWORD_IN_ENTRY(dwPermanentProviderID)
    END_PARAM_TABLE()

    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerRemove(
    HWND    hwndOwner,
    DWORD   dwPermanentProviderID)
{
    // Although this func is never called by TAPI v2.0, we export
    // it so that the Telephony Control Panel Applet knows that it
    // can remove this provider via lineRemoveProvider(), otherwise
    // Telephon.cpl will not consider it removable
    BEGIN_PARAM_TABLE("TSPI_providerRemove")
        DWORD_IN_ENTRY(hwndOwner)
        DWORD_IN_ENTRY(dwPermanentProviderID)
    END_PARAM_TABLE()

    return EPILOG(0);
}

LONG TSPIAPI TSPI_providerUIIdentify(
    LPWSTR   pszUIDLLName)
{
    BEGIN_PARAM_TABLE("TSPI_providerUIIdentify")
        STRING_OUT_ENTRY(pszUIDLLName)
    END_PARAM_TABLE()

    // Use the same DLL for both non-UI and UI functions
    char    szFileName[MAX_PATH+1];
    GetModuleFileNameA(g_hinst, szFileName, MAX_PATH);
    mbstowcs(pszUIDLLName, szFileName, strlen(szFileName) + 1);

    return EPILOG(0);
}

// TSPI_lineXxx functions /////////////////////////////////////////////

#define MIN(a, b) (a < b ? a : b)

LONG TSPIAPI TSPI_lineNegotiateTSPIVersion(
    DWORD   dwDeviceID,
    DWORD   dwLowVersion,
    DWORD   dwHighVersion,
    LPDWORD pdwTSPIVersion)
{
    BEGIN_PARAM_TABLE("TSPI_lineNegotiateTSPIVersion")
        DWORD_IN_ENTRY(dwDeviceID)
        DWORD_IN_ENTRY(dwLowVersion)
        DWORD_IN_ENTRY(dwHighVersion)
        DWORD_OUT_ENTRY(pdwTSPIVersion)
    END_PARAM_TABLE()

    LONG    tr = 0;
    
    if( dwLowVersion <= TAPI_CURRENT_VERSION )
    {
        *pdwTSPIVersion = MIN(TAPI_CURRENT_VERSION, dwHighVersion);
    }
    else
    {
        tr = LINEERR_INCOMPATIBLEAPIVERSION;
    }

    return EPILOG(tr);
}

LONG TSPIAPI TSPI_lineGetDevCaps(
    DWORD           dwDeviceID,
    DWORD           dwTSPIVersion,
    DWORD           dwExtVersion,
    LPLINEDEVCAPS   pldc)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetDevCaps")
        DWORD_IN_ENTRY(dwDeviceID)
        DWORD_IN_ENTRY(dwTSPIVersion)
        DWORD_IN_ENTRY(dwExtVersion)
        DWORD_IN_ENTRY(pldc)
    END_PARAM_TABLE()

    LONG            tr = 0;
    const wchar_t   szProviderInfo[] = L"WAVE Service Provider";
    const wchar_t   szLineName[] = L"WaveLine";

    pldc->dwNeededSize = sizeof(LINEDEVCAPS) +
                         sizeof(szProviderInfo) +
                         sizeof(szLineName);
    
    if( pldc->dwNeededSize <= pldc->dwTotalSize )
    {
        pldc->dwUsedSize = pldc->dwNeededSize;

        pldc->dwProviderInfoSize    = sizeof(szProviderInfo);
        pldc->dwProviderInfoOffset  = sizeof(LINEDEVCAPS) + 0;
        wchar_t* pszProviderInfo = (wchar_t*)((BYTE*)pldc + pldc->dwProviderInfoOffset);
        wcscpy(pszProviderInfo, szProviderInfo);
        
        pldc->dwLineNameSize        = sizeof(szLineName);
        pldc->dwLineNameOffset      = sizeof(LINEDEVCAPS) + sizeof(szProviderInfo);
        wchar_t* pszLineName = (wchar_t*)((BYTE*)pldc + pldc->dwLineNameOffset);
        wcscpy(pszLineName, szLineName);
    }
    else
    {
        pldc->dwUsedSize = sizeof(LINEDEVCAPS);
    }
    
    pldc->dwStringFormat      = STRINGFORMAT_ASCII;

// Microsoft recommended algorithm for
// calculating the permanent line ID
#define MAKEPERMLINEID(dwPermProviderID, dwDeviceID) \
    ((LOWORD(dwPermProviderID) << 16) | dwDeviceID)

    pldc->dwPermanentLineID   = MAKEPERMLINEID(g_dwPermanentProviderID, dwDeviceID - g_dwLineDeviceIDBase);
    pldc->dwAddressModes      = LINEADDRESSMODE_ADDRESSID;
    pldc->dwNumAddresses      = 1;
    pldc->dwBearerModes       = LINEBEARERMODE_VOICE;
    pldc->dwMediaModes        = LINEMEDIAMODE_INTERACTIVEVOICE;
    pldc->dwGenerateDigitModes= LINEDIGITMODE_DTMF;
    pldc->dwDevCapFlags       = LINEDEVCAPFLAGS_CLOSEDROP;
    pldc->dwMaxNumActiveCalls = 1;
    pldc->dwLineFeatures      = LINEFEATURE_MAKECALL;

    // DialParams
    pldc->MinDialParams = g_dpMin;
    pldc->MaxDialParams = g_dpMax;
    pldc->DefaultDialParams = g_dpDef;
    
    return EPILOG(tr);
}

LONG TSPIAPI TSPI_lineGetAddressCaps(
    DWORD              dwDeviceID,
    DWORD              dwAddressID,
    DWORD              dwTSPIVersion,
    DWORD              dwExtVersion,
    LPLINEADDRESSCAPS  pac)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetAddressCaps")
        DWORD_IN_ENTRY(dwDeviceID)
        DWORD_IN_ENTRY(dwAddressID)
        DWORD_IN_ENTRY(dwTSPIVersion)
        DWORD_IN_ENTRY(dwExtVersion)
    END_PARAM_TABLE()

    if( dwAddressID != 0 ) return EPILOG(LINEERR_INVALADDRESSID);

    pac->dwNeededSize           = sizeof(LINEADDRESSCAPS);
    pac->dwUsedSize             = sizeof(LINEADDRESSCAPS);

    pac->dwLineDeviceID         = dwDeviceID;
    pac->dwAddressSharing       = LINEADDRESSSHARING_PRIVATE;
    pac->dwCallInfoStates       = LINECALLINFOSTATE_MEDIAMODE |
                                  LINECALLINFOSTATE_APPSPECIFIC;

    pac->dwCallerIDFlags        = LINECALLPARTYID_UNAVAIL;
    pac->dwCalledIDFlags        = LINECALLPARTYID_UNAVAIL;
    pac->dwRedirectionIDFlags   = LINECALLPARTYID_UNAVAIL;
    pac->dwRedirectingIDFlags   = LINECALLPARTYID_UNAVAIL;

    pac->dwCallStates           = LINECALLSTATE_IDLE |
                                  LINECALLSTATE_DIALING |
                                  LINECALLSTATE_CONNECTED;

    pac->dwDialToneModes        = LINEDIALTONEMODE_UNAVAIL;
    pac->dwBusyModes            = LINEBUSYMODE_UNAVAIL;
    pac->dwSpecialInfo          = LINESPECIALINFO_UNAVAIL;

    pac->dwDisconnectModes      = LINEDISCONNECTMODE_UNAVAIL;

    pac->dwMaxNumActiveCalls    = 1;
    pac->dwAddrCapFlags         = LINEADDRCAPFLAGS_DIALED;

    pac->dwCallFeatures         = LINECALLFEATURE_DIAL |
                                  LINECALLFEATURE_DROP |
                                  LINECALLFEATURE_GENERATEDIGITS;

    pac->dwAddressFeatures      = LINEADDRFEATURE_MAKECALL;
    
    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineGetID(
    HDRVLINE    hdLine,
    DWORD       dwAddressID,
    HDRVCALL    hdCall,
    DWORD       dwSelect,
    LPVARSTRING pDeviceID,
    LPCWSTR     pszDeviceClass,
    HANDLE      hTargetProcess)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetID")
        DWORD_IN_ENTRY(hdLine)
        DWORD_IN_ENTRY(dwAddressID)
        DWORD_IN_ENTRY(hdCall)
        DWORD_IN_ENTRY(dwSelect)
        DWORD_IN_ENTRY(pDeviceID)
        STRING_IN_ENTRY(pszDeviceClass)
        DWORD_IN_ENTRY(hTargetProcess)
    END_PARAM_TABLE()
    
    LONG    tr = LINEERR_NODEVICE;

    /*
    // TAPI handles "tapi/line" and "tapi/phone",
    // so long as this function is present and returns LINEERR_NODEVICE

    LONG        tr = 0;
    CtspCall*   pCall = (dwSelect == LINECALLSELECT_CALL ? (CtspCall*)hdCall : 0);
    CtspLine*   pLine = (dwSelect == LINECALLSELECT_LINE ? (CtspLine*)hdLine : (pCall ? pCall->GetLine() : 0));

    if( lstrcmpiW(pszDeviceClass, L"tapi/line") == 0 )
    {
        pDeviceID->dwNeededSize = sizeof(VARSTRING) + sizeof(DWORD);

        if( pDeviceID->dwNeededSize <= pDeviceID->dwTotalSize )
        {
            pDeviceID->dwUsedSize      = pDeviceID->dwNeededSize;
            pDeviceID->dwStringFormat  = STRINGFORMAT_BINARY;
            pDeviceID->dwStringSize    = sizeof(DWORD);
            pDeviceID->dwStringOffset  = sizeof(VARSTRING);
            
            *((LPDWORD)(pDeviceID + 1)) = pLine->GetDeviceID();
        }
        else
        {
            pDeviceID->dwUsedSize = 3 * sizeof(DWORD);
            tr = LINEERR_STRUCTURETOOSMALL;
        }
    }
    else
    {
        tr = LINEERR_NODEVICE;
    }
    */
    
    return EPILOG(tr);
}

LONG TSPIAPI TSPI_lineOpen(
    DWORD       dwDeviceID,
    HTAPILINE   htLine,
    LPHDRVLINE  phdLine,
    DWORD       dwTSPIVersion,
    LINEEVENT   pfnEventProc)
{
    BEGIN_PARAM_TABLE("TSPI_lineOpen")
        DWORD_IN_ENTRY(dwDeviceID)
        DWORD_IN_ENTRY(htLine)
        DWORD_OUT_ENTRY(phdLine)
        DWORD_IN_ENTRY(dwTSPIVersion)
        DWORD_IN_ENTRY(pfnEventProc)
    END_PARAM_TABLE()
    
    LONG        tr = LINEERR_NOMEM;
    CtspLine*   pLine = new CtspLine(htLine, pfnEventProc, dwDeviceID);

    if( pLine )
    {
        *phdLine = (HDRVLINE)pLine;
        tr = 0;
    }
    
    return EPILOG(tr);
}

LONG TSPIAPI TSPI_lineSetDefaultMediaDetection(
    HDRVLINE    hdLine,
    DWORD       dwMediaModes)
{
    BEGIN_PARAM_TABLE("TSPI_lineSetDefaultMediaDetection")
        DWORD_IN_ENTRY(hdLine)
        DWORD_IN_ENTRY(dwMediaModes)
    END_PARAM_TABLE()
    
    // This function is required, or TAPISRV will crash
    // if any application attempts to open this line
    // for incoming calls (sigh)
    return EPILOG(LINEERR_OPERATIONUNAVAIL);
}

LONG TSPIAPI TSPI_lineGetNumAddressIDs(
    HDRVLINE    hdLine,
    LPDWORD     pdwNumAddressIDs)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetNumAddressIDs")
        DWORD_IN_ENTRY(hdLine)
        DWORD_OUT_ENTRY(pdwNumAddressIDs)
    END_PARAM_TABLE()
    
    // We only support 1 address (id=0)
    *pdwNumAddressIDs = 1;
    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineClose(
    HDRVLINE    hdLine)
{
    BEGIN_PARAM_TABLE("TSPI_lineClose")
        DWORD_IN_ENTRY(hdLine)
    END_PARAM_TABLE()

    delete (CtspLine*)hdLine;

    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineMakeCall(
    DRV_REQUESTID       dwRequestID,
    HDRVLINE            hdLine,
    HTAPICALL           htCall,
    LPHDRVCALL          phdCall,
    LPCWSTR             pszDestAddress,
    DWORD               dwCountryCode,
    LPLINECALLPARAMS    const pCallParams)
{
    BEGIN_PARAM_TABLE("TSPI_lineMakeCall")
        DWORD_IN_ENTRY(dwRequestID)
        DWORD_IN_ENTRY(hdLine)
        DWORD_IN_ENTRY(htCall)
        DWORD_OUT_ENTRY(phdCall)
        STRING_IN_ENTRY(pszDestAddress)
        DWORD_IN_ENTRY(dwCountryCode)
        DWORD_IN_ENTRY(pCallParams)
    END_PARAM_TABLE()

    LONG        tr;
    CtspLine*   pLine = (CtspLine*)hdLine;
    tr = pLine->MakeCall(dwRequestID, htCall, phdCall, pszDestAddress, dwCountryCode, pCallParams);
    return EPILOG(tr);
}

LONG TSPIAPI TSPI_lineSetStatusMessages(
    HDRVLINE    hdLine,
    DWORD       dwLineStates,
    DWORD       dwAddressStates)
{
    BEGIN_PARAM_TABLE("TSPI_lineSetStatusMessages")
        DWORD_IN_ENTRY(hdLine)
        DWORD_IN_ENTRY(dwLineStates)
        DWORD_IN_ENTRY(dwAddressStates)
    END_PARAM_TABLE()

    // Since we don't ever send status messages of any kind
    // we're happy at any limit. However, some apps require
    // that this function return successfully before a call
    // can be made, e.g. Outlook 98.

    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineDrop(
    DRV_REQUESTID   dwRequestID,
    HDRVCALL        hdCall,
    LPCSTR          psUserUserInfo,
    DWORD           dwSize)
{
    BEGIN_PARAM_TABLE("TSPI_lineDrop")
        DWORD_IN_ENTRY(dwRequestID)
        DWORD_IN_ENTRY(hdCall)
        DWORD_IN_ENTRY(psUserUserInfo)
        DWORD_IN_ENTRY(dwSize)
    END_PARAM_TABLE()

    CtspCall*   pCall = (CtspCall*)hdCall;

    pCall->Drop();
    g_pfnCompletionProc(dwRequestID, 0);

    return EPILOG(dwRequestID);
}

LONG TSPIAPI TSPI_lineCloseCall(
    HDRVCALL    hdCall)
{
    BEGIN_PARAM_TABLE("TSPI_lineCloseCall")
        DWORD_IN_ENTRY(hdCall)
    END_PARAM_TABLE()

    // Note that in TAPI 2.0 TSPI_lineCloseCall can get called
    // without TSPI_lineDrop ever being called, so we need to
    // be prepared for either case.
    CtspCall*   pCall = (CtspCall*)hdCall;

    pCall->Drop();
    delete pCall;

    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineGetAddressStatus(
    HDRVLINE            hdLine,
    DWORD               dwAddressID,
    LPLINEADDRESSSTATUS pas)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetAddressStatus")
        DWORD_IN_ENTRY(hdLine)
        DWORD_IN_ENTRY(dwAddressID)
        DWORD_IN_ENTRY(pas)
    END_PARAM_TABLE()

    pas->dwNeededSize = sizeof(LINEADDRESSSTATUS);
    pas->dwUsedSize   = sizeof(LINEADDRESSSTATUS);
    pas->dwNumActiveCalls  = ((CtspLine*)hdLine)->GetNumActiveCalls();
    pas->dwAddressFeatures = LINEADDRFEATURE_MAKECALL;
    
    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineGetCallAddressID(
    HDRVCALL    hdCall,
    LPDWORD     pdwAddressID)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetCallAddressID")
        DWORD_IN_ENTRY(hdCall)
        DWORD_OUT_ENTRY(pdwAddressID)
    END_PARAM_TABLE()

    // We only support 1 address (id=0)
    *pdwAddressID = 0;

    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineGetCallInfo(
    HDRVCALL        hdCall,
    LPLINECALLINFO  pli)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetCallInfo")
        DWORD_IN_ENTRY(hdCall)
        DWORD_IN_ENTRY(pli)
    END_PARAM_TABLE()

    pli->dwNeededSize           = sizeof(LINECALLINFO);
    pli->dwUsedSize             = sizeof(LINECALLINFO);
    
    pli->dwBearerMode           = LINEBEARERMODE_VOICE;
    pli->dwMediaMode            = LINEMEDIAMODE_INTERACTIVEVOICE;
    pli->dwCallStates           = LINECALLSTATE_IDLE |
                                  LINECALLSTATE_DIALING |
                                  LINECALLSTATE_CONNECTED |
                                  LINECALLSTATE_PROCEEDING |
                                  LINECALLSTATE_UNKNOWN;

    pli->dwOrigin               = LINECALLORIGIN_OUTBOUND;
    pli->dwReason               = LINECALLREASON_DIRECT;
    pli->dwCallerIDFlags        = LINECALLPARTYID_UNAVAIL;
    pli->dwCalledIDFlags        = LINECALLPARTYID_UNAVAIL;
    pli->dwConnectedIDFlags     = LINECALLPARTYID_UNAVAIL;
    pli->dwRedirectionIDFlags   = LINECALLPARTYID_UNAVAIL;
    pli->dwRedirectingIDFlags   = LINECALLPARTYID_UNAVAIL;
    
    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineGetCallStatus(
    HDRVCALL            hdCall,
    LPLINECALLSTATUS    pls)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetCallStatus")
        DWORD_IN_ENTRY(hdCall)
        DWORD_IN_ENTRY(pls)
    END_PARAM_TABLE()

    CtspCall*   pCall = (CtspCall*)hdCall;

    pls->dwNeededSize = sizeof(LINECALLSTATUS);
    pls->dwUsedSize   = sizeof(LINECALLSTATUS);
    pls->dwCallState  = pCall->GetCallState();
    
    if( pls->dwCallState != LINECALLSTATE_IDLE )
    {
        pls->dwCallFeatures = LINECALLFEATURE_DROP;
    }
    
    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineGetLineDevStatus(
    HDRVLINE        hdLine,
    LPLINEDEVSTATUS plds)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetLineDevStatus")
        DWORD_IN_ENTRY(hdLine)
        DWORD_IN_ENTRY(plds)
    END_PARAM_TABLE()

    plds->dwUsedSize        = sizeof(LINEDEVSTATUS);
    plds->dwNeededSize      = sizeof(LINEDEVSTATUS);
    
    plds->dwNumActiveCalls  = ((CtspLine*)hdLine)->GetNumActiveCalls();
    plds->dwDevStatusFlags  = LINEDEVSTATUSFLAGS_CONNECTED |
                              LINEDEVSTATUSFLAGS_INSERVICE;

    return EPILOG(0);
}

LONG TSPIAPI TSPI_lineGetIcon(
    DWORD   dwDeviceID,
    LPCWSTR lpszDeviceClass,  
    LPHICON lphIcon)
{
    BEGIN_PARAM_TABLE("TSPI_lineGetIcon")
        DWORD_IN_ENTRY(dwDeviceID)
        STRING_IN_ENTRY(lpszDeviceClass)
        DWORD_OUT_ENTRY(lphIcon)
    END_PARAM_TABLE()

    *lphIcon = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_WAVETSP));

    return EPILOG(0);
}

