/*++

Copyright (c) 1990-1998  Microsoft Corporation, All Rights Reserved.

Module Name:

    callmgr.c

Abstract:

    This module contains the interface of the driver with the Call Manager.

Author:

    Anil Francis Thomas (10/98)

Environment:

    Kernel

Revision History:

--*/
#include "precomp.h"
#pragma hdrstop


#define MODULE_ID    MODULE_CALLMGR

VOID
AtmSmCoAfRegisterNotify(
    IN  NDIS_HANDLE                 ProtocolBindingContext,
    IN  PCO_ADDRESS_FAMILY          pAddressFamily
    )
/*++

Routine Description:

    This routine is called by NDIS when a Call manager registers its support
    for an Address Family over an adapter. If this is the Address Family we
    are interested in (UNI 3.1), then we register our SAP on this adapter.

Arguments:

    ProtocolBindingContext  - our context passed in NdisOpenAdapter, which is
                              a pointer to our Adapter structure.
    pAddressFamily          - points to a structure describing the 
                              Address Family registered by a Call Manager.

Return Value:

    None

--*/
{
    PATMSM_ADAPTER              pAdapt = (PATMSM_ADAPTER)
                                                ProtocolBindingContext;
    NDIS_STATUS                 Status;
    NDIS_CLIENT_CHARACTERISTICS Chars;

    TraceIn(AtmSmCoAfRegisterNotify);

    if(!AtmSmReferenceAdapter(pAdapt))
        return;

    if ((pAddressFamily->AddressFamily   == CO_ADDRESS_FAMILY_Q2931)   &&
        (pAddressFamily->MajorVersion    == 3)                         &&
        (pAddressFamily->MinorVersion    == 1)                         &&
        (pAdapt->NdisAfHandle           == NULL))
    {
        DbgInfo(("AfNotify: Adapter %X\n", pAdapt));

        //
        // We successfully opened the adapter. Now open the address-family
        //
        pAdapt->AddrFamily.AddressFamily = CO_ADDRESS_FAMILY_Q2931;
        pAdapt->AddrFamily.MajorVersion  = 3;
        pAdapt->AddrFamily.MinorVersion  = 1;

        NdisZeroMemory(&Chars, sizeof(NDIS_CLIENT_CHARACTERISTICS));
        Chars.MajorVersion = 5;
        Chars.MinorVersion = 0;
        Chars.ClCreateVcHandler         = AtmSmCreateVc;
        Chars.ClDeleteVcHandler         = AtmSmDeleteVc;
        Chars.ClRequestHandler          = AtmSmCoRequest;
        Chars.ClRequestCompleteHandler  = AtmSmCoRequestComplete;
        Chars.ClOpenAfCompleteHandler   = AtmSmOpenAfComplete;
        Chars.ClCloseAfCompleteHandler  = AtmSmCloseAfComplete;
        Chars.ClRegisterSapCompleteHandler   = AtmSmRegisterSapComplete;
        Chars.ClDeregisterSapCompleteHandler = AtmSmDeregisterSapComplete;
        Chars.ClMakeCallCompleteHandler      = AtmSmMakeCallComplete;
        Chars.ClModifyCallQoSCompleteHandler = NULL;
        Chars.ClCloseCallCompleteHandler = AtmSmCloseCallComplete;
        Chars.ClAddPartyCompleteHandler  = AtmSmAddPartyComplete;
        Chars.ClDropPartyCompleteHandler = AtmSmDropPartyComplete;
        Chars.ClIncomingCallHandler      = AtmSmIncomingCall;
        Chars.ClIncomingCallQoSChangeHandler = AtmSmIncomingCallQoSChange;
        Chars.ClIncomingCloseCallHandler = AtmSmIncomingCloseCall;
        Chars.ClIncomingDropPartyHandler = AtmSmIncomingDropParty;
        Chars.ClCallConnectedHandler     = AtmSmCallConnected;

        Status = NdisClOpenAddressFamily(pAdapt->NdisBindingHandle,
                                         &pAdapt->AddrFamily,
                                         pAdapt,  // Use this as the Af context
                                         &Chars,
                                         sizeof(NDIS_CLIENT_CHARACTERISTICS),
                                         &pAdapt->NdisAfHandle);
        if (NDIS_STATUS_PENDING != Status)
        {
            AtmSmOpenAfComplete(Status, pAdapt, pAdapt->NdisAfHandle);
        }

    } else {

        // Not UNI3.1, hence remove the reference we added above
        AtmSmDereferenceAdapter(pAdapt);
    }

    TraceOut(AtmSmCoAfRegisterNotify);
}


