/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Module Name:

    tapi.c

Abstract:

    This module contains all the TAPI_OID processing routines.  

Author:

    Hakan Berk - Microsoft, Inc. (hakanb@microsoft.com) Feb-2000

Environment:

    Windows 2000 kernel mode Miniport driver or equivalent.

Revision History:

---------------------------------------------------------------------------*/

#include <ntddk.h>
#include <ntddndis.h>
#include <ndis.h>
#include <ndiswan.h>
#include <ndistapi.h>
#include <ntverp.h>

#include "debug.h"
#include "timer.h"
#include "bpool.h"
#include "ppool.h"
#include "util.h"
#include "packet.h"
#include "protocol.h"
#include "miniport.h"
#include "tapi.h"
#include "fsm.h"

extern TIMERQ gl_TimerQ;

extern NPAGED_LOOKASIDE_LIST gl_llistWorkItems;

///////////////////////////////////////////////////////////////////////////////////
//
// Tapi provider, line and call context functions
//
///////////////////////////////////////////////////////////////////////////////////

VOID 
ReferenceCall(
    IN CALL* pCall,
    IN BOOLEAN fAcquireLock
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will increment the reference count on the call object.
    
    CAUTION: If fAcquireLock is set, this function will acquire the lock for the
             call, otherwise it will assume the caller owns the lock.
    
Parameters:

    pCall _ A pointer to our call information structure.

    fAcquireLock _ Indicates if the caller already has the lock or not.
                   Caller must set this flag to FALSE if it has the lock, 
                   otherwise it must be supplied as TRUE.

Return Values:

    None
---------------------------------------------------------------------------*/
{
    LONG lRef;
    
    TRACE( TL_V, TM_Tp, ("+ReferenceCall") );

    if ( fAcquireLock )
        NdisAcquireSpinLock( &pCall->lockCall );

    lRef = ++pCall->lRef;

    if ( fAcquireLock )
        NdisReleaseSpinLock( &pCall->lockCall );

    TRACE( TL_V, TM_Tp, ("-ReferenceCall=$%d",lRef) );
}

VOID 
DereferenceCall(
    IN CALL *pCall
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will decrement the reference count on the call object

    If ref count drops to 0 (which means the call has been closed),
    it will set the CLBF_CallClosed bit. Then it will call TpCloseCallComplete() 
    function which eventually handles destroying the resources allocated for 
    this call context.

    CAUTION: All locks must be released before calling this function because
             it may cause a set of cascading events.

Parameters:

    pCall _ A pointer ot our call information structure.

Return Values:

    None
---------------------------------------------------------------------------*/
{
    BOOLEAN fCallTpCloseCallComplete = FALSE;
    LONG lRef;

    TRACE( TL_V, TM_Tp, ("+DereferenceCall") );

    NdisAcquireSpinLock( &pCall->lockCall );

    lRef = --pCall->lRef;

    if ( lRef == 0 )
    {
        pCall->ulClFlags &= ~CLBF_CallOpen;
        pCall->ulClFlags &= ~CLBF_CallClosePending;
        pCall->ulClFlags |= CLBF_CallClosed;
        
        fCallTpCloseCallComplete = TRUE;
    }

    NdisReleaseSpinLock( &pCall->lockCall );

    if ( fCallTpCloseCallComplete )
        TpCloseCallComplete( pCall );

    TRACE( TL_V, TM_Tp, ("-DereferenceCall=$%d",lRef) );
}


VOID 
ReferenceLine(
    IN LINE* pLine,
    IN BOOLEAN fAcquireLock
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will increment the reference count on the line object.
    
    CAUTION: If fAcquireLock is set, this function will acquire the lock for the
             line, otherwise it will assume the caller owns the lock.
    
Parameters:

    pLine _ A pointer to our line information structure.

    fAcquireLock _ Indicates if the caller already has the lock or not.
                   Caller must set this flag to FALSE if it has the lock, 
                   otherwise it must be supplied as TRUE.

Return Values:

    None
---------------------------------------------------------------------------*/
{
    LONG lRef;
    
    TRACE( TL_V, TM_Tp, ("+ReferenceLine") );

    if ( fAcquireLock )
        NdisAcquireSpinLock( &pLine->lockLine );

    lRef = ++pLine->lRef;

    if ( fAcquireLock )
        NdisReleaseSpinLock( &pLine->lockLine );

    TRACE( TL_V, TM_Tp, ("-ReferenceLine=$%d",lRef) );
        
}

VOID 
DereferenceLine(
    IN LINE *pLine
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will decrement the reference count on the line object

    If the ref count drops to 0 (which means the line has been closed),
    it will set the LNBF_CallClosed bit. Then it will call TpCloseLineComplete() 
    function which eventually handles destroying the resources allocated for 
    this line context.

    CAUTION: All locks must be released before calling this function because
             it may cause a set of cascading events.
    
Parameters:

    pLine _ A pointer to our line information structure.

Return Values:

    None
---------------------------------------------------------------------------*/
{
    BOOLEAN fCallTpCloseLineComplete = FALSE;
    LONG lRef;

    TRACE( TL_V, TM_Tp, ("+DereferenceLine") );
    

    NdisAcquireSpinLock( &pLine->lockLine );

    lRef = --pLine->lRef;
    
    if ( lRef == 0 )
    {
        pLine->ulLnFlags &= ~LNBF_LineOpen;
        pLine->ulLnFlags &= ~LNBF_LineClosePending;
        pLine->ulLnFlags |= LNBF_LineClosed;

        fCallTpCloseLineComplete = TRUE;
    }

    NdisReleaseSpinLock( &pLine->lockLine );

    if ( fCallTpCloseLineComplete )
        TpCloseLineComplete( pLine );

    TRACE( TL_V, TM_Tp, ("-DereferenceLine=$%d",lRef) );
}

VOID 
ReferenceTapiProv(
    IN ADAPTER* pAdapter,
    IN BOOLEAN fAcquireLock
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will increment the reference count on the tapi prov object.
    
    CAUTION: If fAcquireLock is set, this function will acquire the lock for the
             line, otherwise it will assume the caller owns the lock.
    
Parameters:

    pAdapter _ A pointer to our adapter information structure.

    fAcquireLock _ Indicates if the caller already has the lock or not.
                   Caller must set this flag to FALSE if it has the lock, 
                   otherwise it must be supplied as TRUE.

Return Values:

    None
---------------------------------------------------------------------------*/
{
    LONG lRef;
    
    TRACE( TL_V, TM_Tp, ("+ReferenceTapiProv") );

    if ( fAcquireLock )
        NdisAcquireSpinLock( &pAdapter->lockAdapter );

    lRef = ++pAdapter->TapiProv.lRef;

    if ( fAcquireLock )
        NdisReleaseSpinLock( &pAdapter->lockAdapter );

    TRACE( TL_V, TM_Tp, ("-ReferenceTapiProv=$%d",lRef) );
}


VOID 
DereferenceTapiProv(
    IN ADAPTER *pAdapter
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will decrement the reference count on the tapi prov object

    CAUTION: All locks must be released before calling this function because
             it may cause a set of cascading events.
    
Parameters:

    pAdapter _ A pointer to our adapter line information structure.

Return Values:

    None
---------------------------------------------------------------------------*/
{
    BOOLEAN fCallTpProviderShutdownComplete = FALSE;
    LONG lRef;

    TRACE( TL_V, TM_Tp, ("+DereferenceTapiProv") );

    NdisAcquireSpinLock( &pAdapter->lockAdapter );

    lRef = --pAdapter->TapiProv.lRef;
    
    if ( lRef == 0 )
    {
        pAdapter->TapiProv.ulTpFlags &= ~TPBF_TapiProvInitialized;
        pAdapter->TapiProv.ulTpFlags &= ~TPBF_TapiProvShutdownPending;
        pAdapter->TapiProv.ulTpFlags |= TPBF_TapiProvShutdown;
        
        fCallTpProviderShutdownComplete = TRUE;
    }

    NdisReleaseSpinLock( &pAdapter->lockAdapter );

    if ( fCallTpProviderShutdownComplete )
        TpProviderShutdownComplete( pAdapter );

    TRACE( TL_V, TM_Tp, ("-DereferenceTapiProv=$%d",lRef) );
        
}

NDIS_STATUS 
TpProviderInitialize(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_PROVIDER_INITIALIZE pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request initializes the TAPI portion of the miniport.

    It will set Tapi Provider's state to initialize, and reference both the
    owning adapter and tapi provider.

Parameters:

    Adapter _ A pointer ot our adapter information structure.

    Request _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_PROVIDER_INITIALIZE
    {
        IN  ULONG       ulRequestID;
        IN  ULONG       ulDeviceIDBase;
        OUT ULONG       ulNumLineDevs;
        OUT ULONG       ulProviderID;

    } NDIS_TAPI_PROVIDER_INITIALIZE, *PNDIS_TAPI_PROVIDER_INITIALIZE;

Return Values:

    NDIS_STATUS_SUCCESS

---------------------------------------------------------------------------*/
{
    NDIS_STATUS status = NDIS_STATUS_RESOURCES;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpProviderInitialize") );

    do 
    {
        if ( pRequest == NULL || pAdapter == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpProviderInitialize: Invalid parameter") );  

            status = NDIS_STATUS_TAPI_INVALPARAM;

            break;
        }

        //
        // Initialize the tapi provider context
        //
        NdisZeroMemory( &pAdapter->TapiProv, sizeof( pAdapter->TapiProv ) );
    
        //
        // Try to allocate resources
        //
        NdisAllocateMemoryWithTag( (PVOID) &pAdapter->TapiProv.LineTable, 
                                   sizeof( LINE* ) * pAdapter->nMaxLines,
                                   MTAG_TAPIPROV );
    
        if ( pAdapter->TapiProv.LineTable == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpProviderInitialize: Could not allocate line table") );  

            break;
        }

        NdisZeroMemory( pAdapter->TapiProv.LineTable, sizeof( LINE* ) * pAdapter->nMaxLines );

        pAdapter->TapiProv.hCallTable = InitializeHandleTable( pAdapter->nMaxLines * pAdapter->nCallsPerLine );

        if ( pAdapter->TapiProv.hCallTable == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpProviderInitialize: Could not allocate call handle table") );   

            break;
        }
    
        pAdapter->TapiProv.ulTpFlags = TPBF_TapiProvInitialized;
    
        pAdapter->TapiProv.ulDeviceIDBase = pRequest->ulDeviceIDBase;

        //
        // Do referencing
        //
        ReferenceTapiProv( pAdapter, FALSE );
    
        ReferenceAdapter( pAdapter, TRUE );

        status = NDIS_STATUS_SUCCESS;

    } while ( FALSE );

    if ( status == NDIS_STATUS_SUCCESS )
    {
        //
        // Set output information
        //
        pRequest->ulNumLineDevs = pAdapter->nMaxLines;
    
        pRequest->ulProviderID = (ULONG_PTR) pAdapter->MiniportAdapterHandle;

    }
    else
    {
        //
        // Somethings failed, clean up
        //
        TpProviderCleanup( pAdapter );
    }

    TRACE( TL_N, TM_Tp, ("-TpProviderInitialize=$%x",status) );
    
    return status;
}

NDIS_STATUS
TpProviderShutdown(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_PROVIDER_SHUTDOWN pRequest,
    IN BOOLEAN fNotifyNDIS
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request shuts down the miniport. The miniport should terminate any 
    activities it has in progress.

    This operation might pend as there might be lines and call contexts still 
    active. So this function marks the tapi provider context as close pending
    and calls TpCloseLine() on all active calls, and removes the reference added
    on the tapi provider in TpProviderInitialize(). 

    When ref count on the tapi provider context reaches 0, TpProviderShutdownComplete()
    will be called to clean up the tapi provider context, and remove the reference 
    on the owning adapter.

Parameters:

    pAdapter _ A pointer to our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.
               If supplied as NULL, then we do not need to notify NDIS.

    typedef struct _NDIS_TAPI_PROVIDER_SHUTDOWN
    {
        IN  ULONG       ulRequestID;

    } NDIS_TAPI_PROVIDER_SHUTDOWN, *PNDIS_TAPI_PROVIDER_SHUTDOWN;


    fNotifyNDIS _ Indicates if NDIS needs to be notified about the completion 
                  of this operation

Return Values:

    NDIS_STATUS_SUCCESS:
        Tapi provider shutdown and cleaned up succesfully.
    
    NDIS_STATUS_PENDING
        Shutdown operation is pending. When all shutdown operation completes
        owning adapter context will be dereferenced.

---------------------------------------------------------------------------*/
{
    NDIS_STATUS status;
    BOOLEAN fDereferenceTapiProv = FALSE;
    BOOLEAN fLockAcquired = FALSE;
    
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpProviderShutdown") );

    do
    {
        if ( pRequest == NULL || pAdapter == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpProviderShutdown: Invalid parameter") );    

            status = NDIS_STATUS_TAPI_INVALPARAM;

            break;
        }

        NdisAcquireSpinLock( &pAdapter->lockAdapter );

        fLockAcquired = TRUE;

        //
        // See if tapi provider was initialized at all
        //
        if ( !( pAdapter->TapiProv.ulTpFlags & TPBF_TapiProvInitialized ) )
        {
            //
            // Tapi provider was not initialized so just return
            //
            status = NDIS_STATUS_SUCCESS;
            
            break;
        }


        //
        // See if we can shutdown immediately
        //
        if ( pAdapter->TapiProv.lRef == 1 )
        {
            //
            // We are holding the only reference, so we can shutdown immediately
            //
            pAdapter->TapiProv.ulTpFlags &= ~TPBF_TapiProvInitialized;
    
            pAdapter->TapiProv.ulTpFlags |= TPBF_TapiProvShutdown;
    
            status = NDIS_STATUS_SUCCESS;
        }
        else
        {
            UINT i;
    
            //
            // Mark Tapi provider as shutdown pending
            //
            pAdapter->TapiProv.ulTpFlags |= TPBF_TapiProvShutdownPending;
            
            //
            // Mark tapi prov if the result of this operation needs to be reported to NDIS
            //
            if ( fNotifyNDIS )
                pAdapter->TapiProv.ulTpFlags |= TPBF_NotifyNDIS;
    
            // 
            // Close all active lines
            //
            for ( i = 0; i < pAdapter->nMaxLines; i++)
            {
                NDIS_TAPI_CLOSE DummyRequest;
            
                LINE* pLine = (LINE*) pAdapter->TapiProv.LineTable[i];
    
                if ( pLine )
                {
                            
                    DummyRequest.hdLine = pLine->hdLine;
    
                    NdisReleaseSpinLock( &pAdapter->lockAdapter );
        
                    TpCloseLine( pAdapter, &DummyRequest, FALSE );
        
                    NdisAcquireSpinLock( &pAdapter->lockAdapter );
                }
    
            }

            status = NDIS_STATUS_PENDING;       
        }

        fDereferenceTapiProv = TRUE;
        
    } while ( FALSE );

    if ( fLockAcquired ) 
    {
        NdisReleaseSpinLock( &pAdapter->lockAdapter );
    }

    if ( fDereferenceTapiProv )
    {
        DereferenceTapiProv( pAdapter );
    }

    TRACE( TL_N, TM_Tp, ("-TpProviderShutdown=$%x",status) );

    return status;
}

#define INVALID_LINE_HANDLE                         (HDRV_LINE) -1

HDRV_LINE
TpGetHdLineFromDeviceId(
               ADAPTER* pAdapter,
               ULONG ulID
               )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function is used to map a Tapi Device Id to the driver's line handle.
    It returns INVALID_LINE_HANDLE if it can not map the device id.

    REMARK: 
      - pAdapter must not be NULL.
      - It must be called from one of the Tp...OidHandler() functions since
        this function relies on this and assumes there won't be any 
        synchronization problems.

Parameters:

    pAdapter _ A pointer to our adapter information structure.

    uldID _ Device Id that identifies a line context
    
Return Values:

    Handle to the line context if device id can be mapped to a valid line context,
    and INVALID_LINE_HANDLE otherwise.
       
---------------------------------------------------------------------------*/
{
    if ( !( pAdapter->TapiProv.ulTpFlags & TPBF_TapiProvShutdownPending ) &&
          ( pAdapter->TapiProv.ulTpFlags & TPBF_TapiProvInitialized ) )
    {
        if ( ( ulID < ( pAdapter->TapiProv.ulDeviceIDBase + pAdapter->nMaxLines ) ) &&                    
             ( ulID >= pAdapter->TapiProv.ulDeviceIDBase ) )                                    
        {
            return (HDRV_LINE) ( ulID - pAdapter->TapiProv.ulDeviceIDBase );
        }
    }

    return INVALID_LINE_HANDLE;
}

LINE* 
TpGetLinePtrFromHdLineEx(
               ADAPTER* pAdapter,
               HDRV_LINE hdLine
               )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function is used to map a driver line handle to the line context ptr.
    It returns NULL if it can not map the handle. This is exactly the same as
    TpGetLinePtrFromHdLine function except that it doesn't check for the
    shutdown state.

    REMARK: 
      - pAdapter must not be NULL.
      - It must be called from one of the Tp...OidHandler() functions since
        this function relies on this and assumes there won't be any 
        synchronization problems. 
        (Basically assumes pAdapter->lock is being held)

Parameters:

    pAdapter _ A pointer to our adapter information structure.

    hdL _ Driver's line handle
    
Return Values:

    Pointer to the Line context associated with the Line handle provided
    if mapping is succesful, and NULL otherwise.
    
---------------------------------------------------------------------------*/
{
    if ( pAdapter->TapiProv.ulTpFlags & TPBF_TapiProvInitialized )
    {
        if ( ( (ULONG) hdLine < (ULONG) pAdapter->nMaxLines ) )
        {
            ASSERT( pAdapter->TapiProv.LineTable != 0 );
            
            return pAdapter->TapiProv.LineTable[ (ULONG) hdLine ];
        }
    }

    return NULL;
}


LINE* 
TpGetLinePtrFromHdLine(
               ADAPTER* pAdapter,
               HDRV_LINE hdLine
               )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function is used to map a driver line handle to the line context ptr.
    It returns NULL if it can not map the handle.

    REMARK: 
      - pAdapter must not be NULL.
      - It must be called from one of the Tp...OidHandler() functions since
        this function relies on this and assumes there won't be any 
        synchronization problems. 
        (Basically assumes pAdapter->lock is being held)

Parameters:

    pAdapter _ A pointer to our adapter information structure.

    hdL _ Driver's line handle
    
Return Values:

    Pointer to the Line context associated with the Line handle provided
    if mapping is succesful, and NULL otherwise.
    
---------------------------------------------------------------------------*/
{
    if ( !( pAdapter->TapiProv.ulTpFlags & TPBF_TapiProvShutdownPending ) &&
          ( pAdapter->TapiProv.ulTpFlags & TPBF_TapiProvInitialized ) )
    {
        if ( ( (ULONG) hdLine < (ULONG) pAdapter->nMaxLines ) )
        {
            ASSERT( pAdapter->TapiProv.LineTable != 0 );
            
            return pAdapter->TapiProv.LineTable[ (ULONG) hdLine ];
        }
    }

    return NULL;
}


NDIS_STATUS
TpOpenLine(
    ADAPTER* pAdapter,
    PNDIS_TAPI_OPEN pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function opens the line device whose device ID is given, returning
    the miniport�s handle for the device. The miniport must retain the
    Connection Wrapper's handle for the device for use in subsequent calls to
    the LINE_EVENT callback procedure.

    hdLine returned is the index to the pAdapter->TapiProv.LineTable array
    that holds the pointer to the new line context.

Parameters:

    pAdapter _ A pointer to our adapter information structure.

    pRequest - A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_OPEN
    {
        IN  ULONG       ulRequestID;
        IN  ULONG       ulDeviceID;
        IN  HTAPI_LINE  htLine;
        OUT HDRV_LINE   hdLine;

    } NDIS_TAPI_OPEN, *PNDIS_TAPI_OPEN;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_PENDING
    NDIS_STATUS_TAPI_ALLOCATED
    NDIS_STATUS_TAPI_INVALMEDIAMODE
    NDIS_STATUS_FAILURE
    
---------------------------------------------------------------------------*/
{
    NDIS_STATUS status = NDIS_STATUS_FAILURE;
    HDRV_LINE hdLine = INVALID_LINE_HANDLE;
    LINE* pLine = NULL;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpOpenLine") );

    do
    {
        if ( pRequest == NULL || pAdapter == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpOpenLine: Invalid parameter") );    

            status = NDIS_STATUS_TAPI_INVALPARAM;

            break;
        }
    
        //
        // Map the device id to an entry in our line table
        //
        hdLine = TpGetHdLineFromDeviceId( pAdapter, pRequest->ulDeviceID );

        if ( hdLine == INVALID_LINE_HANDLE )
        {
            TRACE( TL_N, TM_Tp, ("TpOpenLine: Invalid handle supplied") );  

            break;
        }

        //
        // Make sure the line is not busy already
        //
        if ( TpGetLinePtrFromHdLine( pAdapter, hdLine ) != NULL )
        {
            TRACE( TL_N, TM_Tp, ("TpOpenLine: Line is busy") ); 

            break;
        }

        //
        // Allocate the line context
        //
        if ( ALLOC_LINE( &pLine ) != NDIS_STATUS_SUCCESS )
        {
            TRACE( TL_A, TM_Tp, ("TpOpenLine: Could not allocate context") );   

            break;
        }

        //
        // Initialize line context
        //
        NdisZeroMemory( pLine, sizeof( LINE ) );
        
        pLine->tagLine = MTAG_LINE;

        NdisAllocateSpinLock( &pLine->lockLine );

        pLine->ulLnFlags = LNBF_LineOpen;

        if ( pAdapter->fClientRole )
        {
            pLine->ulLnFlags |= LNBF_MakeOutgoingCalls;
        }

        //
        // Copy related info from adapter context
        //
        pLine->pAdapter = pAdapter;

        pLine->nMaxCalls = pAdapter->nCallsPerLine;

        InitializeListHead( &pLine->linkCalls );

        //
        // Set tapi handles
        //
        pLine->htLine = pRequest->htLine;

        pLine->hdLine = hdLine;
        
        //
        // Insert new line context to line table of tapi provider
        //
        NdisAcquireSpinLock( &pAdapter->lockAdapter );
        
        pAdapter->TapiProv.LineTable[ (ULONG) hdLine ] = pLine;

        pAdapter->TapiProv.nActiveLines++;

        NdisReleaseSpinLock( &pAdapter->lockAdapter );

        //
        // Do referencing
        //
        ReferenceLine( pLine, FALSE );

        ReferenceTapiProv( pAdapter, TRUE );

        status = NDIS_STATUS_SUCCESS;
        
    } while ( FALSE );

    if ( status == NDIS_STATUS_SUCCESS )
    {
        pRequest->hdLine = hdLine;
    }
        
    TRACE( TL_N, TM_Tp, ("-TpOpenLine=$%x",status) );

    return status;
}

NDIS_STATUS 
TpCloseLine(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_CLOSE pRequest,
    IN BOOLEAN fNotifyNDIS
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request closes the specified open line device after completing or
    aborting all outstanding calls and asynchronous requests on the device.

    It will remove the reference on the line context added in TpOpenLine().

    It will be called from 2 places:
        1. When miniport receives an OID_TAPI_CLOSE.
           In this case, fNotifyNDIS will be set as TRUE.
           
        2. When miniport is halting, TpProviderShutdown() will call
           this function for every active line context.
    
Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_CLOSE
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_LINE   hdLine;

    } NDIS_TAPI_CLOSE, *PNDIS_TAPI_CLOSE;

    fNotifyNDIS _ Indicates if NDIS needs to be notified about the completion 
                  of this operation
                  
Return Values:

    NDIS_STATUS_SUCCESS: Line context destroyed succesfully.
        
    NDIS_STATUS_PENDING: Close operation is pending. When line is closed, 
                         tapi provider will be dereferenced.
        
    NDIS_STATUS_TAPI_INVALLINEHANDLE: An invalid handle was supplied. 
                                      No operations performed.

---------------------------------------------------------------------------*/
{
    LINE* pLine = NULL;
    BOOLEAN fLockReleased = FALSE;
    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
    
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpCloseLine") );


    do
    {
        if ( pRequest == NULL || pAdapter == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpCloseLine: Invalid parameter") );   

            status = NDIS_STATUS_TAPI_INVALPARAM;

            break;
        }

        pLine = TpGetLinePtrFromHdLineEx( pAdapter, pRequest->hdLine );


        if ( pLine == NULL )
        {
            TRACE( TL_N, TM_Tp, ("TpCloseLine: Invalid handle supplied") ); 

            status = NDIS_STATUS_TAPI_INVALLINEHANDLE;
            
            break;
        }

        //
        // Remove line context from tapi providers line table
        // and invalidate the handle, as we do not want any more 
        // requests on this line context.
        //
        // The active line counter will be adjusted in TpCloseLineComplete()
        // when we deallocate the line context.
        //
        NdisAcquireSpinLock( &pAdapter->lockAdapter );
    
        pAdapter->TapiProv.LineTable[ (ULONG) pRequest->hdLine ] = NULL;
    
        NdisReleaseSpinLock( &pAdapter->lockAdapter );

        //
        // Now start closing the line
        //
        NdisAcquireSpinLock( &pLine->lockLine );

        //
        // Do not accept any more incoming calls
        //
        pLine->ulLnFlags &= ~LNBF_AcceptIncomingCalls;

        //
        // Mark the line as close pending, so that we do not accept
        // any more requests on it
        //
        pLine->ulLnFlags |= LNBF_LineClosePending;

        if ( fNotifyNDIS )
            pLine->ulLnFlags |= LNBF_NotifyNDIS;

        while ( !IsListEmpty( &pLine->linkCalls ) )
        {
            CALL* pCall = NULL;
            NDIS_TAPI_CLOSE_CALL DummyRequest;
            
            //
            // Retrieve a call context from the head of active call list
            // and close it.
            //
            pCall = (CALL*) CONTAINING_RECORD( pLine->linkCalls.Flink,
                                               CALL,
                                               linkCalls );

            NdisReleaseSpinLock( &pLine->lockLine );

            DummyRequest.hdCall = pCall->hdCall;

            //
            // This will remove the call from the list,
            // so there will be a new call at the head of the list
            // next time we retrieve.
            //
            TpCloseCall( pAdapter, &DummyRequest, FALSE );

            NdisAcquireSpinLock( &pLine->lockLine );
        } 

        status = NDIS_STATUS_PENDING;
        
    } while ( FALSE );

    if ( status == NDIS_STATUS_PENDING )
    {
        BOOLEAN fNotifyTapiOfInternalLineClose = !( pLine->ulLnFlags & LNBF_NotifyNDIS );
    
        NdisReleaseSpinLock( &pLine->lockLine );

        //
        // Check if this is an internal request to close the line, 
        // notify TAPI if it is
        //
        if ( fNotifyTapiOfInternalLineClose )
        {
            NDIS_TAPI_EVENT TapiEvent;

            NdisZeroMemory( &TapiEvent, sizeof( NDIS_TAPI_EVENT ) );
            
            TapiEvent.htLine = pLine->htLine;
            TapiEvent.ulMsg = LINE_CLOSE;

            NdisMIndicateStatus( pLine->pAdapter->MiniportAdapterHandle,
                                 NDIS_STATUS_TAPI_INDICATION,
                                 &TapiEvent,
                                 sizeof( NDIS_TAPI_EVENT ) );   
        }

        if ( pAdapter->TapiProv.nActiveLines == 1 )
        {
            //
            // We are closing the last line so notify protocol about this so
            // it can remove packet filters
            //
            WORKITEM* pWorkItem = NULL;
            PVOID Args[4];

            Args[0] = (PVOID) BN_ResetFiltersForCloseLine;           // Is a reset filters request
            Args[1] = (PVOID) pLine;
       
            //
            // Allocate work item for reenumerating bindings
            //
            pWorkItem = AllocWorkItem( &gl_llistWorkItems,
                                       ExecBindingWorkItem,
                                       NULL,
                                       Args,
                                       BWT_workPrStartBinds );

            if ( pWorkItem ) 
            {
               //
               // Schedule the work item.
               //
               // Note that we need to referencing here, because we do not want TpCloseLineCopmlete()
               // to be called before the work item is executed.
               //
               // This reference will be removed when the work item is executed.
               //
               ReferenceLine( pLine, TRUE );
               
               ScheduleWorkItem( pWorkItem );

               //
               // In this case this request will be completed later
               //
               status = NDIS_STATUS_PENDING;
            }
        }            

        //
        // Remove the reference added in line open
        //
        DereferenceLine( pLine );

    }

    TRACE( TL_N, TM_Tp, ("-TpCloseLine=$%x",status) );

    return status;
}


NDIS_STATUS 
TpCloseCall(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_CLOSE_CALL pRequest,
    IN BOOLEAN fNotifyNDIS
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function is called to close a call.

    It will remove one of the references added in TpMakeCall() on the call
    context.
    
    It will be called from 2 places:
        1. When miniport receives an OID_TAPI_CLOSE_CALL.
           In this case, fNotifyNDIS will be set as TRUE.
           
        2. When miniport is halting, TpCloseLine() will call
           this function for every active call context.
   
Parameters:

    pAdapter _ A pointer to our adapter information structure.
    
    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_CLOSE_CALL
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_CALL   hdCall;

    } NDIS_TAPI_CLOSE_CALL, *PNDIS_TAPI_CLOSE_CALL;


    fNotifyNDIS _ Indicates if NDIS needs to be notified about the completion 
                  of this operation
    
Return Values:

    NDIS_STATUS_SUCCESS: Call is succesfully closed and resources are freed.
    
    NDIS_STATUS_PENDING: Call close is pending on active calls.
                         When call is closed the owning line context will be
                         dereferenced.
                         
---------------------------------------------------------------------------*/
    
{
    NDIS_STATUS status = NDIS_STATUS_FAILURE;
    CALL* pCall = NULL;
    BOOLEAN fLockReleased = FALSE;
    BOOLEAN fDereferenceCall = FALSE;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpCloseCall") );
    
    do
    {
        if ( pRequest == NULL || pAdapter == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpCloseCall: Invalid parameter") );   

            status = NDIS_STATUS_TAPI_INVALPARAM;

            break;
        }

        pCall = RetrieveFromHandleTable( pAdapter->TapiProv.hCallTable,
                                         (NDIS_HANDLE) pRequest->hdCall );

        if ( pCall == NULL )
        {
            TRACE( TL_N, TM_Tp, ("TpCloseCall: Invalid handle supplied") ); 
        
            break;
        }

        //
        // Now start closing the call
        //
        NdisAcquireSpinLock( &pCall->lockCall );

        /*
        if ( !fNotifyNDIS )
        {
            //
            // Request is not coming from TAPI directly, so see if we have informed TAPI of
            // a new call, because if we have then we can not close the call now, we should 
            // wait for TAPI to close it.
            //
            if ( pCall->htCall )
            {
                TRACE( TL_N, TM_Tp, ("TpCloseCall: Internal close request for a TAPI informed call, can not close now") );

                NdisReleaseSpinLock( &pCall->lockCall );

                status = NDIS_STATUS_FAILURE;

                break;
            }

        }
        */

        //
        // See if call is already closed or closing
        //
        if ( pCall->ulClFlags & CLBF_CallClosePending ||
             pCall->ulClFlags & CLBF_CallClosed )
        {
            TRACE( TL_N, TM_Tp, ("TpCloseCall: Close request on an already closed call") );
            
            NdisReleaseSpinLock( &pCall->lockCall );

            status = NDIS_STATUS_FAILURE;

            break;
        }

        //
        // Mark call if we need to notify NDIS about the completion of close
        //
        if ( fNotifyNDIS )
            pCall->ulClFlags |= CLBF_NotifyNDIS;

        //
        // Mark call as close pending
        //
        pCall->ulClFlags |= CLBF_CallClosePending;
        
        //
        // Drop the call first
        //
        NdisReleaseSpinLock( &pCall->lockCall );

        //
        // Drop will take care of unbinding and cancelling the timer
        //
        {
            NDIS_TAPI_DROP DummyRequest;

            DummyRequest.hdCall = pRequest->hdCall;
            
            TpDropCall( pAdapter, &DummyRequest, 0 );
        }

        status = NDIS_STATUS_PENDING;

    } while ( FALSE );

    if ( status == NDIS_STATUS_SUCCESS ||
         status == NDIS_STATUS_PENDING )
    {
        LINE* pLine = pCall->pLine;
        
        //
        // Remove call from line's active call list, and decrement 
        // active call counter
        //
        NdisAcquireSpinLock( &pLine->lockLine );

        RemoveHeadList( pCall->linkCalls.Blink );
    
        pLine->nActiveCalls--;
        
        NdisReleaseSpinLock( &pLine->lockLine );

        //
        // We should now remove the call from the Tapi provider's call table,
        // and invalidate its' handle
        //
        NdisAcquireSpinLock( &pAdapter->lockAdapter );
    
        RemoveFromHandleTable( pAdapter->TapiProv.hCallTable,
                               (NDIS_HANDLE) pCall->hdCall );
    
        NdisReleaseSpinLock( &pAdapter->lockAdapter );

        //
        // Remove the reference for close call
        //
        DereferenceCall( pCall );
    }

    TRACE( TL_N, TM_Tp, ("-TpCloseCall=$%x",status) );
    
    return status;
}

NDIS_STATUS
TpDropCall(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_DROP pRequest,
    IN ULONG ulLineDisconnectMode
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will be called from a couple of places:
        1. If miniport receives an OID_TAPI_DROP_CALL request from TAPI.

        2. When NIC for the call is unbound, it will call TpUnbindCall(),
           and if the call is not dropped yet, it will call TpDropCall().

        3. When the call is in connect pending stage but the call needs
           to be dropped.

        4. When session is up and call receives a PADT packet from the peer.

    As this is a synchronous call, we do not need an fNotifyNDIS flag.
    
    CAUTION: All locks must be released before calling this function.
    
Parameters:

    pAdapter _ A pointer to our adaptert information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_DROP
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_CALL   hdCall;
        IN  ULONG       ulUserUserInfoSize;
        IN  UCHAR       UserUserInfo[1];

    } NDIS_TAPI_DROP, *PNDIS_TAPI_DROP; 

    ulLineDisconnectMode _ Reason for dropping the call. This is reported 
                           back to TAPI in the appropriate state change
                           notification.

Return Values:

    NDIS_STATUS_SUCCESS: Call is succesfully dropped.
    
---------------------------------------------------------------------------*/   
{
    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
    CALL* pCall = NULL;
    BOOLEAN fSendPADT = FALSE;
    BINDING* pBinding = NULL;
    PPPOE_PACKET* pPacket = NULL;
    BOOLEAN fTapiNotifiedOfNewCall = FALSE;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpDropCall") );


    do
    {
        if ( pRequest == NULL || pAdapter == NULL )
        {
            TRACE( TL_A, TM_Tp, ("TpDropCall: Invalid parameter") );    


            status = NDIS_STATUS_TAPI_INVALPARAM;

            break;
        }

        //
        // Retrieve the pointer to call from the handle table
        //
        pCall = RetrieveFromHandleTable( pAdapter->TapiProv.hCallTable, 
                                         (NDIS_HANDLE) pRequest->hdCall );

        if ( pCall == NULL )
        {
            TRACE( TL_N, TM_Tp, ("TpDropCall: Invalid handle supplied") );  

            break;
        }

        NdisAcquireSpinLock( &pCall->lockCall );

        //
        // Make sure call is not dropped or closed previously
        //
        if ( pCall->ulClFlags & CLBF_CallDropped || pCall->ulClFlags & CLBF_CallClosed)
        {
            //
            // Call already dropped, quit
            //
            NdisReleaseSpinLock( &pCall->lockCall );

            TRACE( TL_N, TM_Tp, ("TpDropCall: Call already dropped or closed") );   

            break;
        }

        // 
        // Then we must be in open state either connected, or connect pending
        //
        ASSERT( pCall->ulClFlags & CLBF_CallOpen );

        pCall->ulClFlags &= ~CLBF_CallOpen;
        pCall->ulClFlags &= ~CLBF_CallConnectPending;
        pCall->ulClFlags |= CLBF_CallDropped;

        if ( pCall->htCall )
        {
            fTapiNotifiedOfNewCall = TRUE;
        }
        
        //
        // Save the binding pointer as we will detach call from it soon
        //
        pBinding = pCall->pBinding;

        if ( pCall->usSessionId && pBinding )
        {
            //
            // Prepare a PADT packet to send if:
            // - A session id is assigned to the call (which is different than fSessionUp)
            //   A session id is assigned to the call when the peer is informed about the session,
            //   however fSessionUp will be TRUE when NDISWAN is notified about the call
            //
            // - A binding exists to send the PADT packet
            //

            status = PacketInitializePADTToSend( &pPacket,
                                                 pCall->SrcAddr,
                                                 pCall->DestAddr,
                                                 pCall->usSessionId );

            if ( status == NDIS_STATUS_SUCCESS )
            {
                //
                // The following references are mandatory as in case PrSend() returns status pending,
                // they will be removed by PrSendComplete()
                //
                ReferencePacket( pPacket );
    
                ReferenceBinding( pBinding, TRUE );
    
                fSendPADT = TRUE;
            }

            //
            // Ignore the current status as this does not affect 
            // the status of the Drop operation.
            //
            status = NDIS_STATUS_SUCCESS;
        }

        //
        // Release the lock to take care of rest of the operation
        //
        NdisReleaseSpinLock( &pCall->lockCall );

        //
        // Cancels the timer if it is set, otherwise it will not have any effect.
        //
        TimerQCancelItem( &gl_TimerQ, &pCall->timerTimeout );

        //
        // Send PADT here if we need to
        //
        if ( fSendPADT )
        {
            NDIS_STATUS SendStatus;

            SendStatus = PrSend( pBinding, pPacket );

            PacketFree( pPacket );
        }

        //
        // This will unbind us from the underlying NIC context if we are bound
        //
        if ( pBinding )
        {
            PrRemoveCallFromBinding( pBinding, pCall );
        }

        //
        // If TAPI was already notified of the call, move it to disconnected state
        //
        if ( fTapiNotifiedOfNewCall )
        {
            TpCallStateChangeHandler( pCall, 
                                      LINECALLSTATE_DISCONNECTED, 
                                      ulLineDisconnectMode );

        }
        
        //
        // Remove the reference added in TpMakeCall() that corresponds 
        // to the drop of the call.
        //
        DereferenceCall( pCall );

    } while ( FALSE );

    TRACE( TL_N, TM_Tp, ("-TpDropCall=$%x",status) );

    return status;
}


VOID 
TpCloseCallComplete(
    IN CALL* pCall
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will be called only from DereferenceCall().
    It will only be called if ref count of the call drops to 0.

    When this function is called, it will deallocate the call context,
    and dereference the line context.
    
    If call contexts CLBF_NotifyNDIS flag is set, then it will call 
    NdisMQueryInformationComplete().

Parameters:

    pCall    _ A pointer to the call context that will be freed.
    
Return Values:

    None
                             
---------------------------------------------------------------------------*/
    
{
    LINE* pLine = NULL;

    ASSERT( VALIDATE_CALL( pCall ) );

    TRACE( TL_N, TM_Tp, ("+TpCloseCallComplete") );

    //
    // No need to use spin locks here, as our ref count has dropped to 0, and
    // we should not be getting anymore requests on this call
    //
    pLine = pCall->pLine;

    //
    // CAUTION: Give an NDIS_MAC_LINE_DOWN indication here.
    //          It would be better to give this at drop time, but in that case
    //          there is a small timing window where NdisLinkHandle will be invalid 
    //          and although NDISWAN protects itself against invalid handles, it might
    //          assert in checked builds, so instead I will do it here.
    //         
    //          If problems occur with this approach, then I will do it at drop time.
    //
    if ( pCall->stateCall == CL_stateSessionUp )
    {
        NDIS_MAC_LINE_DOWN LineDownInfo;

        //
        // Fill-in the line down structure
        //
        LineDownInfo.NdisLinkContext = pCall->NdisLinkContext;

        //
        // Reflect the change onto the call
        //
        pCall->stateCall = CL_stateDisconnected;

        pCall->NdisLinkContext = 0;

        TRACE( TL_N, TM_Tp, ("TpCloseCallComplete: Indicate NDIS_STATUS_WAN_LINE_DOWN") );

        NdisMIndicateStatus( pCall->pLine->pAdapter->MiniportAdapterHandle,
                             NDIS_STATUS_WAN_LINE_DOWN,
                             &LineDownInfo,
                             sizeof( NDIS_MAC_LINE_DOWN ) );    
         
    }

    if ( pCall->ulClFlags & CLBF_NotifyNDIS )
    {

        TRACE( TL_N, TM_Tp, ("TpCloseCallComplete: Notifying NDIS") );  

        //
        // The close call was a result of OID_TAPI_CLOSE_CALL request so complete the request.
        // There is a small timing window where this call may happen before MpSetInformation()
        // returns NDIS_STATUS_PENDING, but ArvindM says this is not a problem.
        //
        NdisMSetInformationComplete( pLine->pAdapter->MiniportAdapterHandle, NDIS_STATUS_SUCCESS );
    }

    //
    // Clean up the call context
    //
    TpCallCleanup( pCall );

    //
    // Remove the reference on the owning line
    //
    DereferenceLine( pLine );

    TRACE( TL_N, TM_Tp, ("-TpCloseCallComplete") );

}


VOID 
TpCloseLineComplete(
    IN LINE* pLine
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will be called to indicate that a line has been closed, and 
    the line context can be freed.

    It will only be called from DereferenceLine() if ref count on the line context
    drops to 0.

    It will also remove the reference on the owning tapi provider context.
    
Parameters:

    pLine    _ A pointer to our line information structure that is closed 
               and ready to be deallocated.
               
Return Values:

    None
---------------------------------------------------------------------------*/
{
    IN ADAPTER* pAdapter = NULL;

    ASSERT( VALIDATE_LINE( pLine ) );

    TRACE( TL_N, TM_Tp, ("+TpCloseLineComplete") );

    pAdapter = pLine->pAdapter;

    //
    // Decrement the tapi provider's active line counter
    //
    NdisAcquireSpinLock( &pAdapter->lockAdapter );

    pAdapter->TapiProv.nActiveLines--;
    
    NdisReleaseSpinLock( &pAdapter->lockAdapter );

    //
    // Notify NDIS if necesarry
    //
    if ( pLine->ulLnFlags & LNBF_NotifyNDIS )
    {

        TRACE( TL_N, TM_Tp, ("TpCloseLineComplete: Notifying NDIS") );  

        //
        // Line was closed as a result of OID_TAPI_CLOSE request,
        // so indicate the completion.
        //
        NdisMSetInformationComplete( pAdapter->MiniportAdapterHandle, NDIS_STATUS_SUCCESS );
    }

    //
    // Clean up line context
    //
    TpLineCleanup( pLine );

    //
    // Remove the reference on the owning tapi provider
    //
    DereferenceTapiProv( pAdapter );

    TRACE( TL_N, TM_Tp, ("-TpCloseLineComplete") );
}

VOID 
TpProviderShutdownComplete(
    IN ADAPTER* pAdapter
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will only be called from DereferenceTapiProv() if ref count
    on the tapi provider object drops to 0.

    It will do the necesarry clean up on the tapi provider context, and dereference
    the owning adapter context.
    
Parameters:

    pAdapter _ A pointer to our adapter information structure.

Return Values:

    None
---------------------------------------------------------------------------*/
{
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpProviderShutdownComplete") );

    //
    // See if we need to notify NDIS about the completion of shut down.
    //
    if ( pAdapter->TapiProv.ulTpFlags & TPBF_NotifyNDIS )
    {

        TRACE( TL_N, TM_Tp, ("TpProviderShutdownComplete: Notifying NDIS") );   

        //
        // Tapi was shut down as a result of OID_TAPI_PROVIDER_SHUTDOWN request,
        // so indicate the completion.
        //
        NdisMSetInformationComplete( pAdapter->MiniportAdapterHandle, NDIS_STATUS_SUCCESS );
    }

    //
    // Clean up tapi provider
    //
    TpProviderCleanup( pAdapter );

    //
    // Remove the reference on the owning adapter context
    //
    DereferenceAdapter( pAdapter );

    TRACE( TL_N, TM_Tp, ("-TpProviderShutdownComplete") );

}

VOID 
TpProviderCleanup(
    IN ADAPTER* pAdapter
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will do the necesarry clean up on the tapi provider deallocating
    all of its resources.
    
Parameters:

    pAdapter _ A pointer to our adapter information structure.

Return Values:

    None
---------------------------------------------------------------------------*/   
{
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpProviderCleanup") );

    if ( pAdapter )
    {
        NdisAcquireSpinLock( &pAdapter->lockAdapter );
    
        if ( pAdapter->TapiProv.LineTable )
        {
            NdisFreeMemory( pAdapter->TapiProv.LineTable,
                            sizeof( LINE* ) * pAdapter->nMaxLines,
                            0 );
    
            pAdapter->TapiProv.LineTable = NULL;
        }
    
        if ( pAdapter->TapiProv.hCallTable )
        {
            FreeHandleTable( pAdapter->TapiProv.hCallTable );
            
            pAdapter->TapiProv.hCallTable = NULL;
        }
    
        NdisZeroMemory( &pAdapter->TapiProv, sizeof( pAdapter->TapiProv ) );
    
        NdisReleaseSpinLock( &pAdapter->lockAdapter );
    }

    TRACE( TL_N, TM_Tp, ("-TpProviderCleanup") );
}

VOID 
TpLineCleanup(
    IN LINE* pLine
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will do the necesarry clean up on the line context deallocating
    all of its resources.
    
Parameters:

    pLine _ A pointer to our line information structure.

Return Values:

    None
---------------------------------------------------------------------------*/   
{
    ASSERT( VALIDATE_LINE( pLine ) );

    TRACE( TL_N, TM_Tp, ("+TpLineCleanup") );

    NdisFreeSpinLock( &pLine->lockLine );

    FREE_LINE( pLine );

    TRACE( TL_N, TM_Tp, ("-TpLineCleanup") );
}

VOID 
TpCallCleanup(
    IN CALL* pCall 
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will do the necesarry clean up on the call context deallocating
    all of its resources.
    
Parameters:

    pCall _ A pointer to our call information structure.

Return Values:

    None
---------------------------------------------------------------------------*/       
{
    PPPOE_PACKET* pPacket = NULL;
    LIST_ENTRY* pLink = NULL;
    
    ASSERT( VALIDATE_CALL( pCall ) );

    TRACE( TL_N, TM_Tp, ("+TpCallCleanup") );

    NdisFreeSpinLock( &pCall->lockCall );

    if ( pCall->pSendPacket )
        PacketFree( pCall->pSendPacket );

    while ( pCall->nReceivedPackets > 0 )
    {
        pLink = RemoveHeadList( &pCall->linkReceivedPackets );

        pCall->nReceivedPackets--;

        pPacket = (PPPOE_PACKET*) CONTAINING_RECORD( pLink, PPPOE_PACKET, linkPackets );

        DereferencePacket( pPacket );
    }
    
    FREE_CALL( pCall );

    TRACE( TL_N, TM_Tp, ("-TpCallCleanup") );
}


NDIS_STATUS
TpSetDefaultMediaDetection(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request informs the miniport of the new set of media modes to detect 
    for the indicated line (replacing any previous set).

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_LINE   hdLine;
        IN  ULONG       ulMediaModes;

    } NDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION, *
PNDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_TAPI_INVALLINEHANDLE

---------------------------------------------------------------------------*/
{
    LINE* pLine = NULL;
    NDIS_STATUS status = NDIS_STATUS_SUCCESS;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpSetDefaultMediaDetection") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpSetDefaultMediaDetection: Invalid parameter") );    

        TRACE( TL_N, TM_Tp, ("-TpSetDefaultMediaDetection=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    //
    // Retrieve the pointer to line context
    //
    pLine = TpGetLinePtrFromHdLine( pAdapter, pRequest->hdLine );

    if ( pLine == NULL )
    {
        TRACE( TL_N, TM_Tp, ("-TpSetDefaultMediaDetection=$%x",NDIS_STATUS_TAPI_INVALLINEHANDLE) );

        return NDIS_STATUS_TAPI_INVALLINEHANDLE;
    }

    //
    // We only accept this request if we are not in client mode, and digital media
    // is one of the modes proposed
    //
    if ( ( pRequest->ulMediaModes & LINEMEDIAMODE_DIGITALDATA ) && !pAdapter->fClientRole )
    {
        pLine->ulLnFlags |= LNBF_AcceptIncomingCalls;
    }
    else
    {
        pLine->ulLnFlags &= ~LNBF_AcceptIncomingCalls;
    }

    {
        //
        // Schedule a work item to reenumerate bindings
        //
        WORKITEM* pWorkItem = NULL;
        PVOID Args[4];
             
        Args[0] = (PVOID) BN_SetFiltersForMediaDetection;           // Is a set filters request
        Args[1] = (PVOID) pLine;
        Args[2] = (PVOID) pRequest;

        //
        // Allocate work item for reenumerating bindings
        //
        pWorkItem = AllocWorkItem( &gl_llistWorkItems,
                                   ExecBindingWorkItem,
                                   NULL,
                                   Args,
                                   BWT_workPrStartBinds );

        if ( pWorkItem ) 
        {
            //
            // Schedule work item.
            //
            // Note that we do not need to referencing becaue we are not completing
            // the query information request at this point, so nothing can go wrong
            // untill it is completed, and it will be done when the work item is executed.
            //
            ScheduleWorkItem( pWorkItem );
      
            //
            // In this case this request will be completed later
            //
            status = NDIS_STATUS_PENDING;
        }
    }

    TRACE( TL_N, TM_Tp, ("-TpSetDefaultMediaDetection=$%x",status) );

    return status;
}

VOID
TpSetDefaultMediaDetectionComplete(
   IN LINE* pLine,
   IN PNDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION pRequest   
   )
{
   TRACE( TL_N, TM_Tp, ("+TpSetDefaultMediaDetectionComplete") );

   NdisMQueryInformationComplete( pLine->pAdapter->MiniportAdapterHandle,
                                  NDIS_STATUS_SUCCESS );
                                  
   TRACE( TL_N, TM_Tp, ("-TpSetDefaultMediaDetectionComplete=$%x", NDIS_STATUS_SUCCESS) );
}


#define TAPI_EXT_VERSION                0x00010000

NDIS_STATUS
TpNegotiateExtVersion(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_NEGOTIATE_EXT_VERSION pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request returns the highest extension version number the service
    provider is willing to operate under for this device given the range of
    possible extension versions.

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_NEGOTIATE_EXT_VERSION
    {
        IN  ULONG       ulRequestID;
        IN  ULONG       ulDeviceID;
        IN  ULONG       ulLowVersion;
        IN  ULONG       ulHighVersion;
        OUT ULONG       ulExtVersion;
    } NDIS_TAPI_NEGOTIATE_EXT_VERSION, *PNDIS_TAPI_NEGOTIATE_EXT_VERSION;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION

---------------------------------------------------------------------------*/
{
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpNegotiateExtVersion") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpNegotiateExtVersion: Invalid parameter") ); 

        TRACE( TL_N, TM_Tp, ("-TpNegotiateExtVersion=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    //
    // Make sure the miniport's version number is within the allowable
    // range requested by the caller.  
    //
    // We ignore the ulDeviceID because the version information applies 
    // to all devices on this adapter.
    //
    if ( TAPI_EXT_VERSION < pRequest->ulLowVersion ||
         TAPI_EXT_VERSION > pRequest->ulHighVersion )
    {
        TRACE( TL_N, TM_Tp, ("-TpNegotiateExtVersion=$%x",NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION) );
    
        return NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION;
    }

    //
    // Looks like we're compatible, so tell the caller what we expect.
    //
    pRequest->ulExtVersion = TAPI_EXT_VERSION;

    TRACE( TL_N, TM_Tp, ("-TpNegotiateExtVersion=$%x",NDIS_STATUS_SUCCESS) );

    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
TpGetExtensionId(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_GET_EXTENSION_ID pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request returns the extension ID that the miniport supports for the
    indicated line device.

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_GET_EXTENSION_ID
    {
        IN  ULONG       ulRequestID;
        IN  ULONG       ulDeviceID;
        OUT LINE_EXTENSION_ID   LineExtensionID;

    } NDIS_TAPI_GET_EXTENSION_ID, *PNDIS_TAPI_GET_EXTENSION_ID;

    typedef struct _LINE_EXTENSION_ID
    {
        ULONG   ulExtensionID0;
        ULONG   ulExtensionID1;
        ULONG   ulExtensionID2;
        ULONG   ulExtensionID3;

    } LINE_EXTENSION_ID, *PLINE_EXTENSION_ID;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_TAPI_NODRIVER

---------------------------------------------------------------------------*/
{
    HDRV_LINE hdLine = INVALID_LINE_HANDLE;
    
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpGetExtensionId") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpGetExtensionId: Invalid parameter") );  

        TRACE( TL_N, TM_Tp, ("-TpGetExtensionId=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    //
    // Retrieve the handle to line context
    //
    hdLine = TpGetHdLineFromDeviceId( pAdapter, pRequest->ulDeviceID );
    
    if ( hdLine == INVALID_LINE_HANDLE )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetExtensionId=$%x",NDIS_STATUS_TAPI_NODRIVER) );
        
        return NDIS_STATUS_TAPI_NODRIVER;
    }
    
    //
    // This driver does not support any extensions, so we return zeros.
    //
    pRequest->LineExtensionID.ulExtensionID0 = 0;
    pRequest->LineExtensionID.ulExtensionID1 = 0;
    pRequest->LineExtensionID.ulExtensionID2 = 0;
    pRequest->LineExtensionID.ulExtensionID3 = 0;

    TRACE( TL_N, TM_Tp, ("-TpGetExtensionId=$%x",NDIS_STATUS_SUCCESS) );

    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
TpGetAddressStatus(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_GET_ADDRESS_STATUS pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request queries the specified address for its current status.

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_GET_ADDRESS_STATUS
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_LINE   hdLine;
        IN  ULONG       ulAddressID;
        OUT LINE_ADDRESS_STATUS LineAddressStatus;

    } NDIS_TAPI_GET_ADDRESS_STATUS, *PNDIS_TAPI_GET_ADDRESS_STATUS;

    typedef struct _LINE_ADDRESS_STATUS
    {
        ULONG   ulTotalSize;
        ULONG   ulNeededSize;
        ULONG   ulUsedSize;

        ULONG   ulNumInUse;
        ULONG   ulNumActiveCalls;
        ULONG   ulNumOnHoldCalls;
        ULONG   ulNumOnHoldPendCalls;
        ULONG   ulAddressFeatures;

        ULONG   ulNumRingsNoAnswer;
        ULONG   ulForwardNumEntries;
        ULONG   ulForwardSize;
        ULONG   ulForwardOffset;

        ULONG   ulTerminalModesSize;
        ULONG   ulTerminalModesOffset;

        ULONG   ulDevSpecificSize;
        ULONG   ulDevSpecificOffset;

    } LINE_ADDRESS_STATUS, *PLINE_ADDRESS_STATUS;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_FAILURE
    NDIS_STATUS_TAPI_INVALLINEHANDLE
    NDIS_STATUS_TAPI_INVALADDRESSID

---------------------------------------------------------------------------*/
{
    LINE* pLine = NULL;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpGetAddressStatus") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpGetAddressStatus: Invalid parameter") );    

        TRACE( TL_N, TM_Tp, ("-TpGetAddressStatus=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }
    
    //
    // Retrieve the pointer to line context
    //
    pLine = TpGetLinePtrFromHdLine( pAdapter, pRequest->hdLine );

    if ( pLine == NULL )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetAddressStatus=$%x",NDIS_STATUS_TAPI_INVALLINEHANDLE) );
    
        return NDIS_STATUS_TAPI_INVALLINEHANDLE;
    }

    pRequest->LineAddressStatus.ulNeededSize = sizeof( LINE_ADDRESS_STATUS );

    if ( pRequest->LineAddressStatus.ulTotalSize < pRequest->LineAddressStatus.ulNeededSize )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetAddressStatus=$%x",NDIS_STATUS_INVALID_LENGTH) );
    
        return NDIS_STATUS_INVALID_LENGTH;
    }

    pRequest->LineAddressStatus.ulUsedSize = pRequest->LineAddressStatus.ulNeededSize;
    
    //
    // Make sure the address is within range - we only support one per line.
    //
    if ( pRequest->ulAddressID > 1 )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetAddressStatus=$%x",NDIS_STATUS_TAPI_INVALADDRESSID) );

        return NDIS_STATUS_TAPI_INVALADDRESSID;
    }

    //
    // Return the current status information for the address
    //
    pRequest->LineAddressStatus.ulNumInUse = ( pLine->nActiveCalls > 0 ) ? 1 : 0;
            
    pRequest->LineAddressStatus.ulNumActiveCalls = pLine->nActiveCalls;
            
    pRequest->LineAddressStatus.ulAddressFeatures = ( pLine->nActiveCalls < pLine->nMaxCalls ) ? 
                                                    LINEADDRFEATURE_MAKECALL : 
                                                    0;
                
    pRequest->LineAddressStatus.ulNumRingsNoAnswer = 999;

    TRACE( TL_N, TM_Tp, ("-TpGetAddressStatus=$%x",NDIS_STATUS_SUCCESS) );

    return NDIS_STATUS_SUCCESS;
}

#define TAPI_DEVICECLASS_NAME        "tapi/line"
#define TAPI_DEVICECLASS_ID          1
#define NDIS_DEVICECLASS_NAME        "ndis"
#define NDIS_DEVICECLASS_ID          2

NDIS_STATUS
TpGetId(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_GET_ID pRequest,
    IN ULONG ulRequestLength
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request returns a device ID for the specified device class
    associated with the selected line, address or call.

    Currently, there are two types of this request that must be supported by WAN 
    NIC drivers:

    1.  IN DeviceClass = "ndis"                 // case insensitive
        IN ulSelect = LINECALLSELECT_CALL
        IN hdCall = ActiveCallHandle
        OUT DeviceID = ConnectionWrapperID 

        DeviceID should be set to the NdisLinkContext handle returned by NDISWAN in 
        the NDIS_MAC_LINE_UP structure for the initial NDIS_STATUS_WAN_LINE_UP 
        indication to establish the link.
    
        The miniport must make the initial line-up indication to establish a link (or 
        open a data channel on a line) before returning from this request in order to 
        supply this DeviceID value. 
    
    2.  IN DeviceClass = "tapi/line"            // case insensitive
        IN ulSelect = LINECALLSELECT_LINE
        IN hdLine = OpenLineHandle
        OUT DeviceID = ulDeviceID 

        DeviceID will be set to the miniport-determined DeviceID associated with the 
        line handle.

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_GET_ID
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_LINE   hdLine;
        IN  ULONG       ulAddressID;
        IN  HDRV_CALL   hdCall;
        IN  ULONG       ulSelect;
        IN  ULONG       ulDeviceClassSize;
        IN  ULONG       ulDeviceClassOffset;
        OUT VAR_STRING  DeviceID;

    } NDIS_TAPI_GET_ID, *PNDIS_TAPI_GET_ID;

    typedef struct _VAR_STRING
    {
        ULONG   ulTotalSize;
        ULONG   ulNeededSize;
        ULONG   ulUsedSize;

        ULONG   ulStringFormat;
        ULONG   ulStringSize;
        ULONG   ulStringOffset;

    } VAR_STRING, *PVAR_STRING;

   ulRequestLength _ Length of the request buffer

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_FAILURE
    NDIS_STATUS_TAPI_INVALDEVICECLASS
    NDIS_STATUS_TAPI_INVALLINEHANDLE
    NDIS_STATUS_TAPI_INVALADDRESSID
    NDIS_STATUS_TAPI_INVALCALLHANDLE
    NDIS_STATUS_TAPI_OPERATIONUNAVAIL

---------------------------------------------------------------------------*/
{
    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
    BOOLEAN fNotifyNDIS = FALSE;
    
    LINE* pLine = NULL;
    CALL* pCall = NULL;

    UINT DeviceClass;

    PUCHAR IDPtr;
    UINT  IDLength;
    ULONG_PTR DeviceID;

    TRACE( TL_N, TM_Tp, ("+TpGetId") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpGetId: Invalid parameter") );   

        TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    if ( pRequest->ulDeviceClassOffset + pRequest->ulDeviceClassSize > ulRequestLength )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    if ( pRequest->ulSelect == LINECALLSELECT_LINE )
    {          

        if ( ( pRequest->ulDeviceClassSize == sizeof(TAPI_DEVICECLASS_NAME) ) &&
              ( _strnicmp(
                         (PCHAR) pRequest + pRequest->ulDeviceClassOffset, 
                         TAPI_DEVICECLASS_NAME, 
                         pRequest->ulDeviceClassSize
                         ) == 0 ) )
        {
            DeviceClass = TAPI_DEVICECLASS_ID;

            //
            // Do the size check up front
            //
            IDLength = sizeof(DeviceID);
            
            pRequest->DeviceID.ulNeededSize = sizeof(VAR_STRING) + IDLength;
            
            if ( pRequest->DeviceID.ulTotalSize < pRequest->DeviceID.ulNeededSize )
            {
                TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_INVALID_LENGTH) );
     
                return NDIS_STATUS_INVALID_LENGTH;
            }
 
            pRequest->DeviceID.ulUsedSize = pRequest->DeviceID.ulNeededSize;

        }
        else    // UNSUPPORTED DEVICE CLASS
        {
            TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALDEVICECLASS) );
        
            return NDIS_STATUS_TAPI_INVALDEVICECLASS;
        }

    }        
    else if ( pRequest->ulSelect == LINECALLSELECT_CALL )
    {

        if ( ( pRequest->ulDeviceClassSize == sizeof(NDIS_DEVICECLASS_NAME) ) &&
              ( _strnicmp(
                   (PCHAR) pRequest + pRequest->ulDeviceClassOffset, 
                   NDIS_DEVICECLASS_NAME, 
                   pRequest->ulDeviceClassSize
                   ) == 0 ) )
        {
            DeviceClass = NDIS_DEVICECLASS_ID;

            //
            // Do the size check up front
            //
            IDLength = sizeof(DeviceID);
            
            pRequest->DeviceID.ulNeededSize = sizeof(VAR_STRING) + IDLength;
            
            if ( pRequest->DeviceID.ulTotalSize < pRequest->DeviceID.ulNeededSize )
            {
                TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_INVALID_LENGTH) );
     
                return NDIS_STATUS_INVALID_LENGTH;
            }
 
            pRequest->DeviceID.ulUsedSize = pRequest->DeviceID.ulNeededSize;

        }        
        else    // UNSUPPORTED DEVICE CLASS
        {
            TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALDEVICECLASS) );
        
            return NDIS_STATUS_TAPI_INVALDEVICECLASS;
        }

    }        

    //
    // Find the link structure associated with the request/deviceclass.
    //
    if ( pRequest->ulSelect == LINECALLSELECT_LINE )
    {
        ASSERT( DeviceClass == TAPI_DEVICECLASS_ID );
        ASSERT( IDLength == sizeof( DeviceID ) );    
        //
        // Retrieve the pointer to line context
        //
        pLine = TpGetLinePtrFromHdLine( pAdapter, pRequest->hdLine );
    
        if ( pLine == NULL )
        {
            TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALLINEHANDLE) );
            
            return NDIS_STATUS_TAPI_INVALLINEHANDLE;
        }

        //
        // TAPI just wants the ulDeviceID for this line.
        //
        DeviceID = (ULONG) pLine->hdLine + pAdapter->TapiProv.ulDeviceIDBase ;
        IDPtr = (PUCHAR) &DeviceID;
        
    }
    else if ( pRequest->ulSelect == LINECALLSELECT_ADDRESS )
    {
    
        //
        // Retrieve the pointer to line context
        //
        pLine = TpGetLinePtrFromHdLine( pAdapter, pRequest->hdLine );
    
        if ( pLine == NULL )
        {
            TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALLINEHANDLE) );
            
            return NDIS_STATUS_TAPI_INVALLINEHANDLE;
        }


        if ( pRequest->ulAddressID > 1 )
        {
            TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALADDRESSID) );

            return NDIS_STATUS_TAPI_INVALADDRESSID;
        }
        
        //
        // Currently, there is no defined return value for this case...
        // This is just a place holder for future extensions.
        //
        TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALDEVICECLASS) );
        
        return NDIS_STATUS_TAPI_INVALDEVICECLASS;
        
    }
    else if ( pRequest->ulSelect == LINECALLSELECT_CALL )
    {
        BOOLEAN fCallReferenced = FALSE;
        
        ASSERT( DeviceClass == NDIS_DEVICECLASS_ID );
        ASSERT( IDLength == sizeof( DeviceID ) );    

        //
        // Retrieve the pointer to call context
        //
        pCall = RetrieveFromHandleTable( pAdapter->TapiProv.hCallTable, 
                                         (NDIS_HANDLE) pRequest->hdCall );
    
        if ( pCall == NULL )
        {
            TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_TAPI_INVALLINEHANDLE) );
            
            return NDIS_STATUS_TAPI_INVALLINEHANDLE;
        }

        //
        // We can only return this if we have a valid NdisLinkContext,
        // and if our session is up, then our link handle must be valid
        //
  
        NdisAcquireSpinLock( &pCall->lockCall );
  
        if ( pCall->ulTapiCallState == LINECALLSTATE_CONNECTED )
        {
            //
            // Give a line-up indication to NDISWAN and obtain its handle
            //
            NDIS_MAC_LINE_UP LineUpInfo;
  
            //
            // Fill-in the line up structure
            //
            NdisZeroMemory( &LineUpInfo, sizeof( LineUpInfo ) );
            
            LineUpInfo.LinkSpeed    = pCall->ulSpeed;
            LineUpInfo.Quality      = NdisWanErrorControl;
            LineUpInfo.SendWindow   = 0;
            
            LineUpInfo.ConnectionWrapperID = (NDIS_HANDLE) pCall->htCall;
            LineUpInfo.NdisLinkHandle      = (NDIS_HANDLE) pCall->hdCall;
            LineUpInfo.NdisLinkContext     = 0;
  
            //
            // Reference the call once and deref it just after indication of status
            // to NDISWAN
            //
            ReferenceCall( pCall, FALSE );
  
            fCallReferenced = TRUE;
  
            NdisReleaseSpinLock( &pCall->lockCall );
  
            TRACE( TL_N, TM_Tp, ("TpGetId: Indicate NDIS_STATUS_WAN_LINE_UP") );
  
            NdisMIndicateStatus( pCall->pLine->pAdapter->MiniportAdapterHandle,
                                 NDIS_STATUS_WAN_LINE_UP,
                                 &LineUpInfo,
                                 sizeof( NDIS_MAC_LINE_UP ) );  
  
            NdisAcquireSpinLock( &pCall->lockCall );                                     
  
            //
            // Set state to indicate that session is established
            //
            pCall->stateCall = CL_stateSessionUp;
  
            //
            // Set link context obtained from NDISWAN on the call context
            //
            pCall->NdisLinkContext = LineUpInfo.NdisLinkContext;
            
            DeviceID = (ULONG_PTR) pCall->NdisLinkContext;
            IDPtr = (PUCHAR) &DeviceID;
  
            //
            // Since the session is up, schedule the MpIndicateReceivedPackets() handler 
            //
            MpScheduleIndicateReceivedPacketsHandler( pCall );
  
            status = NDIS_STATUS_SUCCESS;
        }
        else
        {
            status = NDIS_STATUS_TAPI_OPERATIONUNAVAIL;
        }
  
        NdisReleaseSpinLock( &pCall->lockCall );
  
        if ( fCallReferenced )
        {
           DereferenceCall( pCall );
        }
            
    }
    else // UNSUPPORTED SELECT REQUEST
    {
        TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",NDIS_STATUS_FAILURE) );         
        
        return NDIS_STATUS_FAILURE;
    }    

    if ( status == NDIS_STATUS_SUCCESS )
    {
        //
        // Now we need to place the device ID.
        //
        pRequest->DeviceID.ulStringFormat = STRINGFORMAT_BINARY;
        pRequest->DeviceID.ulStringSize   = IDLength;
        pRequest->DeviceID.ulStringOffset = sizeof(VAR_STRING);
  
        NdisMoveMemory(
                (PCHAR) &pRequest->DeviceID + sizeof(VAR_STRING),
                IDPtr,
                IDLength
                );
    }

    if ( fNotifyNDIS )
    {
        TRACE( TL_N, TM_Tp, ("TpGetId:Completing delayed request") );           

        NdisMQueryInformationComplete( pCall->pLine->pAdapter->MiniportAdapterHandle, status );

    }
    
    TRACE( TL_N, TM_Tp, ("-TpGetId=$%x",status) );          
    
    return status;

}