VOID
AtmSmOpenAfComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolAfContext,
    IN  NDIS_HANDLE             NdisAfHandle
    )
/*++

Routine Description:

    Completion processing for the OpenAf call.

Arguments:

    Status              Status of OpenAf
    ProtocolAfContext   Pointer to the pAdapt
    NdisAfHandle        Ndis Handle to refer to this Af

Return Value:


--*/
{
    PATMSM_ADAPTER  pAdapt= (PATMSM_ADAPTER)ProtocolAfContext;

    TraceIn(AtmSmOpenAfComplete);

    if (NDIS_STATUS_SUCCESS == Status){

        pAdapt->NdisAfHandle = NdisAfHandle;

        pAdapt->ulFlags |= ADAPT_AF_OPENED;

        //
        // Now register our SAP on this interface
        //
        Status = AtmSmRegisterSap(pAdapt);

    } else {

        DbgErr(("Opening of Address Family Failed! Status - 0x%X\n", Status));
    }

    if ((Status != NDIS_STATUS_SUCCESS) && 
                (Status != NDIS_STATUS_PENDING)){

        //
        // Close Address family  (This results in a dereference)
        //
        Status = NdisClCloseAddressFamily(pAdapt->NdisAfHandle);
        if (NDIS_STATUS_PENDING != Status){

            AtmSmCloseAfComplete(Status, pAdapt);
        }

        //
        // Close Adapter (This results in a dereference)
        //
        NdisCloseAdapter(&Status, pAdapt->NdisBindingHandle);
        if (NDIS_STATUS_PENDING != Status){

            AtmSmCloseAdapterComplete(pAdapt, Status);
        }

        // if synchronous - by now the adapter structure wll be freed
    }

    TraceOut(AtmSmOpenAfComplete);
}


NDIS_STATUS
AtmSmRegisterSap(
    IN  PATMSM_ADAPTER  pAdapt
    )
/*++

Routine Description:
    Register the Sap for receiving incoming calls. 

Arguments:

Return Value:

--*/
{
    ULONG           ulSize;
    NDIS_STATUS     Status;
    PATM_SAP        pAtmSap;
    PATM_ADDRESS    pAtmAddress;

    TraceIn(AtmSmRegisterSap);

    do
    {
        //
        // Allocate memory for registering the SAP, if doing it for the 
        // first time.
        //
        ulSize = sizeof(CO_SAP) + sizeof(ATM_SAP) + sizeof(ATM_ADDRESS);

        if (NULL == pAdapt->pSap){

            AtmSmAllocMem(&pAdapt->pSap, PCO_SAP, ulSize);

        }

        if (NULL == pAdapt->pSap){

            Status = NDIS_STATUS_RESOURCES;
            DbgErr(("Failed to allocate memory for Sap\n"));
            break;
        }

        NdisZeroMemory(pAdapt->pSap, ulSize);

        pAdapt->pSap->SapType    = SAP_TYPE_NSAP;
        pAdapt->pSap->SapLength  = sizeof(ATM_SAP) + sizeof(ATM_ADDRESS);

        pAtmSap     = (PATM_SAP)pAdapt->pSap->Sap;
        pAtmAddress = (PATM_ADDRESS)(pAtmSap->Addresses);

        //
        //  Fill in the ATM SAP with default values
        //
        NdisMoveMemory(&pAtmSap->Blli, &AtmSmDefaultBlli, sizeof(ATM_BLLI_IE));
        NdisMoveMemory(&pAtmSap->Bhli, &AtmSmDefaultBhli, sizeof(ATM_BHLI_IE));

        //
        //  ATM Address to "listen" on: Wild card everything except the SEL.
        //
        pAtmSap->NumberOfAddresses  = 1;
        pAtmAddress->AddressType    = SAP_FIELD_ANY_AESA_REST;
        pAtmAddress->NumberOfDigits = ATM_ADDRESS_LENGTH;
        pAtmAddress->Address[ATM_ADDRESS_LENGTH-1]  = pAdapt->SelByte;

        Status = NdisClRegisterSap(pAdapt->NdisAfHandle,
                                   pAdapt,
                                   pAdapt->pSap,
                                   &pAdapt->NdisSapHandle);

        if (Status != NDIS_STATUS_PENDING){

            AtmSmRegisterSapComplete(Status,
                                    pAdapt,
                                    pAdapt->pSap,
                                    pAdapt->NdisSapHandle);
        }
    } while (FALSE);

    if ((Status != NDIS_STATUS_SUCCESS) && 
                (Status != NDIS_STATUS_PENDING)){

        DbgErr(("Registering Sap Failed! Status - 0x%X\n", Status));

        // cleanup done after return
    }

    TraceOut(AtmSmRegisterSap);

    return Status;
}