#define TAPI_PROVIDER_STRING        "VPN\0RASPPPOE"
#define TAPI_LINE_NAME              "RAS PPPoE Line"
#define TAPI_LINE_NUM               "0000"

NDIS_STATUS
TpGetDevCaps(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_GET_DEV_CAPS pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request queries a specified line device to determine its telephony
    capabilities. The returned information is valid for all addresses on the
    line device.

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_GET_DEV_CAPS
    {
        IN  ULONG       ulRequestID;
        IN  ULONG       ulDeviceID;
        IN  ULONG       ulExtVersion;
        OUT LINE_DEV_CAPS   LineDevCaps;

    } NDIS_TAPI_GET_DEV_CAPS, *PNDIS_TAPI_GET_DEV_CAPS;

    typedef struct _LINE_DEV_CAPS
    {
        ULONG   ulTotalSize;
        ULONG   ulNeededSize;
        ULONG   ulUsedSize;

        ULONG   ulProviderInfoSize;
        ULONG   ulProviderInfoOffset;

        ULONG   ulSwitchInfoSize;
        ULONG   ulSwitchInfoOffset;

        ULONG   ulPermanentLineID;
        ULONG   ulLineNameSize;
        ULONG   ulLineNameOffset;
        ULONG   ulStringFormat;

        ULONG   ulAddressModes;
        ULONG   ulNumAddresses;
        ULONG   ulBearerModes;
        ULONG   ulMaxRate;
        ULONG   ulMediaModes;

        ULONG   ulGenerateToneModes;
        ULONG   ulGenerateToneMaxNumFreq;
        ULONG   ulGenerateDigitModes;
        ULONG   ulMonitorToneMaxNumFreq;
        ULONG   ulMonitorToneMaxNumEntries;
        ULONG   ulMonitorDigitModes;
        ULONG   ulGatherDigitsMinTimeout;
        ULONG   ulGatherDigitsMaxTimeout;

        ULONG   ulMedCtlDigitMaxListSize;
        ULONG   ulMedCtlMediaMaxListSize;
        ULONG   ulMedCtlToneMaxListSize;
        ULONG   ulMedCtlCallStateMaxListSize;

        ULONG   ulDevCapFlags;
        ULONG   ulMaxNumActiveCalls;
        ULONG   ulAnswerMode;
        ULONG   ulRingModes;
        ULONG   ulLineStates;

        ULONG   ulUUIAcceptSize;
        ULONG   ulUUIAnswerSize;
        ULONG   ulUUIMakeCallSize;
        ULONG   ulUUIDropSize;
        ULONG   ulUUISendUserUserInfoSize;
        ULONG   ulUUICallInfoSize;

        LINE_DIAL_PARAMS    MinDialParams;
        LINE_DIAL_PARAMS    MaxDialParams;
        LINE_DIAL_PARAMS    DefaultDialParams;

        ULONG   ulNumTerminals;
        ULONG   ulTerminalCapsSize;
        ULONG   ulTerminalCapsOffset;
        ULONG   ulTerminalTextEntrySize;
        ULONG   ulTerminalTextSize;
        ULONG   ulTerminalTextOffset;

        ULONG   ulDevSpecificSize;
        ULONG   ulDevSpecificOffset;

    } LINE_DEV_CAPS, *PLINE_DEV_CAPS;

    typedef struct _LINE_DIAL_PARAMS
    {
        ULONG   ulDialPause;
        ULONG   ulDialSpeed;
        ULONG   ulDigitDuration;
        ULONG   ulWaitForDialtone;

    } LINE_DIAL_PARAMS, *PLINE_DIAL_PARAMS;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_TAPI_NODRIVER

---------------------------------------------------------------------------*/
{
    HDRV_LINE hdLine = INVALID_LINE_HANDLE;
    CHAR szTapiLineNum[] = TAPI_LINE_NUM;
    CHAR *pBuf = NULL;
    ULONG ulDeviceId;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpGetDevCaps") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpGetDevCaps: Invalid parameter") );  

        TRACE( TL_N, TM_Tp, ("-TpGetDevCaps=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }
    
    //
    // Retrieve the handle to line context
    //
    hdLine = TpGetHdLineFromDeviceId( pAdapter, pRequest->ulDeviceID );
    
    if ( hdLine == INVALID_LINE_HANDLE )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetDevCaps=$%x",NDIS_STATUS_TAPI_NODRIVER) );
        
        return NDIS_STATUS_TAPI_NODRIVER;
    }

    pRequest->LineDevCaps.ulNeededSize   = sizeof( LINE_DEV_CAPS ) + 
                                           sizeof( TAPI_PROVIDER_STRING ) +
                                           ( sizeof( TAPI_LINE_NAME ) - 1 ) +
                                           sizeof( TAPI_LINE_NUM );

    if ( pRequest->LineDevCaps.ulTotalSize < pRequest->LineDevCaps.ulNeededSize )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetDevCaps=$%x",NDIS_STATUS_INVALID_LENGTH) );
        
        return NDIS_STATUS_INVALID_LENGTH;
    }

    pRequest->LineDevCaps.ulUsedSize = pRequest->LineDevCaps.ulNeededSize;
    
    pRequest->LineDevCaps.ulAddressModes = LINEADDRESSMODE_ADDRESSID |
                                           LINEADDRESSMODE_DIALABLEADDR;

    pRequest->LineDevCaps.ulNumAddresses = 1;

    pRequest->LineDevCaps.ulBearerModes  = LINEBEARERMODE_DATA;

    pRequest->LineDevCaps.ulDevCapFlags  = LINEDEVCAPFLAGS_CLOSEDROP;

    pRequest->LineDevCaps.ulMaxNumActiveCalls = pAdapter->nCallsPerLine;

    pRequest->LineDevCaps.ulAnswerMode   = LINEANSWERMODE_DROP;

    pRequest->LineDevCaps.ulRingModes    = 1;

    pRequest->LineDevCaps.ulPermanentLineID = pRequest->ulDeviceID;

    pRequest->LineDevCaps.ulMaxRate      = 0;

    pRequest->LineDevCaps.ulMediaModes   = LINEMEDIAMODE_DIGITALDATA;

    //
    // Insert the provider string and enumerated line name into line dev caps
    //
    pRequest->LineDevCaps.ulStringFormat = STRINGFORMAT_ASCII;

    {
        INT i;
        
        //
        // Tack on the ProviderString to the end of the LineDevCaps structure
        //
        pRequest->LineDevCaps.ulProviderInfoSize = sizeof( TAPI_PROVIDER_STRING );
    
        pRequest->LineDevCaps.ulProviderInfoOffset = sizeof( pRequest->LineDevCaps );
    
        pBuf = ( (PUCHAR) &pRequest->LineDevCaps ) + pRequest->LineDevCaps.ulProviderInfoOffset;
        
        NdisMoveMemory( pBuf , TAPI_PROVIDER_STRING, sizeof( TAPI_PROVIDER_STRING ) );
    
        //
        // Tack on the LineName after the ProviderString
        //
        pRequest->LineDevCaps.ulLineNameSize = ( sizeof( TAPI_LINE_NAME ) - 1 ) + sizeof( TAPI_LINE_NUM );
    
        pRequest->LineDevCaps.ulLineNameOffset = pRequest->LineDevCaps.ulProviderInfoOffset +
                                                 pRequest->LineDevCaps.ulProviderInfoSize;
                                                 
        pBuf = ( (PUCHAR) &pRequest->LineDevCaps ) + pRequest->LineDevCaps.ulLineNameOffset;
    
        NdisMoveMemory( pBuf , TAPI_LINE_NAME, sizeof( TAPI_LINE_NAME ) );
    
        //
        // Tack on the line enumeration index at the end of the LineName
        //
        ulDeviceId = (ULONG) hdLine;
        
        //
        // Subtract 2: 1 for '\0' and 1 to adjust for array indexing
        //
        i = ( sizeof( TAPI_LINE_NUM ) / sizeof( CHAR ) ) - 2;
    
        while ( i >= 0 && ( ulDeviceId > 0 ) )
        {
                szTapiLineNum[i] = (UCHAR)( ( ulDeviceId % 10 ) + '0' );
                ulDeviceId /= 10;
                i--;
        }
        
        pBuf += ( sizeof( TAPI_LINE_NAME ) - 1 );
    
        NdisMoveMemory( pBuf, szTapiLineNum, sizeof( TAPI_LINE_NUM ) );
    }
    
    TRACE( TL_N, TM_Tp, ("-TpGetDevCaps=$%x",NDIS_STATUS_SUCCESS) );

    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