VOID
AtmSmRegisterSapComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolSapContext,
    IN  PCO_SAP                 pSap,
    IN  NDIS_HANDLE             NdisSapHandle
    )
/*++

Routine Description:
    Called by NDIS or us when the Sap registration completes.

Arguments:


Return Value:


--*/
{
    PATMSM_ADAPTER  pAdapt = (PATMSM_ADAPTER)ProtocolSapContext;

    TraceIn(AtmSmRegisterSapComplete);

    ASSERT (pSap == pAdapt->pSap);

    if (NDIS_STATUS_SUCCESS != Status){

        DbgErr(("RegisterSapComplete failed (%x): Adapter %x\n",
                                                        Status, pAdapt));
        AtmSmFreeMem(pAdapt->pSap);
        pAdapt->pSap = NULL;

    } else {

        ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

        pAdapt->NdisSapHandle  = NdisSapHandle;
        pAdapt->ulFlags       |= ADAPT_SAP_REGISTERED;

        RELEASE_ADAPTER_GEN_LOCK(pAdapt);

    }

    TraceOut(AtmSmRegisterSapComplete);
}


VOID
AtmSmDeregisterSapComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolSapContext
    )
/*++

Routine Description:
    Called by NDIS or us when the Sap Deregistration completes.

Arguments:


Return Value:


--*/
{
    PATMSM_ADAPTER   pAdapt = (PATMSM_ADAPTER)ProtocolSapContext;

    TraceIn(AtmSmDeregisterSapComplete);

    pAdapt->ulFlags &= ~ADAPT_SAP_REGISTERED;

    pAdapt->NdisSapHandle = NULL;

    AtmSmFreeMem(pAdapt->pSap);

    pAdapt->pSap = NULL;

    TraceOut(AtmSmDeregisterSapComplete);
}


VOID
AtmSmCloseAfComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolAfContext
    )
/*++

Routine Description:
    Called by NDIS or us when closing of AF completes.

Arguments:


Return Value:


--*/
{
    PATMSM_ADAPTER   pAdapt = (PATMSM_ADAPTER)ProtocolAfContext;

    pAdapt->ulFlags &= ~ADAPT_AF_OPENED;

    pAdapt->NdisAfHandle = NULL;

    DbgInfo(("CloseAfComplete: pAdapt %x, Flags %x, Ref %x\n",
                    pAdapt, pAdapt->ulFlags, pAdapt->ulRefCount));

    //
    // Nothing much to do except dereference the pAdapt
    //
    AtmSmDereferenceAdapter(pAdapt);
}


NDIS_STATUS
AtmSmCreateVc(
    IN  NDIS_HANDLE             ProtocolAfContext,
    IN  NDIS_HANDLE             NdisVcHandle,
    OUT PNDIS_HANDLE            ProtocolVcContext
    )
/*++

Routine Description:

    Entry point called by NDIS when the Call Manager wants to create
    a new endpoint (VC). We allocate a new VC structure, and return 
    a pointer to it as our VC context.

Arguments:

    ProtocolAfContext   - Actually a pointer to the ATMSM adapter structure
    NdisVcHandle        - Handle for this VC for all future references
    ProtocolVcContext  - Place where we (protocol) return our context for the VC

Return Value:

    NDIS_STATUS_SUCCESS if we could create a VC
    NDIS_STATUS_RESOURCES otherwise
--*/
{
    PATMSM_ADAPTER  pAdapt = (PATMSM_ADAPTER)ProtocolAfContext;
    PATMSM_VC       pVc;
    NDIS_STATUS     Status;

    DbgInfo(("CreateVc: NdisVcHandle %x, Adapt - %x\n", NdisVcHandle, pAdapt));

    *ProtocolVcContext  = NULL;


    Status = AtmSmAllocVc(pAdapt, &pVc, VC_TYPE_INCOMING, NdisVcHandle);

    if(NDIS_STATUS_SUCCESS == Status){

        *ProtocolVcContext  = pVc;
    }

    return Status;
}


NDIS_STATUS
AtmSmDeleteVc(
    IN  NDIS_HANDLE                 ProtocolVcContext
    )
/*++

Routine Description:

    Our Delete VC handler. This VC would have been allocated as a result
    of a previous entry into our CreateVcHandler, and possibly used for
    an incoming call.

    We dereference the VC here.

Arguments:

    ProtocolVcContext   - pointer to our VC structure

Return Value:

    NDIS_STATUS_SUCCESS always

--*/
{
    PATMSM_VC  pVc = (PATMSM_VC)ProtocolVcContext;

    DbgInfo(("DeleteVc: For Vc %lx\n", pVc));

    pVc->NdisVcHandle = NULL;
    AtmSmDereferenceVc(pVc);

    return NDIS_STATUS_SUCCESS;
}


NDIS_STATUS
AtmSmIncomingCall(
    IN  NDIS_HANDLE             ProtocolSapContext,
    IN  NDIS_HANDLE             ProtocolVcContext,
    IN OUT PCO_CALL_PARAMETERS  CallParameters
    )
/*++

Routine Description:

    Handler for incoming call. We accept the call unless we are shutting down 
    and then do the actual processing when the call processing completes.

Arguments:

    ProtocolSapContext      Pointer to the pAdapt
    ProtocolVcContext       Pointer to the Vc
    CallParameters          Call Parameters

Return Value:


--*/
{
    PATMSM_ADAPTER              pAdapt = (PATMSM_ADAPTER)ProtocolSapContext;
    PATMSM_VC                   pVc    = (PATMSM_VC)ProtocolVcContext;
    Q2931_CALLMGR_PARAMETERS UNALIGNED * CallMgrSpecific;

    ASSERT (pVc->pAdapt == pAdapt);

    DbgInfo(("AtmSmIncomingCall: On Vc %lx\n", pVc));

    //
    // Mark the Vc to indicate the call processing is underway
    //
    ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

    ASSERT ((pVc->ulFlags & (ATMSM_VC_ACTIVE | ATMSM_VC_CALLPROCESSING)) == 0);

    ATMSM_SET_VC_STATE(pVc, ATMSM_VC_CALLPROCESSING);

    RELEASE_ADAPTER_GEN_LOCK(pAdapt);

    //
    // Get the remote atm address from the call-parameters
    //
    CallMgrSpecific = (PQ2931_CALLMGR_PARAMETERS)&CallParameters->
                            CallMgrParameters->CallMgrSpecific.Parameters[0];

    pVc->HwAddr.Address = CallMgrSpecific->CallingParty;

    //
    // Get the max size of packets we can send on this VC, from the
    // AAL5 parameters. Limit it to the size our miniport can support.
    //
    pVc->MaxSendSize = pAdapt->MaxPacketSize; // default

    if (CallMgrSpecific->InfoElementCount > 0) {

        Q2931_IE UNALIGNED *            pIe;
        AAL5_PARAMETERS UNALIGNED *     pAal5;
        ULONG                           IeCount;

        pIe = (PQ2931_IE)CallMgrSpecific->InfoElements;
        for (IeCount = CallMgrSpecific->InfoElementCount;
             IeCount != 0;
             IeCount--) {

            if (pIe->IEType == IE_AALParameters) {

                pAal5 = &(((PAAL_PARAMETERS_IE)pIe->IE)->
                                    AALSpecificParameters.AAL5Parameters);
                //
                // Make sure we don't send more than what the caller can handle.
                //
                if (pAal5->ForwardMaxCPCSSDUSize < pVc->MaxSendSize) {

                    pVc->MaxSendSize = pAal5->ForwardMaxCPCSSDUSize;
                }

                //
                // Make sure the caller doesn't send more than what our
                // miniport can handle.
                //
                if (pAal5->BackwardMaxCPCSSDUSize > pAdapt->MaxPacketSize) {

                    pAal5->BackwardMaxCPCSSDUSize = pAdapt->MaxPacketSize;
                }
                break;
            }
            pIe = (PQ2931_IE)((PUCHAR)pIe + pIe->IELength);
        }
    }


    return NDIS_STATUS_SUCCESS;
}


VOID
AtmSmCallConnected(
    IN  NDIS_HANDLE             ProtocolVcContext
    )