TpGetCallStatus(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_GET_CALL_STATUS pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request returns detailed information about the specified call.

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_GET_CALL_STATUS
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_CALL   hdCall;
        OUT LINE_CALL_STATUS    LineCallStatus;

    } NDIS_TAPI_GET_CALL_STATUS, *PNDIS_TAPI_GET_CALL_STATUS;

    typedef struct _LINE_CALL_STATUS
    {
        ULONG   ulTotalSize;
        ULONG   ulNeededSize;
        ULONG   ulUsedSize;

        ULONG   ulCallState;
        ULONG   ulCallStateMode;
        ULONG   ulCallPrivilege;
        ULONG   ulCallFeatures;

        ULONG   ulDevSpecificSize;
        ULONG   ulDevSpecificOffset;

    } LINE_CALL_STATUS, *PLINE_CALL_STATUS;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_FAILURE
    NDIS_STATUS_TAPI_INVALCALLHANDLE

---------------------------------------------------------------------------*/
{
    CALL* pCall = NULL;
    
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpGetCallStatus") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpGetCallStatus: Invalid parameter") );   

        TRACE( TL_N, TM_Tp, ("-TpGetCallStatus=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    pCall = RetrieveFromHandleTable( pAdapter->TapiProv.hCallTable, 
                                     (NDIS_HANDLE) pRequest->hdCall );

    if ( pCall == NULL )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetCallStatus=$%x",NDIS_STATUS_TAPI_INVALCALLHANDLE) );
    
        return NDIS_STATUS_TAPI_INVALCALLHANDLE;
    }

    pRequest->LineCallStatus.ulNeededSize = sizeof( LINE_CALL_STATUS );

   if ( pRequest->LineCallStatus.ulTotalSize < pRequest->LineCallStatus.ulNeededSize )
   {
        TRACE( TL_N, TM_Tp, ("-TpGetCallStatus=$%x",NDIS_STATUS_INVALID_LENGTH) );
    
        return NDIS_STATUS_INVALID_LENGTH;
   }

    pRequest->LineCallStatus.ulUsedSize = pRequest->LineCallStatus.ulNeededSize;

    pRequest->LineCallStatus.ulCallFeatures = LINECALLFEATURE_ANSWER | LINECALLFEATURE_DROP;
    pRequest->LineCallStatus.ulCallPrivilege = LINECALLPRIVILEGE_OWNER;
    pRequest->LineCallStatus.ulCallState = pCall->ulTapiCallState;

    switch ( pRequest->LineCallStatus.ulCallState )
    {
        case LINECALLSTATE_DIALTONE:
        
            pRequest->LineCallStatus.ulCallStateMode = LINEDIALTONEMODE_NORMAL;

            break;
            
        case LINECALLSTATE_BUSY:
        
            pRequest->LineCallStatus.ulCallStateMode = LINEBUSYMODE_STATION;
            break;
            
        case LINECALLSTATE_DISCONNECTED:
        
            pRequest->LineCallStatus.ulCallStateMode = LINEDISCONNECTMODE_UNKNOWN;
            break;
            
        default:
            break;
    }

    TRACE( TL_N, TM_Tp, ("-TpGetCallStatus=$%x",NDIS_STATUS_SUCCESS) );

    return NDIS_STATUS_SUCCESS;
 }

//
// As we return the MAC addresses for caller and called station id's
// we set their size as 7 although a MAC address occupies 6 bytes.
// This is because TAPI overwrites the last bytes we return in these
// strings with a NULL character destroying the vaulable data.
// See bug: 313295
//
#define TAPI_STATION_ID_SIZE            ( 7 * sizeof( CHAR ) )

NDIS_STATUS
TpGetCallInfo(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_GET_CALL_INFO pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request returns detailed information about the specified call.

Parameters:

    pAdapter _ A pointer to our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_GET_CALL_INFO
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_CALL   hdCall;
        OUT LINE_CALL_INFO  LineCallInfo;

    } NDIS_TAPI_GET_CALL_INFO, *PNDIS_TAPI_GET_CALL_INFO;

    typedef struct _LINE_CALL_INFO
    {
        ULONG   ulTotalSize;
        ULONG   ulNeededSize;
        ULONG   ulUsedSize;

        ULONG   hLine;
        ULONG   ulLineDeviceID;
        ULONG   ulAddressID;

        ULONG   ulBearerMode;
        ULONG   ulRate;
        ULONG   ulMediaMode;

        ULONG   ulAppSpecific;
        ULONG   ulCallID;
        ULONG   ulRelatedCallID;
        ULONG   ulCallParamFlags;
        ULONG   ulCallStates;

        ULONG   ulMonitorDigitModes;
        ULONG   ulMonitorMediaModes;
        LINE_DIAL_PARAMS    DialParams;

        ULONG   ulOrigin;
        ULONG   ulReason;
        ULONG   ulCompletionID;
        ULONG   ulNumOwners;
        ULONG   ulNumMonitors;

        ULONG   ulCountryCode;
        ULONG   ulTrunk;

        ULONG   ulCallerIDFlags;
        ULONG   ulCallerIDSize;
        ULONG   ulCallerIDOffset;
        ULONG   ulCallerIDNameSize;
        ULONG   ulCallerIDNameOffset;

        ULONG   ulCalledIDFlags;
        ULONG   ulCalledIDSize;
        ULONG   ulCalledIDOffset;
        ULONG   ulCalledIDNameSize;
        ULONG   ulCalledIDNameOffset;

        ULONG   ulConnectedIDFlags;
        ULONG   ulConnectedIDSize;
        ULONG   ulConnectedIDOffset;
        ULONG   ulConnectedIDNameSize;
        ULONG   ulConnectedIDNameOffset;

        ULONG   ulRedirectionIDFlags;
        ULONG   ulRedirectionIDSize;
        ULONG   ulRedirectionIDOffset;
        ULONG   ulRedirectionIDNameSize;
        ULONG   ulRedirectionIDNameOffset;

        ULONG   ulRedirectingIDFlags;
        ULONG   ulRedirectingIDSize;
        ULONG   ulRedirectingIDOffset;
        ULONG   ulRedirectingIDNameSize;
        ULONG   ulRedirectingIDNameOffset;

        ULONG   ulAppNameSize;
        ULONG   ulAppNameOffset;

        ULONG   ulDisplayableAddressSize;
        ULONG   ulDisplayableAddressOffset;

        ULONG   ulCalledPartySize;
        ULONG   ulCalledPartyOffset;

        ULONG   ulCommentSize;
        ULONG   ulCommentOffset;

        ULONG   ulDisplaySize;
        ULONG   ulDisplayOffset;

        ULONG   ulUserUserInfoSize;
        ULONG   ulUserUserInfoOffset;

        ULONG   ulHighLevelCompSize;
        ULONG   ulHighLevelCompOffset;

        ULONG   ulLowLevelCompSize;
        ULONG   ulLowLevelCompOffset;

        ULONG   ulChargingInfoSize;
        ULONG   ulChargingInfoOffset;

        ULONG   ulTerminalModesSize;
        ULONG   ulTerminalModesOffset;

        ULONG   ulDevSpecificSize;
        ULONG   ulDevSpecificOffset;

    } LINE_CALL_INFO, *PLINE_CALL_INFO;

    typedef struct _LINE_DIAL_PARAMS
    {
        ULONG   ulDialPause;
        ULONG   ulDialSpeed;
        ULONG   ulDigitDuration;
        ULONG   ulWaitForDialtone;

    } LINE_DIAL_PARAMS, *PLINE_DIAL_PARAMS;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_FAILURE
    NDIS_STATUS_TAPI_INVALCALLHANDLE