/*++

Routine Description:

    Last hand-shake in the incoming call path. Move the Vc to the list of 
    active calls.

Arguments:

    ProtocolVcContext   Pointer to VC

Return Value:

    None.

--*/
{
    PATMSM_VC       pVc     = (PATMSM_VC)ProtocolVcContext;
    PATMSM_ADAPTER  pAdapt  = pVc->pAdapt;

    DbgInfo(("AtmSmCallConnected: On pVc %x  pAdapt %x\n", pVc, pAdapt));

    pAdapt = pVc->pAdapt;

    // now we add a reference to the VC
    AtmSmReferenceVc(pVc);

    ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

    ASSERT(ATMSM_GET_VC_STATE(pVc) != ATMSM_VC_ACTIVE);

    ASSERT(ATMSM_GET_VC_STATE(pVc) == ATMSM_VC_CALLPROCESSING);

    ATMSM_SET_VC_STATE(pVc, ATMSM_VC_ACTIVE);

    // remove the Vc from the inactive list
    RemoveEntryList(&pVc->List);

    // insert the vc into the active list
    InsertHeadList(&pAdapt->ActiveVcHead, &pVc->List);

    RELEASE_ADAPTER_GEN_LOCK(pAdapt);

}


VOID
AtmSmMakeCallComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolVcContext,
    IN  NDIS_HANDLE             NdisPartyHandle     OPTIONAL,
    IN  PCO_CALL_PARAMETERS     CallParameters
    )
/*++

Routine Description:

    Handle completion of an earlier call to NdisClMakeCall. If the call has
    succeeded and if this is a point to multipoint call, initiate Addparty on 
    the rest of the addresses. If it is not multipoint, then send any packets
    pending on the call.

Arguments:

    Status              Result of NdisClMakeCall
    ProtocolVcContext   Pointer to P-P or PMP Vc
    NdisPartyHandle     If successful, the handle for this party
    CallParameters      Pointer to Call parameters

Return Value:

    None.

--*/
{
    PATMSM_VC           pVc     = (PATMSM_VC)ProtocolVcContext;
    PATMSM_ADAPTER      pAdapt  = (PATMSM_ADAPTER)pVc->pAdapt;
    PATMSM_PMP_MEMBER   pMember;
    BOOLEAN             bLockReleased = FALSE;


    DbgInfo(("MakeCallComplete: Status %x, pVc %x, VC flag %x\n",
                        Status, pVc, pVc->ulFlags));

    // Free the call parameters
    AtmSmFreeMem(CallParameters);

    //
    // If this is a point to point connection and we succeeded in connecting
    // then send the packets pending on the VC.
    //
    if(VC_TYPE_PMP_OUTGOING != pVc->VcType){

        PIRP pIrp;


        if(NDIS_STATUS_SUCCESS == Status){
            // successfully connected the P-P call, send any pending packets

            ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

            ATMSM_SET_VC_STATE(pVc, ATMSM_VC_ACTIVE);

            DbgInfo(("PP VC 0x%x successfully connected to destination\n", 
                                                                         pVc));

            // now complete IRP that started this connect call
            pIrp = pVc->pConnectIrp;
            pVc->pConnectIrp = NULL;

            ASSERT(pIrp);

            // remove the Vc from the inactive list
            RemoveEntryList(&pVc->List);

            // insert the vc into the active list
            InsertHeadList(&pAdapt->ActiveVcHead, &pVc->List);

            RELEASE_ADAPTER_GEN_LOCK(pAdapt);

            if(pIrp){

                pIrp->IoStatus.Status = STATUS_SUCCESS;

                // now set the connect context
                *(PHANDLE)(pIrp->AssociatedIrp.SystemBuffer) = (HANDLE)pVc;
                pIrp->IoStatus.Information = sizeof(HANDLE);

                IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
            }

            AtmSmSendQueuedPacketsOnVc(pVc);

        } else {

            // failed to connect the call.  

            // now complete IRP that started this connect call
            PIRP pIrp = pVc->pConnectIrp;
            pVc->pConnectIrp = NULL;

            ASSERT(pIrp);

            if(pIrp){

                pIrp->IoStatus.Status = Status;

                pIrp->IoStatus.Information = 0;

                IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
            }

            DbgInfo(("PP VC 0x%x failed to connect to destination - Status - "
                "0x%x\n", pVc, Status));

            // Cleanup the VC - remove the reference added at create
            AtmSmDereferenceVc(pVc);
        }

        return;
    }


    //
    // This is the completion of the first Make call on a PMP.  If the first
    // has succeeded, we add the rest. If the first one failed, we remove it 
    // and try to make a call on another one.
    //

    ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

    //
    // Get the member we were trying to connect to.
    //
    for (pMember = pVc->pPMPMembers; pMember; pMember = pMember->pNext)
        if(ATMSM_GET_MEMBER_STATE(pMember) == ATMSM_MEMBER_CONNECTING)
            break;

    ASSERT(NULL != pMember);

    pVc->ulNumConnectingMembers--;

    if (NDIS_STATUS_SUCCESS == Status){

        ASSERT(NULL != NdisPartyHandle);

        ATMSM_SET_MEMBER_STATE(pMember, ATMSM_MEMBER_CONNECTED);

        pMember->NdisPartyHandle = NdisPartyHandle;

        pVc->ulNumActiveMembers++;

        //
        // check if the member was invalidated during the call setup
        // if so, remove this guy
        //
        if(ATMSM_IS_MEMBER_INVALID(pMember)){

            RELEASE_ADAPTER_GEN_LOCK(pAdapt);
            bLockReleased = TRUE;

            // This member was invalidated, now drop him off
            AtmSmDropMemberFromVc(pVc, pMember); 
        }
    }
    else
    {
        DbgWarn(("MakeCall error %x, pMember %x to addr:", Status, pMember));

        DumpATMAddress(ATMSMD_ERR, "", &pMember->HwAddr.Address);

        RELEASE_ADAPTER_GEN_LOCK(pAdapt);
        bLockReleased = TRUE;

        //
        // Connection failed. Delete this member from our list of members.
        //
        DeleteMemberInfoFromVc(pVc, pMember);
    }

    if(!bLockReleased) {
    
        RELEASE_ADAPTER_GEN_LOCK(pAdapt);
    }

    //
    // Add anymore members remaining
    //
    AtmSmConnectToPMPDestinations(pVc);

    return;
}