---------------------------------------------------------------------------*/
{
    CALL* pCall = NULL;
    PLINE_CALL_INFO pLineCallInfo = NULL;
    
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpGetCallInfo") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpGetCallInfo: Invalid parameter") ); 

        TRACE( TL_N, TM_Tp, ("-TpGetCallInfo=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    pLineCallInfo = &pRequest->LineCallInfo;

    pCall = RetrieveFromHandleTable( pAdapter->TapiProv.hCallTable, 
                                     (NDIS_HANDLE) pRequest->hdCall );

    if ( pCall == NULL )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetCallInfo=$%x",NDIS_STATUS_TAPI_INVALCALLHANDLE) );

        return NDIS_STATUS_TAPI_INVALCALLHANDLE;
    }

    pLineCallInfo->ulNeededSize = sizeof( LINE_CALL_INFO ) +
                                  TAPI_STATION_ID_SIZE  +
                                  TAPI_STATION_ID_SIZE;

    if ( pLineCallInfo->ulTotalSize < pLineCallInfo->ulNeededSize )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetCallInfo=$%x",NDIS_STATUS_INVALID_LENGTH) );

        return NDIS_STATUS_INVALID_LENGTH;
    }

    pLineCallInfo->ulUsedSize = pLineCallInfo->ulNeededSize;

    pLineCallInfo->ulLineDeviceID = (ULONG) pCall->pLine->hdLine + 
                                    pCall->pLine->pAdapter->TapiProv.ulDeviceIDBase;
    pLineCallInfo->ulAddressID = 0;

    pLineCallInfo->ulBearerMode = LINEBEARERMODE_DATA;
    pLineCallInfo->ulRate = pCall->ulSpeed;
    pLineCallInfo->ulMediaMode = LINEMEDIAMODE_DIGITALDATA;

    pLineCallInfo->ulCallParamFlags = LINECALLPARAMFLAGS_IDLE;
    pLineCallInfo->ulCallStates = TAPI_LINECALLSTATES_SUPPORTED;

    pLineCallInfo->ulCallerIDFlags = LINECALLPARTYID_UNAVAIL;
    pLineCallInfo->ulCallerIDSize = 0;
    pLineCallInfo->ulCalledIDOffset = 0;
    pLineCallInfo->ulCalledIDFlags = LINECALLPARTYID_UNAVAIL;
    pLineCallInfo->ulCalledIDSize = 0;

    //
    // Set the caller and called station id information for both
    // incoming and outgoing calls.
    //
    {
        CHAR *pBuf = NULL;

        //
        // Copy the caller id information
        //
        pLineCallInfo->ulCallerIDFlags = LINECALLPARTYID_ADDRESS;
        pLineCallInfo->ulCallerIDSize = TAPI_STATION_ID_SIZE;
        pLineCallInfo->ulCallerIDOffset = sizeof(LINE_CALL_INFO);

        pBuf = ( (PUCHAR) pLineCallInfo ) + pLineCallInfo->ulCallerIDOffset;
        NdisMoveMemory( pBuf, pCall->DestAddr, TAPI_STATION_ID_SIZE );

        //
        // Copy the called id information
        //
        pLineCallInfo->ulCalledIDFlags = LINECALLPARTYID_ADDRESS;
        pLineCallInfo->ulCalledIDSize = TAPI_STATION_ID_SIZE;
        pLineCallInfo->ulCalledIDOffset = pLineCallInfo->ulCallerIDOffset + 
                                          pLineCallInfo->ulCallerIDSize;

        pBuf = ( (PUCHAR) pLineCallInfo ) + pLineCallInfo->ulCalledIDOffset;
        NdisMoveMemory( pBuf, pCall->SrcAddr, TAPI_STATION_ID_SIZE );

        pLineCallInfo->ulUsedSize = pLineCallInfo->ulNeededSize;
    }

    TRACE( TL_N, TM_Tp, ("-TpGetCallInfo=$%x",NDIS_STATUS_SUCCESS) );

    return NDIS_STATUS_SUCCESS;
}

#define TAPI_LINE_ADDR_STRING           "PPPoE VPN"

NDIS_STATUS
TpGetAddressCaps(
    IN ADAPTER* pAdapter,
    PNDIS_TAPI_GET_ADDRESS_CAPS pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request queries the specified address on the specified line device
    to determine its telephony capabilities.

Parameters:

    pAdapter _ A pointer ot our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_GET_ADDRESS_CAPS
    {
        IN  ULONG       ulRequestID;
        IN  ULONG       ulDeviceID;
        IN  ULONG       ulAddressID;
        IN  ULONG       ulExtVersion;
        OUT LINE_ADDRESS_CAPS   LineAddressCaps;

    } NDIS_TAPI_GET_ADDRESS_CAPS, *PNDIS_TAPI_GET_ADDRESS_CAPS;

    typedef struct _LINE_ADDRESS_CAPS
    {
        ULONG   ulTotalSize;
        ULONG   ulNeededSize;
        ULONG   ulUsedSize;

        ULONG   ulLineDeviceID;

        ULONG   ulAddressSize;
        ULONG   ulAddressOffset;

        ULONG   ulDevSpecificSize;
        ULONG   ulDevSpecificOffset;

        ULONG   ulAddressSharing;
        ULONG   ulAddressStates;
        ULONG   ulCallInfoStates;
        ULONG   ulCallerIDFlags;
        ULONG   ulCalledIDFlags;
        ULONG   ulConnectedIDFlags;
        ULONG   ulRedirectionIDFlags;
        ULONG   ulRedirectingIDFlags;
        ULONG   ulCallStates;
        ULONG   ulDialToneModes;
        ULONG   ulBusyModes;
        ULONG   ulSpecialInfo;
        ULONG   ulDisconnectModes;

        ULONG   ulMaxNumActiveCalls;
        ULONG   ulMaxNumOnHoldCalls;
        ULONG   ulMaxNumOnHoldPendingCalls;
        ULONG   ulMaxNumConference;
        ULONG   ulMaxNumTransConf;

        ULONG   ulAddrCapFlags;
        ULONG   ulCallFeatures;
        ULONG   ulRemoveFromConfCaps;
        ULONG   ulRemoveFromConfState;
        ULONG   ulTransferModes;

        ULONG   ulForwardModes;
        ULONG   ulMaxForwardEntries;
        ULONG   ulMaxSpecificEntries;
        ULONG   ulMinFwdNumRings;
        ULONG   ulMaxFwdNumRings;

        ULONG   ulMaxCallCompletions;
        ULONG   ulCallCompletionConds;
        ULONG   ulCallCompletionModes;
        ULONG   ulNumCompletionMessages;
        ULONG   ulCompletionMsgTextEntrySize;
        ULONG   ulCompletionMsgTextSize;
        ULONG   ulCompletionMsgTextOffset;

    } LINE_ADDRESS_CAPS, *PLINE_ADDRESS_CAPS;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_TAPI_INVALADDRESSID
    NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION
    NDIS_STATUS_TAPI_NODRIVER

---------------------------------------------------------------------------*/        
{
    HDRV_LINE hdLine = INVALID_LINE_HANDLE;

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpGetAddressCaps") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpGetAddressCaps: Invalid parameter") );  

        TRACE( TL_N, TM_Tp, ("-TpGetAddressCaps=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }
    
    //
    // Retrieve the handle to line context
    //
    hdLine = TpGetHdLineFromDeviceId( pAdapter, pRequest->ulDeviceID );
    
    if ( hdLine == INVALID_LINE_HANDLE )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetAddressCaps=$%x",NDIS_STATUS_TAPI_NODRIVER) );
        
        return NDIS_STATUS_TAPI_NODRIVER;
    }

    //
    // Verify the address id
    //
    if ( pRequest->ulAddressID != 0 )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetAddressCaps=$%x",NDIS_STATUS_TAPI_INVALADDRESSID) );
        
        return NDIS_STATUS_TAPI_INVALADDRESSID;
    }

    //
    // Verify the extension versions
    //
    if ( pRequest->ulExtVersion != 0 &&
         pRequest->ulExtVersion != TAPI_EXT_VERSION)
    {
        TRACE( TL_N, TM_Tp, ("-TpGetAddressCaps=$%x",NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION) );
        
        return NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION;
    }

    pRequest->LineAddressCaps.ulNeededSize = sizeof( LINE_ADDRESS_CAPS ) +
                                             sizeof( TAPI_LINE_ADDR_STRING );
    
    if ( pRequest->LineAddressCaps.ulTotalSize < pRequest->LineAddressCaps.ulNeededSize )
    {
        TRACE( TL_N, TM_Tp, ("-TpGetAddressCaps=$%x",NDIS_STATUS_INVALID_LENGTH) );

        return NDIS_STATUS_INVALID_LENGTH;
    }

    pRequest->LineAddressCaps.ulUsedSize = pRequest->LineAddressCaps.ulNeededSize;

    pRequest->LineAddressCaps.ulDialToneModes     = LINEDIALTONEMODE_NORMAL;
  
    pRequest->LineAddressCaps.ulSpecialInfo       = LINESPECIALINFO_UNAVAIL;
  
    pRequest->LineAddressCaps.ulDisconnectModes   = LINEDISCONNECTMODE_NORMAL |                                                    
                                                    LINEDISCONNECTMODE_UNKNOWN |
                                                    LINEDISCONNECTMODE_BUSY |
                                                    LINEDISCONNECTMODE_NOANSWER | 
                                                    LINEDISCONNECTMODE_UNREACHABLE |
                                                    LINEDISCONNECTMODE_BADADDRESS |
                                                    LINEDISCONNECTMODE_INCOMPATIBLE |
                                                    LINEDISCONNECTMODE_REJECT | 
                                                    LINEDISCONNECTMODE_NODIALTONE;
  
    pRequest->LineAddressCaps.ulMaxNumActiveCalls = pAdapter->nCallsPerLine;
    
    pRequest->LineAddressCaps.ulMaxNumTransConf   = 1;
    pRequest->LineAddressCaps.ulAddrCapFlags      = LINEADDRCAPFLAGS_DIALED;
  
    pRequest->LineAddressCaps.ulCallFeatures      = LINECALLFEATURE_ACCEPT |
                                                    LINECALLFEATURE_ANSWER |
                                                    LINECALLFEATURE_COMPLETECALL |
                                                    LINECALLFEATURE_DIAL |
                                                    LINECALLFEATURE_DROP;
  
    pRequest->LineAddressCaps.ulLineDeviceID      = pRequest->ulDeviceID;
    pRequest->LineAddressCaps.ulAddressSharing    = LINEADDRESSSHARING_PRIVATE;
    pRequest->LineAddressCaps.ulAddressStates     = 0;
  
    //
    // List of all possible call states.
    //
    pRequest->LineAddressCaps.ulCallStates        = TAPI_LINECALLSTATES_SUPPORTED;
    
    pRequest->LineAddressCaps.ulAddressSize = sizeof( TAPI_LINE_ADDR_STRING );
    pRequest->LineAddressCaps.ulAddressOffset = sizeof( LINE_ADDRESS_CAPS );

    {
        CHAR* pBuf;

        pBuf = ( (PUCHAR) &pRequest->LineAddressCaps ) + sizeof( LINE_ADDRESS_CAPS );
        NdisMoveMemory( pBuf, TAPI_LINE_ADDR_STRING, sizeof( TAPI_LINE_ADDR_STRING ) );
    }

    TRACE( TL_N, TM_Tp, ("-TpGetAddressCaps=$%x",NDIS_STATUS_SUCCESS) );

    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
TpSetStatusMessages(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_SET_STATUS_MESSAGES pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request enables the Connection Wrapper to specify which notification 
    messages the miniport should generate for events related to status changes 
    for the specified line or any of its addresses. By default, address and 
    line status reporting is initially disabled for a line.

Parameters:

    pAdapter _ A pointer to our adapter information structure.

    pRequest _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_SET_STATUS_MESSAGES
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_LINE   hdLine;
        IN  ULONG       ulLineStates;
        IN  ULONG       ulAddressStates;

    } NDIS_TAPI_SET_STATUS_MESSAGES, *PNDIS_TAPI_SET_STATUS_MESSAGES;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_TAPI_INVALLINEHANDLE
    NDIS_STATUS_TAPI_INVALLINESTATE
    NDIS_STATUS_TAPI_INVALADDRESSSTATE

---------------------------------------------------------------------------*/
{

    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpSetStatusMessages") );
    //
    // We do not send any line or address state change notifications at all,
    // so we do not care about it. 
    // 
    // We care about call notification messages and they are always on by default.
    //

    TRACE( TL_N, TM_Tp, ("-TpSetStatusMessages=$%x",NDIS_STATUS_SUCCESS) );
    
    return NDIS_STATUS_SUCCESS;
}