VOID
AtmSmIncomingCloseCall(
    IN  NDIS_STATUS             CloseStatus,
    IN  NDIS_HANDLE             ProtocolVcContext,
    IN  PVOID                   CloseData   OPTIONAL,
    IN  UINT                    Size        OPTIONAL
    )
/*++

Routine Description:

    Indication of an incoming close call from the network. If this
    is not an outgoing PMP VC, then we mark the VC as inactive, and
    move it to the Inactive VC list. If this is on PMP Vc,
    there must be only one party on the PMP connection. We update
    that member's state.

    In any case, we call NdisClCloseCall to complete the handshake.

Arguments:

    CloseStatus         Status of Close
    ProtocolVcContext   Pointer to VC 
    CloseData           Optional Close data (IGNORED)
    Size                Size of Optional Close Data (OPTIONAL)

Return Value:

    None

--*/
{
    PATMSM_VC           pVc             = (PATMSM_VC)ProtocolVcContext;
    PATMSM_ADAPTER      pAdapt          = pVc->pAdapt;
    NDIS_HANDLE         NdisVcHandle    = pVc->NdisVcHandle;
    NDIS_HANDLE         NdisPartyHandle;
    PATMSM_PMP_MEMBER   pMember;
    NDIS_STATUS         Status;


    if (VC_TYPE_PMP_OUTGOING != pVc->VcType){


        DbgInfo(("AtmSmIncomingCloseCall: On Vc 0x%x\n",
                                        ProtocolVcContext));

        ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

        ASSERT (ATMSM_GET_VC_STATE(pVc) != ATMSM_VC_CLOSING);

        NdisPartyHandle = NULL;


        ATMSM_SET_VC_STATE(pVc, ATMSM_VC_CLOSING);


        ASSERT(pVc->HwAddr.SubAddress == NULL);

        RemoveEntryList(&pVc->List);
        InsertHeadList(&pAdapt->InactiveVcHead, &pVc->List);

        RELEASE_ADAPTER_GEN_LOCK(pAdapt);

        pMember = NULL;

        Status = NdisClCloseCall(NdisVcHandle, NdisPartyHandle, NULL, 0);
        
        DbgInfo(("NdisCloseCall Status - 0x%X Vc - 0x%X\n", Status, pVc));

        if (Status != NDIS_STATUS_PENDING){

            AtmSmCloseCallComplete(Status, pVc, (NDIS_HANDLE)pMember);
        }

    } else {

        //
        // May be the net has gone bad
        //
        DbgInfo(("PMP IncomingCloseCall: On Vc %x\n", ProtocolVcContext));

        AtmSmDisconnectVc(pVc);
    }
}