VOID
TpCallStateChangeHandler(
    IN CALL* pCall,
    IN ULONG ulCallState,
    IN ULONG ulStateParam
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This routine will indicate the given LINECALLSTATE to the Connection
    wrapper if the event has been enabled by the wrapper. Otherwise the state
    information is saved, but no indication is made.


    LINECALLSTATE_ Constants: The LINECALLSTATE_ bit-flag constants describe the
                              call states a call can be in. 

    LINECALLSTATE_ACCEPTED:
        The call was in the offering state and has been accepted. This indicates to 
        other (monitoring) applications that the current owner application has 
        claimed responsibility for answering the call. In ISDN, the accepted state is 
        entered when the called-party equipment sends a message to the switch 
        indicating that it is willing to present the call to the called person. This 
        has the side effect of alerting (ringing) the users at both ends of the call. 
        An incoming call can always be immediately answered without first being 
        separately accepted. 
    
    LINECALLSTATE_BUSY 
        The call is receiving a busy tone. A busy tone indicates that the call cannot 
        be completed either a circuit (trunk) or the remote party's station are in use
        . See LINEBUSYMODE_ Constants. 
        
    LINECALLSTATE_CONFERENCED 
        The call is a member of a conference call and is logically in the connected 
        state. 
        
    LINECALLSTATE_CONNECTED 
        The call has been established and the connection is made. Information is able 
        to flow over the call between the originating address and the destination 
        address. 
        
    LINECALLSTATE_DIALING 
        The originator is dialing digits on the call. The dialed digits are collected 
        by the switch. Note that neither lineGenerateDigits nor 
        TSPI_lineGenerateDigits will place the line into the dialing state. 
        
    LINECALLSTATE_DIALTONE 
        The call is receiving a dial tone from the switch, which means that the 
        switch is ready to receive a dialed number. See LINEDIALTONEMODE_ Constants 
        for identifiers of special dial tones, such as a stutter tone of normal voice 
        mail. 
        
    LINECALLSTATE_DISCONNECTED 
        The remote party has disconnected from the call. 
        
    LINECALLSTATE_IDLE 
        The call exists but has not been connected. No activity exists on the call, 
        which means that no call is currently active. A call can never transition 
        into the idle state. 
        
    LINECALLSTATE_OFFERING 
        The call is being offered to the station, signaling the arrival of a new call
        . The offering state is not the same as causing a phone or computer to ring. 
        In some environments, a call in the offering state does not ring the user 
        until the switch instructs the line to ring. An example use might be where an 
        incoming call appears on several station sets but only the primary address 
        rings. The instruction to ring does not affect any call states. 
        
    LINECALLSTATE_ONHOLD 
        The call is on hold by the switch. This frees the physical line, which allows 
        another call to use the line. 
        
    LINECALLSTATE_ONHOLDPENDCONF 
        The call is currently on hold while it is being added to a conference. 

    LINECALLSTATE_ONHOLDPENDTRANSFER 
        The call is currently on hold awaiting transfer to another number. 

    LINECALLSTATE_PROCEEDING 
        Dialing has completed and the call is proceeding through the switch or 
        telephone network. This occurs after dialing is complete and before the call 
        reaches the dialed party, as indicated by ringback, busy, or answer. 

    LINECALLSTATE_RINGBACK 
        The station to be called has been reached, and the destination's switch is 
        generating a ring tone back to the originator. A ringback means that the 
        destination address is being alerted to the call. 

    LINECALLSTATE_SPECIALINFO 
        The call is receiving a special information signal, which precedes a 
        prerecorded announcement indicating why a call cannot be completed. See 
        LINESPECIALINFO_ Constants. 
        
    LINECALLSTATE_UNKNOWN 
        The call exists, but its state is currently unknown. This may be the result 
        of poor call progress detection by the service provider. A call state message 
        with the call state set to unknown may also be generated to inform the TAPI 
        DLL about a new call at a time when the actual call state of the call is not 
        exactly known. 

Parameters:

    pCall _ A pointer to our call information structure.

    ulCallState _ The LINECALLSTATE event to be posted to TAPI/WAN.

    ulStateParam _ This value depends on the event being posted, and some
                   events will pass in zero if they don't use this parameter.

Return Values:

    None

---------------------------------------------------------------------------*/
{
    BOOLEAN fIndicateStatus = FALSE;
    NDIS_TAPI_EVENT TapiEvent;
    ULONG ulOldCallState;
    
    ASSERT( VALIDATE_CALL( pCall ) );

    TRACE( TL_N, TM_Tp, ("+TpCallStateChangeHandler") );

    NdisAcquireSpinLock( &pCall->lockCall );

    do 
    {
        //
        // Check if we have a valid htCall member, otherwise it means we are already done,
        // so we should not give any more notifications to TAPI about state changes
        //
        if ( pCall->htCall == (HTAPI_CALL) NULL )
        {
            TRACE( TL_N, TM_Tp, ("TpCallStateChangeHandler: No valid htCall") );

            break;
        }

        //
        // A connect notification can come only after a PROCEEDING or OFFERING state
        // is reached
        //
        if ( ulCallState == LINECALLSTATE_CONNECTED && 
             ( pCall->ulTapiCallState != LINECALLSTATE_OFFERING &&
               pCall->ulTapiCallState != LINECALLSTATE_PROCEEDING ) )
        {
            TRACE( TL_N, TM_Tp, ("TpCallStateChangeHandler: Invalid order of state change") );
            
            break;
        }

        //
        // If the new state is the same as old state, just return
        //
        if ( pCall->ulTapiCallState == ulCallState )
        {
            TRACE( TL_N, TM_Tp, ("TpCallStateChangeHandler: No state change") );

            break;
        }

        //
        // Otherwise, change the calls state, and 
        // make a notification to TAPI about the new state
        //
        ulOldCallState = pCall->ulTapiCallState;
        pCall->ulTapiCallState = ulCallState;
        
        TapiEvent.htLine = pCall->pLine->htLine;
        TapiEvent.htCall = pCall->htCall;   
    
        TapiEvent.ulMsg  = LINE_CALLSTATE;
        
        TapiEvent.ulParam1 = ulCallState;
        TapiEvent.ulParam2 = ulStateParam;
        TapiEvent.ulParam3 = LINEMEDIAMODE_DIGITALDATA; 

        fIndicateStatus = TRUE;

        if ( ulCallState == LINECALLSTATE_CONNECTED )
        {
            ADAPTER* pAdapter = pCall->pLine->pAdapter;
            
            //
            // Since the call is connected, reset CLBF_CallConnectPending bit
            //
            pCall->ulClFlags &= ~CLBF_CallConnectPending;

            //
            // Also prepare the WanLinkInfo structure of call context now
            // as right after we indicate line-up to NDISWAN, it will query us
            // for this info.
            //
            NdisZeroMemory( &pCall->NdisWanLinkInfo, sizeof( pCall->NdisWanLinkInfo ) );
            
            pCall->NdisWanLinkInfo.MaxSendFrameSize = pCall->ulMaxFrameSize;
            pCall->NdisWanLinkInfo.MaxRecvFrameSize = pCall->ulMaxFrameSize;
    
            pCall->NdisWanLinkInfo.HeaderPadding = pAdapter->NdisWanInfo.HeaderPadding;
            pCall->NdisWanLinkInfo.TailPadding = pAdapter->NdisWanInfo.TailPadding;
    
            pCall->NdisWanLinkInfo.SendFramingBits = pAdapter->NdisWanInfo.FramingBits;
            pCall->NdisWanLinkInfo.RecvFramingBits = pAdapter->NdisWanInfo.FramingBits;
        
            pCall->NdisWanLinkInfo.SendACCM = 0;
            pCall->NdisWanLinkInfo.RecvACCM = 0;
        
        }
        else if ( ulCallState == LINECALLSTATE_DISCONNECTED )
        {
            TRACE( TL_N, TM_Tp, ("TpCallStateChangeHandler: LINEDISCONNECTMODE: %x", ulStateParam ) );

            //
            // This state change will only occur if TpDropCall() is in progress,
            // so we invalidate the htCall member of call context in order to prevent
            // a possible out of sync state change notification.
            //
            pCall->htCall = (HTAPI_CALL) NULL;
            
        }

    } while ( FALSE) ;
    
    NdisReleaseSpinLock( &pCall->lockCall );

    //
    // Notify state change to TAPI if needed
    //
    if ( fIndicateStatus )
    {
        TRACE( TL_N, TM_Tp, ("TpCallStateChangeHandler: Indicate LINE_CALLSTATE change: %x -> %x",ulOldCallState,ulCallState ) );

        NdisMIndicateStatus( pCall->pLine->pAdapter->MiniportAdapterHandle,
                             NDIS_STATUS_TAPI_INDICATION,
                             &TapiEvent,
                             sizeof( NDIS_TAPI_EVENT ) );   
    }

    TRACE( TL_N, TM_Tp, ("-TpCallStateChangeHandler") );
}


NDIS_STATUS
TpMakeCall(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_MAKE_CALL pRequest,
    IN ULONG ulRequestLength
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request places a call on the specified line to the specified
    destination address. Optionally, call parameters can be specified if
    anything but default call setup parameters are requested.

Parameters:

    Adapter _ A pointer ot our adapter information structure.

    Request _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_MAKE_CALL
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_LINE   hdLine;
        IN  HTAPI_CALL  htCall;
        OUT HDRV_CALL   hdCall;
        IN  ULONG       ulDestAddressSize;
        IN  ULONG       ulDestAddressOffset;
        IN  BOOLEAN     bUseDefaultLineCallParams;
        IN  LINE_CALL_PARAMS    LineCallParams;

    } NDIS_TAPI_MAKE_CALL, *PNDIS_TAPI_MAKE_CALL;

    typedef struct _LINE_CALL_PARAMS        // Defaults:
    {
        ULONG   ulTotalSize;                // ---------

        ULONG   ulBearerMode;               // voice
        ULONG   ulMinRate;                  // (3.1kHz)
        ULONG   ulMaxRate;                  // (3.1kHz)
        ULONG   ulMediaMode;                // interactiveVoice

        ULONG   ulCallParamFlags;           // 0
        ULONG   ulAddressMode;              // addressID
        ULONG   ulAddressID;                // (any available)

        LINE_DIAL_PARAMS DialParams;        // (0, 0, 0, 0)

        ULONG   ulOrigAddressSize;          // 0
        ULONG   ulOrigAddressOffset;
        ULONG   ulDisplayableAddressSize;
        ULONG   ulDisplayableAddressOffset;

        ULONG   ulCalledPartySize;          // 0
        ULONG   ulCalledPartyOffset;

        ULONG   ulCommentSize;              // 0
        ULONG   ulCommentOffset;

        ULONG   ulUserUserInfoSize;         // 0
        ULONG   ulUserUserInfoOffset;

        ULONG   ulHighLevelCompSize;        // 0
        ULONG   ulHighLevelCompOffset;

        ULONG   ulLowLevelCompSize;         // 0
        ULONG   ulLowLevelCompOffset;

        ULONG   ulDevSpecificSize;          // 0
        ULONG   ulDevSpecificOffset;    
        
    } LINE_CALL_PARAMS, *PLINE_CALL_PARAMS;

    typedef struct _LINE_DIAL_PARAMS
    {
        ULONG   ulDialPause;
        ULONG   ulDialSpeed;
        ULONG   ulDigitDuration;
        ULONG   ulWaitForDialtone;

    } LINE_DIAL_PARAMS, *PLINE_DIAL_PARAMS;

    RequestLength _ Length of the request buffer

Return Values:

    NDIS_STATUS_TAPI_ADDRESSBLOCKED
    NDIS_STATUS_TAPI_BEARERMODEUNAVAIL
    NDIS_STATUS_TAPI_CALLUNAVAIL
    NDIS_STATUS_TAPI_DIALBILLING
    NDIS_STATUS_TAPI_DIALQUIET
    NDIS_STATUS_TAPI_DIALDIALTONE
    NDIS_STATUS_TAPI_DIALPROMPT
    NDIS_STATUS_TAPI_INUSE
    NDIS_STATUS_TAPI_INVALADDRESSMODE
    NDIS_STATUS_TAPI_INVALBEARERMODE
    NDIS_STATUS_TAPI_INVALMEDIAMODE
    NDIS_STATUS_TAPI_INVALLINESTATE
    NDIS_STATUS_TAPI_INVALRATE
    NDIS_STATUS_TAPI_INVALLINEHANDLE
    NDIS_STATUS_TAPI_INVALADDRESS
    NDIS_STATUS_TAPI_INVALADDRESSID
    NDIS_STATUS_TAPI_INVALCALLPARAMS
    NDIS_STATUS_RESOURCES
    NDIS_STATUS_TAPI_OPERATIONUNAVAIL
    NDIS_STATUS_FAILURE
    NDIS_STATUS_TAPI_RESOURCEUNAVAIL
    NDIS_STATUS_TAPI_RATEUNAVAIL
    NDIS_STATUS_TAPI_USERUSERINFOTOOBIG

---------------------------------------------------------------------------*/
{   
    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
    LINE* pLine = NULL;

    CALL* pCall = NULL;
    HDRV_CALL hdCall = (HDRV_CALL) NULL;

    BOOLEAN fCallInsertedToHandleTable = FALSE;

    WORKITEM* pWorkItem = NULL;
    PVOID Args[4];

    BOOLEAN fRenumerationNotScheduled = FALSE;
    
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpMakeCall") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpMakeCall: Invalid parameter") );    

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }

    //
    // Retrieve a pointer to the line context
    //
    pLine = TpGetLinePtrFromHdLine( pAdapter, pRequest->hdLine );

    if ( pLine == NULL )
    {
        status = NDIS_STATUS_TAPI_INVALLINEHANDLE;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }

    //
    // See if we can make calls on this line at all
    //
    if ( ! (pLine->ulLnFlags & LNBF_MakeOutgoingCalls ) )
    {
        status = NDIS_STATUS_TAPI_ADDRESSBLOCKED;
        
        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }

    //
    // See if we can still make calls on this line
    //
    if ( pLine->nActiveCalls == pLine->nMaxCalls )
    {
        status = NDIS_STATUS_TAPI_OPERATIONUNAVAIL;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }

    //
    // Make sure the parameters suppied in the request are acceptable
    //
    if ( pRequest->bUseDefaultLineCallParams )
    {
        status = NDIS_STATUS_TAPI_INVALCALLPARAMS;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }

    if ( !( pRequest->LineCallParams.ulBearerMode & LINEBEARERMODE_DATA ) )
    {
        status = NDIS_STATUS_TAPI_INVALBEARERMODE;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }
    
    if ( !( pRequest->LineCallParams.ulMediaMode & LINEMEDIAMODE_DIGITALDATA ) )
    {
        status = NDIS_STATUS_TAPI_INVALMEDIAMODE;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }

    if ( !( pRequest->LineCallParams.ulAddressMode &
         ( LINEADDRESSMODE_ADDRESSID | LINEADDRESSMODE_DIALABLEADDR ) ) )
    {
        status = NDIS_STATUS_TAPI_INVALADDRESSMODE;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }
                                           
    if ( pRequest->LineCallParams.ulAddressID > 0 )
    {
        status = NDIS_STATUS_TAPI_INVALADDRESSID;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }

    if ( pRequest->ulDestAddressOffset + pRequest->ulDestAddressSize > ulRequestLength )
    {
        status = NDIS_STATUS_TAPI_INVALPARAM;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;

    }

    //
    // Create a call context 
    //
    if ( ALLOC_CALL( &pCall ) != NDIS_STATUS_SUCCESS )
    {
        status = NDIS_STATUS_RESOURCES;

        TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );   

        return status;
    }

    do
    {
        //
        // Initialize the call context
        //
        status = TpCallInitialize( pCall, pLine, pRequest->htCall, FALSE /* fIncoming */ );
    
        if ( status != NDIS_STATUS_SUCCESS )
            break;

        //
        // Insert the call context into the tapi provider's handle table
        //
        NdisAcquireSpinLock( &pAdapter->lockAdapter );
        
        hdCall = (HDRV_CALL) InsertToHandleTable( pAdapter->TapiProv.hCallTable,
                                                  NO_PREFERED_INDEX,
                                                  pCall );
                            
        NdisReleaseSpinLock( &pAdapter->lockAdapter );

        if ( hdCall == (HDRV_CALL) NULL )
        {
            status = NDIS_STATUS_TAPI_CALLUNAVAIL;

            break;
        }

        fCallInsertedToHandleTable = TRUE;

        //
        // Set the call's hdCall member
        //
        pCall->hdCall = hdCall;

        //
        // Set AC Name and the service name passed in the request.
        // We expect it in the following format:
        // AC Name\Service Name
        //
        // The following examles are all valid:
        // AC Name\                          -> Connect to the default service on the specified AC
        // Service Name                      -> Connect to the specified service on any AC
        // AC Name\Service Name              -> Connect to the specified service on the specified AC
        //                                   -> Connect to the default service on any AC
        //
        // We will also strip off any leading or trailing space chars.
        //
                                                 
        {
            CHAR* pBuf = ( (PUCHAR) pRequest ) + pRequest->ulDestAddressOffset;
            ULONG size = pRequest->ulDestAddressSize;

            ULONG ACNameStartPos, ACNameEndPos;
            ULONG ServiceNameStartPos, ServiceNameEndPos;
            
            //
            // Remove the terminating NULL characters if passed any.
            // 
            for ( ; size > 0 ; size-- )
            {
                if ( pBuf[ size - 1] != '\0' )
                {
                    break;
                }
            }

            //
            // Get the AC Name and service name
            //
            do
            {
               ULONG i = 0;
               CHAR* pTempChar = pBuf;

               ACNameStartPos = ACNameEndPos = 0;
               ServiceNameStartPos = ServiceNameEndPos = 0;

               //
               // Skip leading spaces
               //
               while (i < size)
               {
                  if (*pTempChar != ' ')
                  {
                     break;
                  }
                  
                  i++;
                  
                  pTempChar++;
               }

               if (i == size)
               {
                  break;
               }

               ACNameStartPos = ACNameEndPos = i;
                              
               while (i < size)
               {
                  if (*pTempChar == '\\')
                  {
                     break;
                  }

                  i++;

                  if (*pTempChar != ' ')
                  {
                     //
                     // Mark the beginning of trailing spaces
                     //
                     ACNameEndPos = i;   
                  }

                  pTempChar++;
               }

               if (i == size)
               {
                  //
                  // No AC Name was specified, it was just Service Name 
                  // and we parsed it
                  //
                  ServiceNameStartPos = ACNameStartPos;
                  ServiceNameEndPos = ACNameEndPos;

                  ACNameStartPos = ACNameEndPos = 0;

                  break;
               }

               //
               // Advance 'i' and 'pTempChar' once to skip the '\' character
               //
               i++;
               
               pTempChar++;

               //
               // Skip leading spaces
               //
               while (i < size)
               {
                  if (*pTempChar != ' ')
                  {
                     break;
                  }
                  
                  i++;
                  
                  pTempChar++;
               }

               if (i == size)
               {
                  break;
               }

               ServiceNameStartPos = ServiceNameEndPos = i;

               while (i < size)
               {
                  i++;

                  if (*pTempChar != ' ')
                  {
                     //
                     // Mark the beginning of trailing spaces
                     //
                     ServiceNameEndPos = i;   
                  }

                  pTempChar++;
               }
               
            } while ( FALSE );

            //
            // Retrieve the AC Name information into the call context
            //
            pCall->nACNameLength = (USHORT) ( ( MAX_AC_NAME_LENGTH < ( ACNameEndPos - ACNameStartPos ) ) ?
                                                MAX_AC_NAME_LENGTH : ( ACNameEndPos - ACNameStartPos ) );


            if ( pCall->nACNameLength != 0 )
            {
                NdisMoveMemory( pCall->ACName, &pBuf[ACNameStartPos], pCall->nACNameLength );

                pCall->fACNameSpecified = TRUE;
            }

            //
            // Retrieve the Service Name information into the call context
            //
            pCall->nServiceNameLength = (USHORT) ( ( MAX_SERVICE_NAME_LENGTH < ( ServiceNameEndPos - ServiceNameStartPos ) ) ?
                                                     MAX_SERVICE_NAME_LENGTH : ( ServiceNameEndPos - ServiceNameStartPos ) );


            if ( pCall->nServiceNameLength != 0 )
            {
                NdisMoveMemory( pCall->ServiceName, &pBuf[ServiceNameStartPos], pCall->nServiceNameLength );
            }
        }

        //
        // Allocate a work item for scheduling FsmMakeCall()
        //
        // Set the arguements array
        //
        Args[0] = (PVOID) pCall;

        pWorkItem = AllocWorkItem( &gl_llistWorkItems,
                                   ExecAdapterWorkItem,
                                   NULL,
                                   Args, 
                                   CWT_workFsmMakeCall );

        if ( pWorkItem == NULL ) 
        {
            status = NDIS_STATUS_RESOURCES;

            break;
        }

        {
           //
           // Schedule a work item to reenumerate bindings
           //
            WORKITEM* pCallWorkItem;
            
            Args[0] = (PVOID) BN_SetFiltersForMakeCall;       // Is a set filter request
            Args[1] = (PVOID) pCall;
            Args[2] = (PVOID) pRequest;
            Args[3] = (PVOID) pWorkItem;

            pCallWorkItem = pWorkItem;

            //
            // Allocate work item for the bind
            //
            pWorkItem = AllocWorkItem( &gl_llistWorkItems,
                                       ExecBindingWorkItem,
                                       NULL,
                                       Args,
                                       BWT_workPrStartBinds );

            if ( pWorkItem == NULL ) 
            {
               //
               // We can not allocate the work item for reenumeration of bindings
               // But may be all enumerations are intact, so let the
               // make call request continue
               //

               pWorkItem = pCallWorkItem;

               fRenumerationNotScheduled = TRUE;
            }
        }

        //
        // Insert the call context into the line's active call list
        //
        NdisAcquireSpinLock( &pLine->lockLine );

        InsertHeadList( &pLine->linkCalls, &pCall->linkCalls );

        pLine->nActiveCalls++;

        ReferenceLine( pLine, FALSE );

        NdisReleaseSpinLock( &pLine->lockLine );

        //
        // Reference the call 3 times:
        //  1. For scheduling of FsmMakeCall()
        //  2. For dropping of the call
        //  3. For closing of the call
        //
        NdisAcquireSpinLock( &pCall->lockCall );
        
        ReferenceCall( pCall, FALSE );
        ReferenceCall( pCall, FALSE );
        ReferenceCall( pCall, FALSE );

        NdisReleaseSpinLock( &pCall->lockCall );

        //
        // Schedule the bind operation
        //
        ScheduleWorkItem( pWorkItem );

        status = NDIS_STATUS_SUCCESS;

    } while ( FALSE );

    if ( status == NDIS_STATUS_SUCCESS )
    {
        //
        // If succesfull, return the call handle to TAPI and mark call as TAPI notified
        // of new call
        //
        pRequest->hdCall = hdCall;

        //
        // If we have scheduled a reenumeration work item, then pend this request
        // It will be completed when reenumeration is complete.
        //
        if ( !fRenumerationNotScheduled )
        {
           status = NDIS_STATUS_PENDING;
        }           

    }
    else
    {

        //
        // Somethings failed, do clean up
        //
        
        if ( fCallInsertedToHandleTable )
        {
            NdisAcquireSpinLock( &pAdapter->lockAdapter );
        
            RemoveFromHandleTable( pAdapter->TapiProv.hCallTable, (NDIS_HANDLE) hdCall );

            NdisReleaseSpinLock( &pAdapter->lockAdapter );
        }

        if ( pCall )
        {
            TpCallCleanup( pCall );
        }

    }

    TRACE( TL_N, TM_Tp, ("-TpMakeCall=$%x",status) );

    return status;  
}

VOID
TpMakeCallComplete(
   IN CALL* pCall,
   IN PNDIS_TAPI_MAKE_CALL pRequest   
   )
{
   TRACE( TL_N, TM_Tp, ("+TpMakeCallComplete") );

   NdisMQueryInformationComplete( pCall->pLine->pAdapter->MiniportAdapterHandle,
                                  NDIS_STATUS_SUCCESS );

   TRACE( TL_N, TM_Tp, ("-TpMakeCallComplete=$%x",NDIS_STATUS_SUCCESS) );

}

NDIS_STATUS
TpCallInitialize(
    IN CALL* pCall,
    IN LINE* pLine,
    IN HTAPI_CALL htCall,
    IN BOOLEAN fIncoming
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function makes initialization on the call context.

Parameters:

    pCall _ A pointer to our call information structure.

    pLine _ A pointer to the line information structure that the call belongs.

    htCall _ Handle assigned to the call by TAPI.

    fIncoming _ Flag that indicates if the call is inbound or outbound.

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_FAILURE

---------------------------------------------------------------------------*/   
{

    TRACE( TL_N, TM_Tp, ("+TpCallInitialize") );

    NdisZeroMemory( pCall, sizeof( CALL ) );
    
    InitializeListHead( &pCall->linkCalls );

    pCall->tagCall = MTAG_CALL;

    pCall->ulClFlags = ( CLBF_CallOpen | CLBF_CallConnectPending );

    NdisAllocateSpinLock( &pCall->lockCall );

    pCall->fIncoming = fIncoming;

    pCall->pLine = pLine;

    pCall->htCall = htCall;

    InitializeListHead( &pCall->linkReceivedPackets );

    pCall->ulTapiCallState = LINECALLSTATE_IDLE;

    TRACE( TL_N, TM_Tp, ("-TpCallInitialize") );

    return NDIS_STATUS_SUCCESS;
}

NDIS_STATUS
TpAnswerCall(
    IN ADAPTER* pAdapter,
    IN PNDIS_TAPI_ANSWER pRequest
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This request answers the specified offering call.  It may optionally send
    the specified user-to-user information to the calling party.

Parameters:

    Adapter _ A pointer ot our adapter information structure.

    Request _ A pointer to the NDIS_TAPI request structure for this call.

    typedef struct _NDIS_TAPI_ANSWER
    {
        IN  ULONG       ulRequestID;
        IN  HDRV_CALL   hdCall;
        IN  ULONG       ulUserUserInfoSize;
        IN  UCHAR       UserUserInfo[1];

    } NDIS_TAPI_ANSWER, *PNDIS_TAPI_ANSWER;

Return Values:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_TAPI_INVALCALLHANDLE

---------------------------------------------------------------------------*/
{
    NDIS_STATUS status = NDIS_STATUS_SUCCESS;
    CALL* pCall = NULL;
    
    ASSERT( VALIDATE_ADAPTER( pAdapter ) );

    TRACE( TL_N, TM_Tp, ("+TpAnswerCall") );

    if ( pRequest == NULL || pAdapter == NULL )
    {
        TRACE( TL_A, TM_Tp, ("TpAnswerCall: Invalid parameter") );  

        TRACE( TL_N, TM_Tp, ("-TpAnswerCall=$%x",NDIS_STATUS_TAPI_INVALPARAM) );

        return NDIS_STATUS_TAPI_INVALPARAM;
    }
    
    
    pCall = RetrieveFromHandleTable( pAdapter->TapiProv.hCallTable, 
                                     (NDIS_HANDLE) pRequest->hdCall );

    if ( pCall == NULL )
    {
        status = NDIS_STATUS_TAPI_INVALCALLHANDLE;

        TRACE( TL_N, TM_Tp, ("-TpAnswerCall=$%x",status) ); 
    
        return status;
    }

    status = FsmAnswerCall( pCall );

    TRACE( TL_N, TM_Tp, ("-TpAnswerCall=$%x",status) ); 
    
    return status;
}

VOID 
ExecAdapterWorkItem(
    IN PVOID Args[4],
    IN UINT workType
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function executes the scheduled work items for the adapter.

    
Parameters:

    Args:
        An array of length 4 keeping PVOIDs

    workType:
        Indicates the type of the work to be executed.
        We use this to understand what we should do in this function.

Return Values:

    None
    
---------------------------------------------------------------------------*/
{

    TRACE( TL_N, TM_Mp, ("+ExecAdapterWorkItem") );

    switch ( workType )
    {

        case CWT_workFsmMakeCall:

            //
            // Scheduled from TpMakeCall() to start an outgoing call
            //
            {
                CALL* pCall = (CALL*) Args[0];
                
                FsmMakeCall( pCall );   

                //
                // Remove the reference due to scheduling of FsmMakeCall()
                //
                DereferenceCall( pCall );

                break;
            }

        default:

            break;


    }

    TRACE( TL_N, TM_Mp, ("-ExecAdapterWorkItem") );

}


VOID
TpReceiveCall(
    IN ADAPTER* pAdapter,
    IN BINDING* pBinding,
    IN PPPOE_PACKET* pPacket
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will be called by miniport when we receive a PADR packet 
    to initiate a call.
    
Parameters:

    pAdapter:
        A pointer to our adapter information structure.

    pPacket:
        A pointer to the received PADI packet.

Return Values:

    None
    
---------------------------------------------------------------------------*/   
{
    HANDLE_TABLE hCallTable = NULL; 
    UINT hCallTableSize     = 0;
    UINT nIndex             = 0;
    LINE* pLine             = NULL;
    CALL* pCall             = NULL;
    UINT i;
    NDIS_STATUS status;

    BOOLEAN fCallInsertedToHandleTable = FALSE;

    TRACE( TL_N, TM_Tp, ("+TpReceiveCall") );

    NdisAcquireSpinLock( &pAdapter->lockAdapter );

    //
    // Traverse the call handle table and find an empty spot
    //
    hCallTableSize = pAdapter->nMaxLines * pAdapter->nCallsPerLine;
                
    hCallTable = pAdapter->TapiProv.hCallTable;

    for ( nIndex = 0; nIndex < hCallTableSize; nIndex++ )
    {

        if ( RetrieveFromHandleTableByIndex( hCallTable, (USHORT) nIndex ) == NULL )
            break;
            
    }

    if ( nIndex == hCallTableSize )
    {
        //
        // We are already maxed out with current calls, do not respond to the request
        //
        // TODO: We could send a PADO packet with an error tag saying that we can 
        //       not accept calls temporarily.
        //
        TRACE( TL_N, TM_Tp, ("-TpReceiveCall: Can not take calls - Call table full") );
        
        NdisReleaseSpinLock( &pAdapter->lockAdapter );

        return;
    }

    //
    // We have found an empty spot, now see if any of the open lines accept calls
    //
    for ( i = 0; i < pAdapter->nMaxLines; i++ )
    {
        pLine = pAdapter->TapiProv.LineTable[i];

        if ( pLine == NULL )
            continue;

        if ( pLine->nActiveCalls == pAdapter->nCallsPerLine )
        {
            pLine = NULL;
            
            continue;
        }

        if ( !( pLine->ulLnFlags & LNBF_AcceptIncomingCalls ) )
        {
            pLine = NULL;
            
            continue;
        }

        break;
        
    }

    if ( pLine == NULL )
    {
        //
        // We do not have any lines accepting calls right now
        //
        // TODO: We could send a PADO packet with an error tag saying that there are
        //       no active lines accepting calls at the moment.
        //
        TRACE( TL_N, TM_Tp, ("-TpReceiveCall: Can not take calls - No lines taking calls") );

        NdisReleaseSpinLock( &pAdapter->lockAdapter );

        return;

    }

    //
    // We have found a line accepting calls, and we have a free spot in call handle table,
    // so create a call context, add it to TapiProv structures, and notify TAPI of the new
    // call
    //

    do
    {
        HDRV_CALL hdCall;
        
        //
        // Create a call context 
        //
        if ( ALLOC_CALL( &pCall ) != NDIS_STATUS_SUCCESS )
        {
            status = NDIS_STATUS_RESOURCES;

            break;
        }

        //
        // Initialize the call context
        //
        status = TpCallInitialize( pCall, pLine, (HTAPI_CALL) 0, TRUE /* fIncoming */ );
    
        if ( status != NDIS_STATUS_SUCCESS )
            break;

        //
        // Insert the call context into the tapi provider's handle table
        //
        
        hdCall = (HDRV_CALL) InsertToHandleTable( pAdapter->TapiProv.hCallTable,
                                                  (USHORT) nIndex,
                                                  (PVOID) pCall );
                            

        if ( hdCall == (HDRV_CALL) NULL )
        {
            status = NDIS_STATUS_TAPI_CALLUNAVAIL;

            break;
        }

        fCallInsertedToHandleTable = TRUE;

        //
        // Set the call's hdCall member
        //
        pCall->hdCall = hdCall;

        //
        // Insert the call context into the line's active call list
        //
        NdisAcquireSpinLock( &pLine->lockLine );

        InsertHeadList( &pLine->linkCalls, &pCall->linkCalls );

        pLine->nActiveCalls++;

        ReferenceLine( pLine, FALSE );

        NdisReleaseSpinLock( &pLine->lockLine );

        //
        // Reference the call 3 times:
        //  1. For running FsmReceiveCall() below
        //  2. For dropping of the call
        //  3. For closing of the call
        //
        NdisAcquireSpinLock( &pCall->lockCall );

        ReferenceCall( pCall, FALSE );
        ReferenceCall( pCall, FALSE );
        ReferenceCall( pCall, FALSE );

        NdisReleaseSpinLock( &pCall->lockCall );

        status = NDIS_STATUS_SUCCESS;

    } while ( FALSE );

    NdisReleaseSpinLock( &pAdapter->lockAdapter );
    
    //
    // Check the status
    //
    if ( status == NDIS_STATUS_SUCCESS )
    {
    
        //
        // Kick the state machine to start receiving the call
        //
        FsmReceiveCall( pCall, pBinding, pPacket );

        //
        // Remove the reference added above
        //
        DereferenceCall( pCall );

    }
    else
    {
        //
        // If something failed, do clean up
        //  
        
        if ( fCallInsertedToHandleTable )
        {
            NdisAcquireSpinLock( &pAdapter->lockAdapter );
        
            RemoveFromHandleTable( pAdapter->TapiProv.hCallTable, (NDIS_HANDLE) pCall->hdCall );

            NdisReleaseSpinLock( &pAdapter->lockAdapter );
        }

        if ( pCall )
        {
            TpCallCleanup( pCall );
        }

    }

    TRACE( TL_N, TM_Tp, ("-TpReceiveCall=$%x",status) );
}

BOOLEAN
TpIndicateNewCall(
    IN CALL* pCall
    )
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Functional Description:

    This function will be called to indicate the new call context to to TAPI.

    If TAPI can be notified succesfully, then it returns TRUE, otherwise it 
    returns FALSE.
    
Parameters:

    pCall _ New call context to be indicated to TAPI.
    
Return Values:

    TRUE
    FALSE
    
---------------------------------------------------------------------------*/   
{
    NDIS_TAPI_EVENT TapiEvent;
    BOOLEAN fRet = FALSE;

    TRACE( TL_N, TM_Tp, ("+TpIndicateNewCall") );

    NdisAcquireSpinLock( &pCall->lockCall );

    if ( pCall->ulClFlags & CLBF_CallDropped ||
         pCall->ulClFlags & CLBF_CallClosePending )
    {
        TRACE( TL_N, TM_Tp, ("TpIndicateNewCall: Can not indicate new call since call is going down") );

        TRACE( TL_N, TM_Tp, ("-TpIndicateNewCall") );

        //
        // This may happen if call is closed internally due to the FSM timeout handlers
        //
        NdisReleaseSpinLock( &pCall->lockCall );

        return fRet;
    }

    NdisReleaseSpinLock( &pCall->lockCall );
    
    //
    // Indicate the new call to TAPI, retrieve the corresponding TAPI handle (htCall)
    // and set it in the call
    //
    // Future: The casts below between ulParam2. pCall->hdCall and pCall->htCall will
    //         be a problem on 64 bit machines.
    //
    TapiEvent.htLine = pCall->pLine->htLine;
    TapiEvent.htCall = 0;
    TapiEvent.ulMsg  = LINE_NEWCALL;
        
    TapiEvent.ulParam1 = (ULONG) pCall->hdCall;
    TapiEvent.ulParam2 = 0;
    TapiEvent.ulParam3 = 0; 

    TRACE( TL_N, TM_Tp, ("TpIndicateNewCall: Indicate LINE_NEWCALL") );

    NdisMIndicateStatus( pCall->pLine->pAdapter->MiniportAdapterHandle,
                         NDIS_STATUS_TAPI_INDICATION,
                         &TapiEvent,
                         sizeof( NDIS_TAPI_EVENT ) );   

    NdisAcquireSpinLock( &pCall->lockCall );
        
    pCall->htCall = (HTAPI_CALL) TapiEvent.ulParam2;

    fRet = TRUE;

    NdisReleaseSpinLock( &pCall->lockCall );

    TRACE( TL_N, TM_Tp, ("-TpIndicateNewCall") );

    return fRet;

}