VOID
AtmSmCloseCallComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolVcContext,
    IN  NDIS_HANDLE             ProtocolPartyContext OPTIONAL
    )
/*++

Routine Description:

    This is called to complete our call to NdisClCloseCall. If the VC
    is other than outgoing PMP, we update its state and dereference it

    If this is an outgoing PMP, we delete the last member

Arguments:

    Status                  Status of NdisClCloseCall
    ProtocolVcContext       Pointer to our VC structure
    ProtocolPartyContext    If the VC is PMP Vc, this is a pointer
                            to the Member that was disconnected.

Return Value:

    None

--*/
{
    PATMSM_VC       pVc             = (PATMSM_VC)ProtocolVcContext;
    PATMSM_ADAPTER  pAdapt          = pVc->pAdapt;
    BOOLEAN         bStopping;

    DbgInfo(("AtmSmCloseCallComplete: On Vc %lx\n", pVc));

    ASSERT(Status == NDIS_STATUS_SUCCESS);

    switch(pVc->VcType){

        case VC_TYPE_INCOMING:
        case VC_TYPE_PP_OUTGOING:

            // Both incoming call and outgoing PP calls

            ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

            ASSERT (ATMSM_GET_VC_STATE(pVc) == ATMSM_VC_CLOSING);
            
            ATMSM_SET_VC_STATE(pVc, ATMSM_VC_CLOSED);

            RELEASE_ADAPTER_GEN_LOCK(pAdapt);

            // Now dereference the VC
            AtmSmDereferenceVc(pVc);

        break;


        case VC_TYPE_PMP_OUTGOING: {

            // Outgoing PMP

            ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

            pVc->ulNumDroppingMembers--;

            ATMSM_SET_VC_STATE(pVc, ATMSM_VC_CLOSED);

            ASSERT(1 == pVc->ulNumTotalMembers);
            ASSERT((0 == pVc->ulNumActiveMembers)      &&
                   (0 == pVc->ulNumConnectingMembers)  &&
                   (0 == pVc->ulNumDroppingMembers));

            RELEASE_ADAPTER_GEN_LOCK(pAdapt);

            if(DeleteMemberInfoFromVc(pVc, 
                (PATMSM_PMP_MEMBER)ProtocolPartyContext)){

            }
        }

        break;

        default:
            ASSERT(FALSE);

    }
}


VOID
AtmSmAddPartyComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolPartyContext,
    IN  NDIS_HANDLE             NdisPartyHandle,
    IN  PCO_CALL_PARAMETERS     CallParameters
    )
/*++

Routine Description:

    Completion of NdisClAddParty to add a new party to PMP Vc.
    If successful, update the member's state. Otherwise, delete it.

Arguments:

    Status                  Status of AddParty
    ProtocolPartyContext    Pointer to Member being added
    NdisPartyHandle         Valid if AddParty successful
    CallParameters          Pointer to AddParty call parameters

Return Value:

    None

--*/
{
    PATMSM_PMP_MEMBER   pMember = (PATMSM_PMP_MEMBER)ProtocolPartyContext;
    PATMSM_VC           pVc     = (PATMSM_VC)pMember->pVc;
    PATMSM_ADAPTER      pAdapt  = (PATMSM_ADAPTER)pVc->pAdapt;
    BOOLEAN             bLockReleased = FALSE;

    // Free the memory for CallParameters
    AtmSmFreeMem(CallParameters);

    DbgLoud(("AddPartyComplete: Status %x, pMember %x, NdisPartyHandle %x\n",
                    Status, pMember, NdisPartyHandle));

    ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

    pVc->ulNumConnectingMembers--;

    if (NDIS_STATUS_SUCCESS == Status){

        ATMSM_SET_MEMBER_STATE(pMember, ATMSM_MEMBER_CONNECTED);

        ASSERT(NdisPartyHandle);

        pMember->NdisPartyHandle = NdisPartyHandle;

        pVc->ulNumActiveMembers++;

        //
        // check if the member was invalidated during the call setup
        // if so, remove this guy
        //
        if(ATMSM_IS_MEMBER_INVALID(pMember)){

            RELEASE_ADAPTER_GEN_LOCK(pAdapt);
            bLockReleased = TRUE;

            // This member was invalidated, now drop him off
            AtmSmDropMemberFromVc(pVc, pMember); 
        }

    } else {

        DbgWarn(("MakeCall error %x, pMember %x to addr:", Status, pMember));

        DumpATMAddress(ATMSMD_ERR, "", &pMember->HwAddr.Address);

        RELEASE_ADAPTER_GEN_LOCK(pAdapt);
        bLockReleased = TRUE;

        //
        // Connection failed. Delete this member from our list of members.
        //
        DeleteMemberInfoFromVc(pVc, 
            (PATMSM_PMP_MEMBER)ProtocolPartyContext);

    }

    if(!bLockReleased) {
    
        RELEASE_ADAPTER_GEN_LOCK(pAdapt);
    }

    //
    // Add anymore members remaining
    //
    AtmSmConnectToPMPDestinations(pVc);

}


VOID
AtmSmDropPartyComplete(
    IN  NDIS_STATUS             Status,
    IN  NDIS_HANDLE             ProtocolPartyContext
    )
/*++

Routine Description:

    This is called to signify completion of a previous NdisClDropParty,
    to drop a member off a PMP Vc. Delete the member.

Arguments:

    Status                  Status of DropParty
    ProtocolPartyContext    Pointer to Member being dropped

Return Value:

    None.

--*/
{
    PATMSM_PMP_MEMBER   pMember = (PATMSM_PMP_MEMBER)ProtocolPartyContext;
    PATMSM_VC           pVc     = (PATMSM_VC)pMember->pVc;
    PATMSM_ADAPTER      pAdapt  = (PATMSM_ADAPTER)pVc->pAdapt;
    BOOLEAN             IsVcClosing;

    DbgInfo(("DropPartyComplete: Vc - %x Member - %x\n", pVc, pMember));

    ASSERT(Status == NDIS_STATUS_SUCCESS);

    ACQUIRE_ADAPTER_GEN_LOCK(pAdapt);

    pVc->ulNumDroppingMembers--;

    IsVcClosing = ((ATMSM_GET_VC_STATE(pVc) == ATMSM_VC_NEED_CLOSING) &&
                   (pVc->ulNumActiveMembers == 1) &&
                   ((pVc->ulNumDroppingMembers + pVc->ulNumConnectingMembers)
                                                                       == 0));
    RELEASE_ADAPTER_GEN_LOCK(pAdapt);

    //
    // Delete this member info structure
    //
    DeleteMemberInfoFromVc(pVc, pMember);

    //
    // If this VC is closing remove the last member in it
    // (This member will issue a close call).
    //
    if(IsVcClosing){

        ASSERT(1 == pVc->ulNumTotalMembers);
        
        AtmSmDropMemberFromVc(pVc, pVc->pPMPMembers);
    }
}


VOID
AtmSmIncomingDropParty(
    IN  NDIS_STATUS             DropStatus,
    IN  NDIS_HANDLE             ProtocolPartyContext,
    IN  PVOID                   CloseData   OPTIONAL,
    IN  UINT                    Size        OPTIONAL
    )
/*++

Routine Description:

    Indication that a Member has dropped off the PMP Vc
    We complete this handshake by calling NdisClDropParty.

Arguments:

    DropStatus              Status
    ProtocolPartyContext    Pointer to Member
    CloseData               Optional Close data (IGNORED)
    Size                    Size of Optional Close Data (OPTIONAL)

Return Value:

    None

--*/
{
    PATMSM_PMP_MEMBER   pMember = (PATMSM_PMP_MEMBER)ProtocolPartyContext;
    PATMSM_VC           pVc     = (PATMSM_VC)pMember->pVc;

    ASSERT(DropStatus == NDIS_STATUS_SUCCESS);

    ASSERT(ATMSM_GET_MEMBER_STATE(pMember) == ATMSM_MEMBER_CONNECTED);

    DbgInfo(("IncomingDropParty: pVc %x, pMember %x, Addr: ", 
                                                    pVc, pMember));

    DumpATMAddress(ATMSMD_INFO, "", &pMember->HwAddr.Address);

 
    AtmSmDropMemberFromVc(pVc, pMember);
}



VOID
AtmSmIncomingCallQoSChange(
    IN  NDIS_HANDLE             ProtocolVcContext,
    IN  PCO_CALL_PARAMETERS     CallParameters
    )
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
    DbgWarn(("QoSChange: Ignored\n"));
}