/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    miniport.c

Abstract:

    ATM Ethernet PVC driver

Author:
    ADube - created
    
Revision History:


--*/

#include "precomp.h"
#pragma hdrstop


//--------------------------------------------------------------------------------
//                                                                              //
//  Global Variables used by miniports                                          //
//                                                                              //
//--------------------------------------------------------------------------------

static
NDIS_OID EthernetSupportedOids[] = {
    OID_GEN_SUPPORTED_LIST,
    OID_GEN_HARDWARE_STATUS,
    OID_GEN_MEDIA_CONNECT_STATUS,
    OID_GEN_MEDIA_SUPPORTED,
    OID_GEN_MEDIA_IN_USE,
    OID_GEN_MAXIMUM_LOOKAHEAD,
    OID_GEN_MAXIMUM_FRAME_SIZE,
    OID_GEN_MAXIMUM_TOTAL_SIZE,
    OID_GEN_MAC_OPTIONS,
    OID_GEN_PROTOCOL_OPTIONS,
    OID_GEN_LINK_SPEED,
    OID_GEN_TRANSMIT_BUFFER_SPACE,
    OID_GEN_RECEIVE_BUFFER_SPACE,
    OID_GEN_TRANSMIT_BLOCK_SIZE,
    OID_GEN_RECEIVE_BLOCK_SIZE,
    OID_GEN_MAXIMUM_SEND_PACKETS,
    OID_GEN_VENDOR_DESCRIPTION,
    OID_GEN_VENDOR_ID,
    OID_GEN_DRIVER_VERSION,
    OID_GEN_VENDOR_DRIVER_VERSION,
    OID_GEN_CURRENT_PACKET_FILTER,
    OID_GEN_CURRENT_LOOKAHEAD,
    OID_GEN_XMIT_OK,
    OID_GEN_RCV_OK,
    OID_GEN_XMIT_ERROR,
    OID_GEN_RCV_ERROR,
    OID_GEN_RCV_NO_BUFFER,
    OID_802_3_PERMANENT_ADDRESS,
    OID_802_3_CURRENT_ADDRESS,
    OID_802_3_MULTICAST_LIST,
    OID_802_3_MAXIMUM_LIST_SIZE,
    OID_802_3_RCV_ERROR_ALIGNMENT,
    OID_802_3_XMIT_ONE_COLLISION,
    OID_802_3_XMIT_MORE_COLLISIONS,
    OID_GEN_NETWORK_LAYER_ADDRESSES,
    };


MP_REG_ENTRY NICRegTable[] = {
// reg value name                  Offset in MP_ADAPTER            Field size        Default Value              Min             Max               
{NDIS_STRING_CONST("VCI"),       0, MP_OFFSET(config.vci),     MP_SIZE(config.vci),      0,                      0,              65535},
{NDIS_STRING_CONST("VPI"),       0, MP_OFFSET(config.vpi),     MP_SIZE(config.vpi),      0,                      0,              255},
{NDIS_STRING_CONST("Encap"),     0, MP_OFFSET(Encap),          MP_SIZE(Encap),           2,                      0,              3},
};
    

BOOLEAN g_bDumpPackets = FALSE;
BOOLEAN g_fDiscardNonUnicastPackets  = DISCARD_NON_UNICAST;

//-------------------------------------------------------------//
//                                                             //
// Pre defined LLC, SNAP and Other Headers for encapsulation        //
//                                                             //
//-------------------------------------------------------------//


//
// Ethernet Encapsulation
//
UCHAR LLCSnapEthernet[] = 
{
    0xaa, 0xaa,0x03, // LLC
    0x00, 0x80,0xc2, // OUI
    0x00, 0x07,      // PID
    0x00, 0x00       // PAD
};

//
// Ip v4 encapsulation
//
UCHAR LLCSnapIpv4[8] = 
{
    0xaa, 0xaa,0x03, // LLC
    0x00, 0x00,0x00, // OUI
    0x08, 0x00       // PID
};


UCHAR gPaddingBytes[MINIMUM_ETHERNET_LENGTH] = 
{
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,
    0,0,0,0

};





//--------------------------------------------------------------------------------
//                                                                              //
//  miniports   functions                                                          //
//                                                                              //
//--------------------------------------------------------------------------------


VOID
epvcReturnPacketUsingAllocation(
    IN PEPVC_I_MINIPORT pMiniport, 
    IN PNDIS_PACKET Packet,
    OUT PNDIS_PACKET *ppOriginalPacket,
    IN  PRM_STACK_RECORD        pSR

    )
/*++

Routine Description:
 Extracts the original packet 
 frees all the ndis buffers in new packet
 
 returns the original packet 
 

Arguments:


Return Value:


--*/
{   
    PNDIS_PACKET        pOrigPacket = NULL;
    PEPVC_PKT_CONTEXT   pPktContext = NULL;

    TRACE (TL_T, TM_Recv , ("==>epvcReturnPacketUsingAllocation  pMiniport %p, pPacket %p", 
                          pMiniport, 
                          Packet));

    pPktContext = (PEPVC_PKT_CONTEXT )(Packet->MiniportReservedEx);

    pOrigPacket = pPktContext->pOriginalPacket;

    if (pMiniport->fDoIpEncapsulation == TRUE)
    {
        //
        // Extract the lookaside buffer from the packet
        //
        PNDIS_BUFFER            pBuffer = Packet->Private.Head;
        PEPVC_IP_RCV_BUFFER     pIpBuffer= pPktContext ->Stack.ipv4Recv.pIpBuffer;


        if (pIpBuffer == NULL)
        {
            return ; // early return because of failure
        }
        ASSERT (pIpBuffer == NdisBufferVirtualAddress (pBuffer));

        

        //
        // Free the Lookaside Buffer
        //
        epvcFreeToNPagedLookasideList (&pMiniport->rcv.LookasideList,
                                       (PVOID)pIpBuffer);           

        
        //
        // In this case, we have allocated a new ndis buffer
        // so delete it and free the local memory
        epvcFreeBuffer (pBuffer);


        // 
        // The original packet is unchanged and well./
        //
    }
    else
    {
        //
        // This code path is used in both Ethernet and Ethernet+LLC encaps
        //

        // We only need to free the head of the packet as that was allocated
        // by us
        PNDIS_BUFFER            pNdisBuffer = Packet->Private.Head;

        if (pNdisBuffer != NULL)
        {
            epvcFreeBuffer (pNdisBuffer);
        }
    }

    
    epvcFreePacket(Packet,&pMiniport->PktPool.Recv);

    *ppOriginalPacket = pOrigPacket;


    TRACE (TL_T, TM_Recv , ("<==epvcReturnPacketUsingAllocation  pOrigPacket %p", 
                             *ppOriginalPacket));

    return;
}



VOID
epvcReturnPacketUsingStacks (
    IN PEPVC_I_MINIPORT pMiniport, 
    IN PNDIS_PACKET Packet,
    IN  PRM_STACK_RECORD        pSR

    )
/*++

Routine Description:
    
    ipv4 - Restores the orginal Head and tail to this packet
    
 

Arguments:


Return Value:


--*/
{
    PEPVC_PKT_CONTEXT   pPktContext = NULL;
    BOOLEAN Remaining = FALSE; // Unused
    PNDIS_BUFFER    pOldHead = NULL;
    PNDIS_BUFFER    pOldTail = NULL;

    TRACE (TL_T, TM_Recv , ("==>epvcReturnPacketUsingStacks pMiniport %p, pPacket %p", 
                            pMiniport, 
                            Packet));
                            
    pPktContext = (PEPVC_PKT_CONTEXT ) NdisIMGetCurrentPacketStack(Packet, &Remaining);



    if (pMiniport->fDoIpEncapsulation == TRUE)
    {
        //
        // Extract the lookaside buffer from the packet
        //
        PNDIS_BUFFER            pBuffer = Packet->Private.Head;
        PEPVC_IP_RCV_BUFFER     pIpBuffer= pPktContext ->Stack.ipv4Recv.pIpBuffer;

        if (pIpBuffer == NULL)
        {
            return; // early return
        }

        //
        // Extract the old head and tail from the packet
        //
        pOldHead = pIpBuffer->pOldHead;
        pOldTail = pIpBuffer->pOldTail;


        // check to see if we are in this code path because of a failure
        if (pOldHead == NULL)
        {
            return; // early return
        }
        ASSERT (pOldHead != NULL);
        ASSERT (pOldTail != NULL);
        
        ASSERT (&pIpBuffer->u.Byte[0] == NdisBufferVirtualAddress (pBuffer));



        // 
        // Set The original Head and Tail
        //
        Packet->Private.Head = pOldHead;
        Packet->Private.Tail = pOldTail;

        Packet->Private.ValidCounts= FALSE;

        //
        // Free the Lookaside Buffer
        //
        epvcFreeToNPagedLookasideList (&pMiniport->rcv.LookasideList,
                                       (PVOID)pIpBuffer);           

        
        //
        // In this case, we have allocated a new ndis buffer
        // so delete it and free the local memory
        epvcFreeBuffer (pBuffer);
    }
    else
    {
        //
        // This code path is used in both Ethernet and Ethernet+LLC encaps
        //
        
        //
        // We need to free the head as that was locally allocated/
        // We need to revert back to the old Head and tail stored 
        // in the context
        //
        if (pPktContext->Stack.EthLLC.pOldHead == NULL)
        {
            return ; //early return 
        }

        epvcFreeBuffer (Packet->Private.Head);

        Packet->Private.Head = pPktContext->Stack.EthLLC.pOldHead;
        Packet->Private.Tail = pPktContext->Stack.EthLLC.pOldTail;

        Packet->Private.ValidCounts= FALSE;

    }

    TRACE (TL_T, TM_Recv , ("<==epvcReturnPacketUsingStacks ",pMiniport, Packet));

    return;

}


VOID
epvcProcessReturnPacket (
    IN  PEPVC_I_MINIPORT    pMiniport,
    IN  PNDIS_PACKET        Packet,
    OUT PPNDIS_PACKET       ppOrigPacket, 
    IN  PRM_STACK_RECORD    pSR
    )
/*++

Routine Description:
 Free all the locally allocated structures in the packet (packet , mdl, memory)
 Also be able to handle failure cases

Arguments:


Return Value:


--*/
{
    ENTER("epvcProcessReturnPacket", 0x7fafa89d)
    PNDIS_PACKET pOrigPacket = NULL;
    
    TRACE (TL_T, TM_Recv , ("==>epvcProcessReturnPacket  pMiniport %p, pPacket %p", 
                          pMiniport, 
                          Packet));

    if (Packet == NULL)
    {
        return;
    }
    //
    // Packet stacking: Check if this packet belongs to us.
    //
    
    if (NdisGetPoolFromPacket(Packet) != pMiniport->PktPool.Recv.Handle)
    {
        //
        // We reused the original packet in a receive indication.
        //
        epvcReturnPacketUsingStacks (pMiniport, Packet, pSR);
        pOrigPacket = Packet;
    }
    else
    {
        //
        // This is a packet allocated from this IM's receive packet pool.
        // Reclaim our packet, and return the original to the driver below.
        //
        epvcReturnPacketUsingAllocation(pMiniport, Packet, &pOrigPacket, pSR);
    }

    //
    // Update the output variable
    //
    if (ppOrigPacket)
    {
        *ppOrigPacket = pOrigPacket;
    }
    EXIT()
}
    



VOID
EpvcReturnPacket(
    IN  NDIS_HANDLE             MiniportAdapterContext,
    IN  PNDIS_PACKET            Packet
    )
/*++

Routine Description:


Arguments:


Return Value:


--*/
{
    ENTER ("EpvcReturnPacket",0x58d2259e)
    PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext;
    PNDIS_PACKET pOrigPacket = NULL;

    RM_DECLARE_STACK_RECORD (SR);

    // Free all the locally allocated structures in the packet
    //
    epvcProcessReturnPacket (pMiniport, Packet, &pOrigPacket ,&SR);

    // Return the original packet to ndis
    //
    if (pOrigPacket != NULL)
    {
        epvcReturnPacketToNdis(pMiniport, pOrigPacket, &SR);
    }
    else
    {
        ASSERT (!"Original packet is NULL\n");
    }
    
    EXIT();

}



NDIS_STATUS
MPTransferData(
    OUT PNDIS_PACKET            Packet,
    OUT PUINT                   BytesTransferred,
    IN  NDIS_HANDLE             MiniportAdapterContext,
    IN  NDIS_HANDLE             MiniportReceiveContext,
    IN  UINT                    ByteOffset,
    IN  UINT                    BytesToTransfer
    )
/*++

Routine Description:

    Miniport's transfer data handler.

Arguments:

    Packet                  Destination packet
    BytesTransferred        Place-holder for how much data was copied
    MiniportAdapterContext  Pointer to the adapter structure
    MiniportReceiveContext  Context
    ByteOffset              Offset into the packet for copying data
    BytesToTransfer         How much to copy.

Return Value:

    Status of transfer

--*/
{
    PEPVC_I_MINIPORT pMiniport= (PEPVC_I_MINIPORT)MiniportAdapterContext;
    NDIS_STATUS Status;

    //
    // Return, if the device is OFF
    //

    if (MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == FALSE)
    {
        return NDIS_STATUS_FAILURE;
    }


    NdisTransferData(&Status,
                     pMiniport->pAdapter->bind.BindingHandle,
                     MiniportReceiveContext,
                     ByteOffset,
                     BytesToTransfer,
                     Packet,
                     BytesTransferred);

    return(Status);
}







NDIS_STATUS
MPReset(
    OUT PBOOLEAN                AddressingReset,
    IN  NDIS_HANDLE             MiniportAdapterContext
    )
/*++

Routine Description:

    Reset Handler. We just don't do anything.

Arguments:

    AddressingReset         To let NDIS know whether we need help from it with our reset
    MiniportAdapterContext  Pointer to our adapter

Return Value:


--*/
{
    PADAPT  pAdapt = (PADAPT)MiniportAdapterContext;



    *AddressingReset = FALSE;

    return(NDIS_STATUS_SUCCESS);
}


//
// The functions that do the LBFO work and bundling.
// If LBFO is turned off, then the Set Scondary API is never called and there are no bundles
//






//--------------------------------------------------------------------------------
//                                                                              //
//  Intermediate Miniports. We have one instantiation per address family.       //
//  Entry points used by the RM Apis                                            //
//                                                                              //
//                                                                              //
//                                                                              //
//--------------------------------------------------------------------------------


PRM_OBJECT_HEADER
epvcIMiniportCreate(
        PRM_OBJECT_HEADER   pParentObject,
        PVOID               pCreateParams,
        PRM_STACK_RECORD    psr
        )
/*++

Routine Description:

    Allocate and initialize an object of type EPVC_I_MINIPORT.

Arguments:

    pParentObject   - Object that is to be the parent of the adapter.
    pCreateParams   - Actually a pointer to a EPVC_I_MINIPORT_PARAMS structure,
                      which contains information required to create the adapter.

Return Value:

    Pointer to the allocated and initialized object on success.
    NULL otherwise.

--*/
{
    PEPVC_I_MINIPORT            pIM;
    PEPVC_I_MINIPORT_PARAMS     pParams = (PEPVC_I_MINIPORT_PARAMS)pCreateParams;
    NDIS_STATUS Status = NDIS_STATUS_FAILURE;
    extern RM_STATIC_OBJECT_INFO EpvcGlobals_I_MiniportStaticInfo; 

    ENTER("IMiniport Create", 0x075b24c1);

    
    TRACE (TL_V, TM_Pt, ("--> epvcIMiniportCreate") );

    EPVC_ALLOCSTRUCT(pIM, TAG_MINIPORT  );
    do
    {


        if (pIM == NULL)
        {
            break;
        }

        EPVC_ZEROSTRUCT(pIM);

        pIM->Hdr.Sig = TAG_MINIPORT;

        //
        // Do all the initialization work here
        //

        RmInitializeLock(
            &pIM->Lock,
            LOCKLEVEL_MINIPORT
            );

        RmInitializeHeader(
            pParentObject,
            &pIM->Hdr,
            TAG_MINIPORT,
            &pIM->Lock,
            &EpvcGlobals_I_MiniportStaticInfo,
            NULL,
            psr
            );

        //
        // Now initialize the adapter structure with the parameters 
        // that were passed in.
        //

        Status = epvcCopyUnicodeString(
                        &(pIM->ndis.DeviceName),
                        pParams->pDeviceName,
                        TRUE                        // Upcase
                        );

        if (FAIL(Status))
        {
            pIM->ndis.DeviceName.Buffer=NULL; // so we don't try to free it later
            break;
        }

        //
        // initialize the informational stuff on the miniport
        //
        pIM->pAdapter               = pParams->pAdapter;
        pIM->info.PacketFilter      = 0;
        pIM->info.CurLookAhead      = pParams->CurLookAhead; 
        pIM->info.NumberOfMiniports     = pParams->NumberOfMiniports;
        pIM->info.LinkSpeed         = pParams->LinkSpeed.Outbound;
        pIM->info.MediaState        = pParams->MediaState;

        
        //
        //  Start by using the real ATM card's MAC address
        //
        
        NdisMoveMemory(
            &pIM->MacAddressEth,
            &pIM->pAdapter->info.MacAddress, 
            sizeof(MAC_ADDRESS)
            );

            //
            //  Not Elan number zero so generate a locally 
            //  administered address by manipulating the first two bytes.
            //
            pIM->MacAddressEth.Byte[0] = 
                0x02 | (((UCHAR)pIM->info.NumberOfMiniports & 0x3f) << 2);
            pIM->MacAddressEth.Byte[1] = 
                (pIM->pAdapter->info.MacAddress.Byte[1] & 0x3f) | 
                ((UCHAR)pIM->info.NumberOfMiniports & 0x3f);


            pIM->info.MacAddressDummy   =   pIM->MacAddressEth;

            pIM->info.MacAddressDummy.Byte[0]++;
            
            pIM->info.MacAddressDummy.Byte[1]++;

            pIM->info.MacAddressDummy.Byte[2]++;

        
        {
            //
            // Create a Dummy Mac address  for receive indications
            //
            pIM->info.MacAddressDest = pIM->MacAddressEth;
            
            
        
        }

        {
            //
            // Create an Ethernet Header to be used
            //
            PEPVC_ETH_HEADER    pRcvEnetHeader = &pIM->RcvEnetHeader ;

            pRcvEnetHeader->eh_daddr = pIM->info.MacAddressDest;
            pRcvEnetHeader->eh_saddr  = pIM->info.MacAddressDummy;
            pRcvEnetHeader->eh_type = net_short (IP_PROT_TYPE );  

        }

        pIM->info.McastAddrCount = 0;

        Status = NDIS_STATUS_SUCCESS;


    }
    while(FALSE);

    if (FAIL(Status))
    {
        if (pIM != NULL)
        {
            epvcIMiniportDelete ((PRM_OBJECT_HEADER) pIM, psr);
            pIM = NULL;
        }
    }

    TRACE (TL_V, TM_Pt, ("<-- epvcIMiniportCreate pIMiniport. %p",pIM) );

    return (PRM_OBJECT_HEADER) pIM;
}


VOID
epvcIMiniportDelete (
    PRM_OBJECT_HEADER pObj,
    PRM_STACK_RECORD psr
    )
/*++

Routine Description:

    Free an object of type EPVC_I_MINIPORT.

Arguments:

    pHdr    - Actually a pointer to the EPVC_I_MINIPORT to be deleted.

--*/
{
    PEPVC_I_MINIPORT pMiniport = (PEPVC_I_MINIPORT) pObj;

    TRACE (TL_V, TM_Pt, ("-- epvcIMiniportDelete  pAdapter %p",pMiniport) );
    
    pMiniport->Hdr.Sig = TAG_FREED;

    EPVC_FREE   (pMiniport);
}




BOOLEAN
epvcIMiniportCompareKey(
    PVOID           pKey,
    PRM_HASH_LINK   pItem
    )
/*++

Routine Description:

    Hash comparison function for EPVC_I_MINIPORT.

Arguments:

    pKey        - Points to a Epvc Protocol object.
    pItem       - Points to EPVC_I_MINIPORT.Hdr.HashLink.

Return Value:

    TRUE IFF the key (adapter name) exactly matches the key of the specified 
    adapter object.

--*/
{
    PEPVC_I_MINIPORT pIM = NULL;
    PNDIS_STRING pName = (PNDIS_STRING) pKey;
    BOOLEAN fCompare;

    pIM  = CONTAINING_RECORD(pItem, EPVC_I_MINIPORT, Hdr.HashLink);

    //
    // TODO: maybe case-insensitive compare?
    //

    if (   (pIM->ndis.DeviceName.Length == pName->Length)
        && NdisEqualMemory(pIM->ndis.DeviceName.Buffer, pName->Buffer, pName->Length))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
    

    TRACE (TL_V, TM_Pt, ("-- epvcProtocolCompareKey pIM %p, pKey, return %x",pIM, pKey, fCompare ) );

    return fCompare;
}



ULONG
epvcIMiniportHash(
    PVOID           pKey
    )
/*++

Routine Description:

    Hash function responsible for returning a hash of pKey, which
    we expect to be a pointer to an Epvc Protocol block.

Return Value:

    ULONG-sized hash of the string.
    

--*/
{
    TRACE(TL_T, TM_Mp, ("epvcIMiniportHash %x", pKey));
    {   
        PNDIS_STRING pName = (PNDIS_STRING) pKey;
        WCHAR *pwch = pName->Buffer;
        WCHAR *pwchEnd = pName->Buffer + pName->Length/sizeof(*pwch);
        ULONG Hash  = 0;


        for (;pwch < pwchEnd; pwch++)
        {
            Hash ^= (Hash<<1) ^ *pwch;
        }
        
        return Hash;
    }
    
}





NDIS_STATUS
epvcTaskVcSetup(
    IN  struct _RM_TASK *           pTask,
    IN  RM_TASK_OPERATION           Code,
    IN  UINT_PTR                    UserParam,
    IN  PRM_STACK_RECORD            pSR
    )
/*++

Routine Description:


Arguments:
    
    UserParam   for (Code ==  RM_TASKOP_START)          : UnbindContext

--*/
{

    ENTER("epvcTaskVcSetup", 0x64085960)
    NDIS_STATUS         Status      = NDIS_STATUS_FAILURE;
    PEPVC_I_MINIPORT    pMiniport   = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask);
    PTASK_VC            pTaskVc     = (PTASK_VC) pTask;
    PEPVC_ADAPTER       pAdapter    = (PEPVC_ADAPTER)pMiniport->Hdr.pParentObject;
    NDIS_HANDLE         NdisVcHandle = NULL;
    PCO_CALL_PARAMETERS pCallParameters = NULL;


    enum 
    {
        Stage_Start =0, // default
        Stage_CreateVc,
        Stage_MakeCall,
        Stage_DeleteVc, // in case of failure
        Stage_TaskCompleted,
        Stage_End       
    
    }; // To be used in pTask->Hdr.State to indicate the state of the Task



    
    TRACE ( TL_T, TM_Pt, ("==> epvcTaskVcSetup %x",pTask->Hdr.State  ) );

    switch (pTask->Hdr.State)
    {
        case Stage_Start:
        {
            LOCKOBJ (pMiniport, pSR);
            
            if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->vc.pTaskVc)) == FALSE)
            {
                PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->vc.pTaskVc);
                
                RmTmpReferenceObject (&pOtherTask->Hdr, pSR);

                //
                // Set The state so we restart this code after main task completes 
                //

                pTask->Hdr.State = Stage_Start;
                UNLOCKOBJ(pMiniport, pSR);

                

                RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR);

                RmTmpDereferenceObject(&pOtherTask->Hdr,pSR);
                Status = NDIS_STATUS_PENDING;
                break;
            }

            //
            // We are the primary task
            //
            ASSERT (pMiniport->vc.pTaskVc == pTaskVc);
            //
            // Check to see if our work is already done
            //
            if (MiniportTestFlag(pMiniport,  fMP_MakeCallSucceeded) == TRUE)
            {
                //
                // Our work had been done. So break out and complete the task
                //
                Status = NDIS_STATUS_SUCCESS;
                pTaskVc->ReturnStatus = NDIS_STATUS_SUCCESS;

                
                pTask->Hdr.State = Stage_TaskCompleted;
                UNLOCKOBJ(pMiniport, pSR);
                break;
            }

            MiniportClearFlag (pMiniport,fMP_InfoCallClosed);
            MiniportSetFlag (pMiniport, fMP_InfoMakingCall);

            UNLOCKOBJ(pMiniport, pSR);

            //
            // Now begin the real work
            //

            //
            // Set up the call parameters. If it fails ,then exit
            //
            epvcSetupMakeCallParameters(pMiniport, &pCallParameters);

            if (pCallParameters  == NULL)
            {
                Status = NDIS_STATUS_FAILURE;
                pTaskVc->ReturnStatus = NDIS_STATUS_FAILURE;
                pTask->Hdr.State = Stage_TaskCompleted;
                break;
            
            }
            //
            // Create Vc - Syncronous call
            // 
            ASSERT (pAdapter->Hdr.Sig = TAG_ADAPTER);
            
            Status  = epvcCoCreateVc(pAdapter->bind.BindingHandle,
                                    pMiniport->af.AfHandle      OPTIONAL,   // For CM signalling VCs
                                    pMiniport,
                                    &NdisVcHandle);
                                    
            ASSERT (PEND(Status) == FALSE); // this is a synchronous call

            if (FAIL(Status) == TRUE)
            {       
                //
                // We have failed. This task is done. There are not
                // resources to be freed, although a flag has to be 
                // cleared
                //
                NdisVcHandle = NULL;
                pMiniport->vc.VcHandle = NULL;

                pTask->Hdr.State = Stage_TaskCompleted;
                break;
            }

            ASSERT (Status == NDIS_STATUS_SUCCESS);
            //
            // Store the Vc Handle
            //
            LOCKOBJ (pMiniport, pSR);

            pMiniport->vc.VcHandle = NdisVcHandle;
            epvcLinkToExternal( &pMiniport->Hdr,
                             0xf52962f1,
                             (UINT_PTR)pMiniport->vc.VcHandle,
                             EPVC_ASSOC_MINIPORT_OPEN_VC,
                             "    VcHandle %p\n",
                             pSR);


            UNLOCKOBJ (pMiniport, pSR);


    
            //
            // Do a Make Call
            //
            pTask->Hdr.State  = Stage_MakeCall;


            RmSuspendTask(pTask, 0, pSR);
            
            Status = epvcClMakeCall(NdisVcHandle,
                                 pCallParameters,
                                 NULL,  //Party Context
                                 NULL // PartyHandle
                                 );
                                 
            if (NDIS_STATUS_PENDING !=Status)
            {
                EpvcCoMakeCallComplete(Status,
                                      pMiniport,
                                      NULL,
                                      0);
                
                
            }
            break;  
        }
        case Stage_MakeCall:
        {
            //
            // The make call has been completed. 
            // If we have succeeded then we update our flags 
            // and exit.
            //
            // If the make call has failed, then I need to delete the VC
            //

            ASSERT (NDIS_STATUS_CALL_ACTIVE  != pTaskVc->ReturnStatus);
            
            if (NDIS_STATUS_SUCCESS == pTaskVc->ReturnStatus)
            {
                LOCKOBJ(pMiniport, pSR);

                MiniportSetFlag (pMiniport, fMP_MakeCallSucceeded);
                MiniportClearFlag (pMiniport, fMP_InfoMakingCall);

    
                UNLOCKOBJ (pMiniport, pSR);

                
            
            }
            else
            {
                NDIS_HANDLE VcHandle = NULL;
                //
                // Delete the VC, as we do not want a VC without an active 
                // Make call on it.
                //
                ASSERT (NDIS_STATUS_SUCCESS == pTaskVc->ReturnStatus);              
                                        
                LOCKOBJ(pMiniport, pSR);

                VcHandle = pMiniport->vc.VcHandle;
                
                epvcUnlinkFromExternal( &pMiniport->Hdr,
                                        0xa914405a,
                                        (UINT_PTR)pMiniport->vc.VcHandle,
                                        EPVC_ASSOC_MINIPORT_OPEN_VC, 
                                        pSR);

                pMiniport->vc.VcHandle = NULL;

                UNLOCKOBJ (pMiniport, pSR);

                TRACE (TL_I, TM_Mp,("Deleting Vc because of a failure in MakeCall"));

                Status = epvcCoDeleteVc(VcHandle);
                
                //
                // TODO: Fix Failure case
                //
                ASSERT (NDIS_STATUS_SUCCESS == Status );

                
            
                
            }

            //
            // This task is over. Now do the indications
            //
            pTask->Hdr.State = Stage_TaskCompleted;

            Status = NDIS_STATUS_SUCCESS;
            break;
        }

        case Stage_End:
        {
            Status = NDIS_STATUS_SUCCESS;
            break;
        }
        default:
        {
            ASSERTEX(!"Unknown task op", pTask);
        }
        

    } // end of switch 

    if ( Stage_TaskCompleted == pTask->Hdr.State )
    {

        pTask->Hdr.State = Stage_End;

        ASSERT (NDIS_STATUS_PENDING !=Status );

        //
        // Do any cleanup indications to NDIS over here
        //
        epvcVcSetupDone ( pTaskVc, pMiniport);

        LOCKOBJ(pMiniport, pSR);

        pMiniport->vc.pTaskVc = NULL;
    
        UNLOCKOBJ (pMiniport, pSR);

        

    }

    TRACE ( TL_T, TM_Mp, ("<== epvcTaskVcSetup , Status %x",Status) );

    RM_ASSERT_NOLOCKS(pSR);
    EXIT()
    return Status;          

}


VOID
epvcVcSetupDone (
    PTASK_VC pTaskVc, 
    PEPVC_I_MINIPORT pMiniport
    )
/*++

Routine Description:

    If the task was queued because of SetPacket Filter request then 
    this function completes the request.

    If the task was run because of the Indicate Media Connect event, then
    this thread indicates a Media Connect to NDIS

Arguments:
    Status  - Did the VcSetup Succeed or Fail
    pTaskVc - Task in question
    pMiniport - the Miniport that the task operated on
    
Return Value:

    None:
    
--*/
    
{


    if (TaskCause_NdisRequest == pTaskVc->Cause )
    {
        //
        // Since requests are serialized, we don't acquire the lock
        //
        TRACE (TL_V, TM_Rq, ("Completing SetPacketFilter Request %x", pTaskVc->ReturnStatus ));

        if (pTaskVc->ReturnStatus == NDIS_STATUS_SUCCESS)
        {
            pMiniport->info.PacketFilter = pTaskVc->PacketFilter;
        }
        NdisMSetInformationComplete (pMiniport->ndis.MiniportAdapterHandle, pTaskVc->ReturnStatus);

    }
    else
    {
        ASSERT (TaskCause_MediaConnect == pTaskVc->Cause );

        pMiniport->info.MediaState = NdisMediaStateConnected;
        
        NdisMIndicateStatus ( pMiniport->ndis.MiniportAdapterHandle,
                              NDIS_STATUS_MEDIA_CONNECT,
                              NULL,
                              0);
    }


}



NDIS_STATUS
epvcTaskVcTeardown(
    IN  struct _RM_TASK *           pTask,
    IN  RM_TASK_OPERATION           Code,
    IN  UINT_PTR                    UserParam,
    IN  PRM_STACK_RECORD            pSR
    )
/*++

Routine Description:


Arguments:
    
    UserParam   for (Code ==  RM_TASKOP_START)          : UnbindContext

--*/
{

    ENTER("epvcTaskVcTeardown", 0x68c96c4d)
    NDIS_STATUS         Status      = NDIS_STATUS_FAILURE;
    PEPVC_I_MINIPORT    pMiniport   = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask);
    PTASK_VC            pTaskVc     = (PTASK_VC) pTask;
    PEPVC_ADAPTER       pAdapter    = (PEPVC_ADAPTER)pMiniport->Hdr.pParentObject;
    NDIS_HANDLE         NdisVcHandle = NULL;
    PCO_CALL_PARAMETERS pCallParameters = NULL;


    enum 
    {
        Stage_Start =0, // default
        Stage_CloseCallComplete,
        Stage_DeleteVc, 
        Stage_TaskCompleted,
        Stage_End
    
    }; // To be used in pTask->Hdr.State to indicate the state of the Task

    TRACE ( TL_T, TM_Pt, ("==> epvcTaskVcTeardown %x",pTask->Hdr.State  ) );

    switch (pTask->Hdr.State)
    {
        case Stage_Start:
        {
            LOCKOBJ (pMiniport, pSR);
            
            if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->vc.pTaskVc)) == FALSE)
            {
                PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->vc.pTaskVc);
                
                RmTmpReferenceObject (&pOtherTask->Hdr, pSR);

                //
                // Set The state so we restart this code after main task completes 
                //

                pTask->Hdr.State = Stage_Start;
                UNLOCKOBJ(pMiniport, pSR);

                

                RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR);

                RmTmpDereferenceObject(&pOtherTask->Hdr,pSR);
                Status = NDIS_STATUS_PENDING;
                break;
            }

            //
            // We are the primary task
            //
            ASSERT (pMiniport->vc.pTaskVc == pTaskVc);
            //
            // Check to see if our work is already done
            //
            if (MiniportTestFlag(pMiniport,  fMP_MakeCallSucceeded) == FALSE)
            {
                //
                // Our work had been done. So break out and complete the task
                //
                Status = NDIS_STATUS_SUCCESS;
                pTask->Hdr.State = Stage_TaskCompleted;
                UNLOCKOBJ(pMiniport, pSR);
                break;
            }

            
            MiniportClearFlag (pMiniport, fMP_MakeCallSucceeded);
            MiniportSetFlag (pMiniport, fMP_InfoClosingCall);
    
            UNLOCKOBJ(pMiniport, pSR);

            //
            // Now close the call - Asynchronously. 
            //
            pTask->Hdr.State = Stage_CloseCallComplete;

            RmSuspendTask (pTask, 0, pSR);
            
            Status = epvcClCloseCall( pMiniport->vc.VcHandle);

            if (NDIS_STATUS_PENDING != Status)
            {
                EpvcCoCloseCallComplete (Status,
                                         pMiniport,
                                         NULL
                                         );
                
            }

            Status = NDIS_STATUS_PENDING;
            break;
        }

        case Stage_CloseCallComplete:
        {
            NDIS_HANDLE VcHandle = NULL;
            
            LOCKOBJ(pMiniport, pSR);

            VcHandle = pMiniport->vc.VcHandle;
            
            epvcUnlinkFromExternal(&pMiniport->Hdr,
                                   0x5d7b5ea8,
                                   (UINT_PTR)pMiniport->vc.VcHandle,
                                   EPVC_ASSOC_MINIPORT_OPEN_VC,
                                   pSR);

            pMiniport->vc.VcHandle = NULL;
            
            UNLOCKOBJ(pMiniport, pSR);

            Status = epvcCoDeleteVc(VcHandle);
            //
            // This is an assertion because the DeleteVc cannot fail.
            // We do a DeleteVc in one place only and it is serialized.
            //

            ASSERT (Status == NDIS_STATUS_SUCCESS);

            pTask->Hdr.State = Stage_TaskCompleted;

            break;
        }

        case Stage_End:
        {
            Status = NDIS_STATUS_SUCCESS;
            break;
        }
        default:
        {
            ASSERTEX(!"Unknown task op", pTask);
        }
        
        
    }


    if (Stage_TaskCompleted == pTask->Hdr.State )
    {
        pTask->Hdr.State  = Stage_End;

        //
        // Complete the request or the Media Disconnect;
        //
        epvcVcTeardownDone(pTaskVc, pMiniport);

        LOCKOBJ (pMiniport, pSR);

        //
        // Update informational flags
        //
        MiniportClearFlag (pMiniport, fMP_InfoClosingCall);
        MiniportSetFlag (pMiniport, fMP_InfoCallClosed);

        pMiniport->vc.pTaskVc = NULL;
    
        
        UNLOCKOBJ(pMiniport, pSR);
    }
    TRACE ( TL_T, TM_Mp, ("<== epvcTaskVcTeardown , Status %x",Status) );

    RM_ASSERT_NOLOCKS(pSR);
    EXIT()
    return Status;          

}



VOID
epvcVcTeardownDone(
    PTASK_VC pTaskVc, 
    PEPVC_I_MINIPORT pMiniport
    )
{
    TRACE ( TL_T, TM_Mp, ("==> epvcVcTeardownDone ") );

    switch (pTaskVc->Cause)
    {
        case TaskCause_NdisRequest:
        {

            ASSERT (pTaskVc->ReturnStatus != NDIS_STATUS_PENDING);

            //
            // Since requests are serialized, we don't acquire the lock
            //
            pMiniport->info.PacketFilter = pTaskVc->PacketFilter;

            NdisMSetInformationComplete(pMiniport->ndis.MiniportAdapterHandle,
                                        pTaskVc->ReturnStatus);
            
            break;
        }
        case TaskCause_MediaDisconnect:
        {
        
            pMiniport->info.MediaState = NdisMediaStateDisconnected;
            
            epvcMIndicateStatus ( pMiniport,
                                  NDIS_STATUS_MEDIA_DISCONNECT,
                                  NULL,
                                  0);
            break;
        }

        default:
        {
            // Do nothing.
            //
        }
        




    }

    
    


    TRACE ( TL_T, TM_Mp, ("<== epvcVcTeardownDone ") );

}





NDIS_STATUS
EpvcInitialize(
    OUT PNDIS_STATUS            OpenErrorStatus,
    OUT PUINT                   SelectedMediumIndex,
    IN  PNDIS_MEDIUM            MediumArray,
    IN  UINT                    MediumArraySize,
    IN  NDIS_HANDLE             MiniportAdapterHandle,
    IN  NDIS_HANDLE             WrapperConfigurationContext
    )
/*++

Routine Description:

    This is the initialize handler which gets called as a result of the BindAdapter handler
    calling NdisIMInitializeDeviceInstanceEx(). The context parameter which we pass there is
    the adapter structure which we retreive here. We also need to initialize the Power Management
    variable.
    LoadBalalncing- We keep a global list of all the passthru miniports installed and bundle
    two of them together if they have the same BundleId (read from registry)

    Arguments:

    OpenErrorStatus         Not used by us.
    SelectedMediumIndex     Place-holder for what media we are using
    MediumArray             Array of ndis media passed down to us to pick from
    MediumArraySize         Size of the array
    MiniportAdapterHandle   The handle NDIS uses to refer to us
    WrapperConfigurationContext For use by NdisOpenConfiguration

Return Value:

    NDIS_STATUS_SUCCESS unless something goes wrong

--*/
{
    ENTER ("EpvcInitialize", 0xa935a2a5)
    UINT    i;
    PEPVC_I_MINIPORT                pMiniport = NULL;
    NDIS_STATUS                     Status = NDIS_STATUS_FAILURE;
    KIRQL                           OldIrql;
    
    enum 
    {
        Stage_Start,
        Stage_AllocatedPacketPools,
        Stage_AllocatedLookasideLists
    };

    ULONG                           State = Stage_Start;

    RM_DECLARE_STACK_RECORD (SR);
    
    TRACE (TL_T, TM_Mp, ("==>EpvcInitialize MiniportAdapterHandle %x", MiniportAdapterHandle));

    //
    // Start off by retrieving the adapter context and storing the Miniport handle in it
    //
    pMiniport = NdisIMGetDeviceContext(MiniportAdapterHandle);

    if (pMiniport->Hdr.Sig != TAG_MINIPORT)
    {
        ASSERT (pMiniport->Hdr.Sig == TAG_MINIPORT);
        return NDIS_STATUS_FAILURE;
    }
    
    pMiniport->ndis.MiniportAdapterHandle  = MiniportAdapterHandle;

    //
    // Make sure the medium saved is one of the ones being offered
    //

    for (i = 0; i < MediumArraySize; i++)
    {
        if (MediumArray[i] == ATMEPVC_MP_MEDIUM )
        {
            *SelectedMediumIndex = i;
            break;
        }
    }

    if (i == MediumArraySize)
    {
        return(NDIS_STATUS_UNSUPPORTED_MEDIA);
    }


    //
    // Set the attributes now. The NDIS_ATTRIBUTE_DESERIALIZE is the key. This enables us
    // to make up-calls to NDIS w/o having to call NdisIMSwitchToMiniport/NdisIMQueueCallBack.
    // This also forces us to protect our data using spinlocks where appropriate. Also in this
    // case NDIS does not queue packets on out behalf. Since this is a very simple pass-thru
    // miniport, we do not have a need to protect anything. However in a general case there
    // will be a need to use per-adapter spin-locks for the packet queues at the very least.
    //
    NdisMSetAttributesEx(MiniportAdapterHandle,
                         pMiniport,
                         0,                                     // CheckForHangTimeInSeconds
                         NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT   |
                            NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT|
                            NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER |
                            NDIS_ATTRIBUTE_DESERIALIZE,                         
                         0);


    //
    // We are done, with the no failure stuff. From now on we need to undo
    //

    do
    {

        Status = epvcMiniportReadConfig(pMiniport, WrapperConfigurationContext,&SR  );

        if (Status != NDIS_STATUS_SUCCESS)
        {
            //
            // Undo Configuration values
            // 
            ASSERT (Status == NDIS_STATUS_SUCCESS);
            break;

        }

        epvcInitializeMiniportParameters(pMiniport);

        
        //
        // allocate Packet pools.
        //

        Status = epvcInitializeMiniportPacketPools (pMiniport);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            ASSERT (Status == NDIS_STATUS_SUCCESS);
            break;
        }

        State = Stage_AllocatedPacketPools;
 

        //
        // Allocate lookaside lists
        //

        epvcInitializeMiniportLookasideLists(pMiniport);


        State = Stage_AllocatedLookasideLists;


        Status = NDIS_STATUS_SUCCESS;

    } while (FALSE);
    

    TRACE (TL_T, TM_Mp, ("<==EpvcInitialize pMiniport %x, Status %x", pMiniport, Status ));

    if (Status == NDIS_STATUS_SUCCESS)
    {
        BOOLEAN fSetDeInit = FALSE;
        
        LOCKOBJ(pMiniport, &SR);
        MiniportSetFlag(pMiniport, ,fMP_MiniportInitialized);

        if (MiniportTestFlag (pMiniport, fMP_MiniportCancelInstance))
        {
            fSetDeInit = TRUE;
        }
        UNLOCKOBJ(pMiniport, &SR);

        //
        // Check to see if we have a DeInit Waiting for us.
        // This will only be set if a Cancel Device Instance fails.
        //
        if (fSetDeInit  == TRUE)
        {
            epvcSetEvent (&pMiniport->pnp.DeInitEvent);
        }
    }
    else
    {
        //
        // Undo Code
        //
        ASSERT (FAIL(Status) == TRUE);
        
        switch (State)
        {

            case Stage_AllocatedLookasideLists:

                epvcDeleteMiniportLookasideLists (pMiniport);

                FALL_THROUGH
                
            case Stage_AllocatedPacketPools:

                epvcDeleteMiniportPacketPools(pMiniport);
                FALL_THROUGH


            default:
                break;



        }



    }


    RM_ASSERT_CLEAR(&SR);
    EXIT();
    return Status;
}


VOID
EpvcHalt(
    IN  NDIS_HANDLE             MiniportAdapterContext
    )
/*++

Routine Description:

    Halt handler. All the hard-work for clean-up is done here.

Arguments:

    MiniportAdapterContext  Pointer to the Adapter

Return Value:

    None.

--*/
{
    ENTER("EpvcHalt",0x6b407ae1)
    PEPVC_I_MINIPORT    pMiniport   = (PEPVC_I_MINIPORT)MiniportAdapterContext;
    PEPVC_ADAPTER       pAdapter    = pMiniport->pAdapter;
    PRM_TASK            pTask       = NULL;
    NDIS_STATUS         Status      = NDIS_STATUS_FAILURE;

    RM_DECLARE_STACK_RECORD (SR);
    
    TRACE (TL_V, TM_Mp, ("==>Epvc MPHaltMiniport"));

    do
    {
        LOCKOBJ (pMiniport, &SR);
        //
        // Clear the flag so we can block all sends/receives/requests
        //
        MiniportClearFlag(pMiniport, fMP_MiniportInitialized);
        MiniportSetFlag(pMiniport, fMP_InfoHalting);                    
    
        //
        // Ref the miniport, this indirectly refs the adpater as well
        //
        RmTmpReferenceObject (&pMiniport->Hdr, &SR);

        //
        // Kick of the miniport halt task and wait for it to complete
        //
        Status = epvcAllocateTask(
                &pMiniport->Hdr,            // pParentObject,
                epvcTaskHaltMiniport,   // pfnHandler,
                0,                          // Timeout,
                "Task: Halt Intermediate Miniport", // szDescription
                &pTask,
                &SR
                );

        if (FAIL(Status))
        {
            pTask = NULL;
            break;
        }

        //
        // Reference the task so it is around until our Wait for completion
        // is complete
        //
        RmTmpReferenceObject (&pTask->Hdr, &SR);

        UNLOCKOBJ (pMiniport, &SR);

        //
        // This Kicks of the task that will close the Call, Delete
        // the VC and close the AF. We do this all synchronously
        //
        {
            PTASK_HALT pHalt = (PTASK_HALT) pTask;
            
            epvcInitializeEvent (&pHalt->CompleteEvent);
            
            RmStartTask(pTask, 0, &SR);

            TRACE (TL_V, TM_Mp, ("About to Wait - for Halt Complete Event"));

            epvcWaitEvent (&pHalt->CompleteEvent, WAIT_INFINITE);

            TRACE (TL_V, TM_Mp, ("Wait Complete- for Halt Complete Event"));


        }       

        LOCKOBJ (pMiniport, &SR);

        //
        // Deref the task . Ref was made above.
        //
        
        RmTmpDereferenceObject (&pTask->Hdr, &SR);


    } while (FALSE);    


    MiniportClearFlag(pMiniport, fMP_InfoHalting);

    UNLOCKOBJ(pMiniport, &SR);

    RmTmpDereferenceObject(&pMiniport->Hdr, &SR);


    RM_ASSERT_CLEAR(&SR);

    TRACE (TL_V, TM_Mp, ("<==Epvc MPHaltMiniport"));

}



VOID    
epvcSetPacketFilterWorkItem (
    PNDIS_WORK_ITEM  pWorkItem, 
    PVOID Context
    )
/*++
Routine Description:

    Decrements the refcount on the filter and processes the new packet filter
    

Return Value:

    None
    
--*/
{
    ENTER ("epvcSetPacketFilterWorkItem  ",0x3e1cdbba )
    PEPVC_I_MINIPORT    pMiniport = NULL;
    PRM_TASK            pTask = NULL;
    NDIS_STATUS         Status = NDIS_STATUS_FAILURE;
    UINT                Filter ;
    
    RM_DECLARE_STACK_RECORD (SR);


    do
    {
        pMiniport = CONTAINING_RECORD (pWorkItem, 
                                       EPVC_I_MINIPORT,
                                       vc.PacketFilterWorkItem) ;

        //
        // Dereference the workitem off the miniport 
        //
            

        epvcUnlinkFromExternal( &pMiniport->Hdr,
                             0xa1f5e3cc,
                             (UINT_PTR)pWorkItem,
                             EPVC_ASSOC_SET_FILTER_WORKITEM,
                             &SR);

        //
        // Start the task to create or delete the VC
        //
        Filter = pMiniport->vc.NewFilter ;
        //
        // If this is a repition, then succeed it synchronously
        //
        if (Filter  == pMiniport->info.PacketFilter)
        {
            Status = NDIS_STATUS_SUCCESS;
            break;
        }

        LOCKOBJ(pMiniport, &SR);

        //
        // Are we moving to a Zero filter value
        //

        if (Filter  == 0)
        {
            //
            // Delete the Vc, so that we stop doing any receives
            // 

            Status = epvcAllocateTask(
                &pMiniport->Hdr,            // pParentObject,
                epvcTaskVcTeardown, // pfnHandler,
                0,                          // Timeout,
                "Task: Delete Vc",  // szDescription
                &pTask,
                &SR
                );


        }
        else
        {
            //
            // We are moving a non-zero values
            //

            //
            // Create the Vc, so that we can send 
            // 

            Status = epvcAllocateTask(
                &pMiniport->Hdr,            // pParentObject,
                epvcTaskVcSetup,    // pfnHandler,
                0,                          // Timeout,
                "Task: Create Vc",  // szDescription
                &pTask,
                &SR
                );



        }

        UNLOCKOBJ(pMiniport, &SR);
        
        if (FAIL(Status) == TRUE)
        {
            // Ugly situation. We'll just leave things as they are...
            //
            pTask = NULL;
            TR_WARN(("FATAL: couldn't allocate create/ delete Vc task!\n"));
            ASSERT (0);
            break;
        }
        


        //
        // Update the cause if the task
        //
        
        ((PTASK_VC)pTask)->Cause = TaskCause_NdisRequest;
        ((PTASK_VC)pTask)->PacketFilter  = Filter  ;
        
        RmStartTask(pTask, 0, &SR);

        Status = NDIS_STATUS_PENDING;

    } while (FALSE);

    //
    // complete the request if the task has not been started
    //
    if (PEND(Status) != TRUE)
    {
        NdisMSetInformationComplete (pMiniport->ndis.MiniportAdapterHandle, Status);

    }

    EXIT();
}




NDIS_STATUS
epvcSetPacketFilter(
    IN PEPVC_I_MINIPORT pMiniport,
    IN ULONG Filter,
    PRM_STACK_RECORD pSR
    )

/*++
Routine Description:

    This routine is called when a miniport get a set packet filter.
    It validates the arguments, If all is well then it process the request

    For a non-zero filter, a create VC and a Make call is done.
    For a zero filter, the call is closed and the Vc Deleted

Return Value:

    NDIS_STATUS_SUCCESS unless something goes wrong

--*/
{
    ENTER ("epvcSetPacketFilter", 0x97c6b961)
    NDIS_STATUS Status = NDIS_STATUS_PENDING;
    PNDIS_WORK_ITEM pSetFilterWorItem = &pMiniport->vc.PacketFilterWorkItem; 
    PRM_TASK pTask = NULL;

    
    TRACE (TL_T, TM_Mp, ("==>epvcSetPacketFilter Filter %X", Filter ));

    do
    {
        LOCKOBJ (pMiniport, pSR);

        epvcLinkToExternal( &pMiniport->Hdr,
                             0x20bc1fbf,
                             (UINT_PTR)pSetFilterWorItem,
                             EPVC_ASSOC_SET_FILTER_WORKITEM,
                             "    PacketFilterWorkItem %p\n",
                             pSR);

        //
        // Update the cause of the task
        //
        UNLOCKOBJ(pMiniport, pSR);


        //
        // Now schedule the work item so it runs at passive level and pass the Vc as
        // an argument
        //

        pMiniport->vc.NewFilter = Filter;
        
        NdisInitializeWorkItem ( pSetFilterWorItem , 
                             (NDIS_PROC)epvcSetPacketFilterWorkItem ,
                             (PVOID)pTask );

                            

        NdisScheduleWorkItem (pSetFilterWorItem );

            

        Status = NDIS_STATUS_PENDING;


    } while (FALSE);
    


    TRACE (TL_T, TM_Mp, ("<==epvcSetPacketFilter %x", Status));

    EXIT();
    return Status;
}



NDIS_STATUS 
EpvcMpQueryInformation(
    IN  NDIS_HANDLE             MiniportAdapterContext,
    IN  NDIS_OID                Oid,
    IN  PVOID                   InformationBuffer,
    IN  ULONG                   InformationBufferLength,
    OUT PULONG                  BytesWritten,
    OUT PULONG                  BytesNeeded
)
/*++

Routine Description:

    The QueryInformation Handler for the virtual miniport.

Arguments:

    MiniportAdapterContext  - a pointer to the Elan.

    Oid                     - the NDIS_OID to process.

    InformationBuffer       - a pointer into the NdisRequest->InformationBuffer
                              into which store the result of the query.

    InformationBufferLength - a pointer to the number of bytes left in the
    InformationBuffer.

    BytesWritten            - a pointer to the number of bytes written into the
    InformationBuffer.

    BytesNeeded             - If there is not enough room in the information
                              buffer then this will contain the number of bytes
                              needed to complete the request.

Return Value:

    The function value is the status of the operation.

--*/
{
    ENTER ("EpvcMpQueryInformation", 0x3da2473b)
    UINT                    BytesLeft       = InformationBufferLength;
    PUCHAR                  InfoBuffer      = (PUCHAR)(InformationBuffer);
    NDIS_STATUS             StatusToReturn  = NDIS_STATUS_SUCCESS;
    NDIS_HARDWARE_STATUS    HardwareStatus  = NdisHardwareStatusReady;
    NDIS_MEDIUM             Medium;
    PEPVC_I_MINIPORT        pMiniport = NULL;   
    PEPVC_ADAPTER           pAdapter= NULL;
    ULONG                   GenericULong =0;
    USHORT                  GenericUShort=0;
    UCHAR                   GenericArray[6];
    UINT                    MoveBytes       = sizeof(ULONG);
    PVOID                   MoveSource      = (PVOID)(&GenericULong);
    ULONG                   i=0;
    BOOLEAN                 IsShuttingDown = FALSE;
    RM_DECLARE_STACK_RECORD (SR);
        
    TRACE(TL_T, TM_Rq, ("==>EpvcMpQueryInformation pMiniport %x, Oid, Buffer %x, Length, %x",
                       pMiniport,
                       Oid,
                       InformationBuffer,
                       InformationBufferLength));               

    pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext;


    LOCKOBJ(pMiniport, &SR);
    IsShuttingDown = (! MiniportTestFlag(pMiniport, fMP_MiniportInitialized));
    pAdapter = pMiniport->pAdapter;
    UNLOCKOBJ(pMiniport,&SR);

    //
    // Switch on request type
    //
    switch (Oid) 
    {
        case OID_GEN_MAC_OPTIONS:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAC_OPTIONS"));

            GenericULong =                      
                NDIS_MAC_OPTION_NO_LOOPBACK;

            break;

        case OID_GEN_SUPPORTED_LIST:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_SUPPORTED_LIST"));

            MoveSource = (PVOID)(EthernetSupportedOids);
            MoveBytes = sizeof(EthernetSupportedOids);

            break;

        case OID_GEN_HARDWARE_STATUS:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_HARDWARE_STATUS"));

            HardwareStatus = NdisHardwareStatusReady;
            MoveSource = (PVOID)(&HardwareStatus);
            MoveBytes = sizeof(NDIS_HARDWARE_STATUS);

            break;

        case OID_GEN_MEDIA_CONNECT_STATUS:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MEDIA_CONNECT_STATUS"));

            MoveSource = (PVOID)(&pMiniport->info.MediaState);
            MoveBytes = sizeof(NDIS_MEDIA_STATE);

            break;

        case OID_GEN_MEDIA_SUPPORTED:
        case OID_GEN_MEDIA_IN_USE:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MEDIA_SUPPORTED"));
            Medium = ATMEPVC_MP_MEDIUM;

            MoveSource = (PVOID) (&Medium);
            MoveBytes = sizeof(NDIS_MEDIUM);

            break;

        case OID_GEN_MAXIMUM_LOOKAHEAD:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_LOOKAHEAD"));

            GenericULong = pMiniport->info.CurLookAhead;
            
            
            break;
            
        case OID_GEN_CURRENT_LOOKAHEAD:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_CURRENT_LOOKAHEAD"));
            GenericULong  = pMiniport->info.CurLookAhead  ;
            
            
            break;

        case OID_GEN_MAXIMUM_FRAME_SIZE:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_FRAME_SIZE"));
            // 
            // Similiar to AtmLane . Take the size of the Ethernet frame and strip the
            // ethernet header off. 
            //
            GenericULong = EPVC_MAX_FRAME_SIZE  - EPVC_ETH_HEADERSIZE   ;
            
            break;

        case OID_GEN_MAXIMUM_TOTAL_SIZE:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_TOTAL_SIZE"));
            //
            // This value is inclusive of headers 
            //
            GenericULong = EPVC_MAX_FRAME_SIZE;
                        
            break;

        case OID_GEN_TRANSMIT_BLOCK_SIZE:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_TRANSMIT_BLOCK_SIZE"));
            //
            // This is inclusive of headers. 
            //
            GenericULong = EPVC_MAX_FRAME_SIZE;
            

            break;
            
        case OID_GEN_RECEIVE_BLOCK_SIZE:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RECEIVE_BLOCK_SIZE"));
            GenericULong = EPVC_MAX_FRAME_SIZE ;
            
            break;
        
        case OID_GEN_MAXIMUM_SEND_PACKETS:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_MAXIMUM_SEND_PACKETS"));
            GenericULong = 32;      // XXX What is our limit? From adapter?
            
            break;
        
            case OID_GEN_LINK_SPEED:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_LINK_SPEED"));
            GenericULong = pMiniport->info.LinkSpeed;

            
            break;

        case OID_GEN_TRANSMIT_BUFFER_SPACE:
        case OID_GEN_RECEIVE_BUFFER_SPACE:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RECEIVE_BUFFER_SPACE"));
            GenericULong = 32 * 1024;   // XXX What should this really be?
            

            break;

        case OID_GEN_VENDOR_ID:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_VENDOR_ID"));
            NdisMoveMemory(
                (PVOID)&GenericULong,
                &pMiniport->MacAddressEth,
                3
                );
            GenericULong &= 0xFFFFFF00;
            MoveSource = (PVOID)(&GenericULong);
            MoveBytes = sizeof(GenericULong);
            break;

        case OID_GEN_VENDOR_DESCRIPTION:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_VENDOR_DESCRIPTION"));
            MoveSource = (PVOID)"Microsoft ATM Ethernet Emulation";
            MoveBytes = 28;

            break;

        case OID_GEN_DRIVER_VERSION:
        case OID_GEN_VENDOR_DRIVER_VERSION:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_DRIVER_VERSION"));
            GenericUShort = ((USHORT)5 << 8) | 0;
            MoveSource = (PVOID)(&GenericUShort);
            MoveBytes = sizeof(GenericUShort);

            break;

        case OID_802_3_PERMANENT_ADDRESS:
        case OID_802_3_CURRENT_ADDRESS:
        
            TRACE (TL_V, TM_Rq,(" Miniport Query OID_802_3_CURRENT_ADDRESS"));

            NdisMoveMemory((PCHAR)GenericArray,
                        &pMiniport->MacAddressEth,
                        sizeof(MAC_ADDRESS));
            MoveSource = (PVOID)(GenericArray);
            MoveBytes = sizeof(MAC_ADDRESS);


            break;


        case OID_802_3_MULTICAST_LIST:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_802_3_MULTICAST_LIST"));
            MoveSource = (PVOID) &pMiniport->info.McastAddrs[0];
            MoveBytes = pMiniport->info.McastAddrCount * sizeof(MAC_ADDRESS);

            break;

        case OID_802_3_MAXIMUM_LIST_SIZE:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_802_3_MAXIMUM_LIST_SIZE"));
            GenericULong = MCAST_LIST_SIZE;
        
            
            break;
            


        case OID_GEN_XMIT_OK:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_XMIT_OK"));
            GenericULong = (UINT)(pMiniport->count.FramesXmitOk);
            
            break;

        case OID_GEN_RCV_OK:

            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RCV_OK"));
            GenericULong = (UINT)(pMiniport->count.FramesRecvOk);

            
            break;
        case OID_GEN_RCV_ERROR:
        
            TRACE (TL_V, TM_Rq,(" Miniport Query OID_GEN_RCV_OK"));
            GenericULong = pMiniport->count.RecvDropped ;
            break;

        case OID_GEN_XMIT_ERROR:
        case OID_GEN_RCV_NO_BUFFER:
        case OID_802_3_RCV_ERROR_ALIGNMENT:
        case OID_802_3_XMIT_ONE_COLLISION:
        case OID_802_3_XMIT_MORE_COLLISIONS:
    
            TRACE (TL_V, TM_Rq,(" Miniport Query - Unimplemented Stats Oid"));
            GenericULong = 0;

            
            break;

        default:

            StatusToReturn = NDIS_STATUS_INVALID_OID;
            break;

    }


    if (StatusToReturn == NDIS_STATUS_SUCCESS) 
    {
        if (MoveBytes > BytesLeft) 
        {
            //
            // Not enough room in InformationBuffer. Punt
            //
            *BytesNeeded = MoveBytes;

            StatusToReturn = NDIS_STATUS_INVALID_LENGTH;
        }
        else
        {
            //
            // Store and print result.
            //
            NdisMoveMemory(InfoBuffer, MoveSource, MoveBytes);

            TRACE (TL_V, TM_Rq, ("Query Request Oid %x", Oid));
            DUMPDW( TL_V, TM_Rq, MoveSource, MoveBytes);
            
            (*BytesWritten) = MoveBytes;
        }
    }


    TRACE(TL_T, TM_Rq, ("<==EpvcMpQueryInformation Status %x",StatusToReturn)); 
    RM_ASSERT_CLEAR(&SR);
    return StatusToReturn;
}




NDIS_STATUS 
EpvcMpSetInformation(
    IN  NDIS_HANDLE             MiniportAdapterContext,
    IN  NDIS_OID                Oid,
    IN  PVOID                   InformationBuffer,
    IN  ULONG                   InformationBufferLength,
    OUT PULONG                  BytesRead,
    OUT PULONG                  BytesNeeded
)
/*++

Routine Description:

    Handles a set operation for a single OID.

Arguments:

    MiniportAdapterContext  - a pointer to the Elan.

    Oid                     - the NDIS_OID to process.

    InformationBuffer       - Holds the data to be set.

    InformationBufferLength - The length of InformationBuffer.

    BytesRead               - If the call is successful, returns the number
                              of bytes read from InformationBuffer.

    BytesNeeded             - If there is not enough data in InformationBuffer
                              to satisfy the OID, returns the amount of storage
                              needed.

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_PENDING
    NDIS_STATUS_INVALID_LENGTH
    NDIS_STATUS_INVALID_OID

--*/
{
    ENTER ("EpvcMpSetInformation", 0x619a7528)
    NDIS_STATUS         StatusToReturn  = NDIS_STATUS_SUCCESS;
    UINT                BytesLeft       = InformationBufferLength;
    PUCHAR              InfoBuffer      = (PUCHAR)(InformationBuffer);
    UINT                OidLength;
    ULONG               LookAhead;
    ULONG               Filter;
    PEPVC_I_MINIPORT    pMiniport = NULL;
    PEPVC_ADAPTER       pAdapter = NULL;
    BOOLEAN             IsShuttingDown = FALSE;
    RM_DECLARE_STACK_RECORD (SR);
        
    TRACE(TL_T, TM_Mp, ("==>EpvcMpSetInformation pMiniport %x, Oid, Buffer %x, Length, %x",
                       pMiniport,
                       Oid,
                       InformationBuffer,
                       InformationBufferLength));               

    pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext;

    LOCKOBJ(pMiniport, &SR);
    IsShuttingDown =(! MiniportTestFlag(pMiniport, fMP_MiniportInitialized));
    pAdapter = pMiniport->pAdapter;
    UNLOCKOBJ(pMiniport,&SR);

    if (IsShuttingDown)
    {
        TRACE (TL_I, TM_Mp,(" Miniport shutting down. Trivially succeeding Set OID %x \n", Oid ));
        *BytesRead = 0;
        *BytesNeeded = 0;

        StatusToReturn = NDIS_STATUS_SUCCESS;
        return (StatusToReturn);
    }

    //
    // Get Oid and Length of request
    //
    OidLength = BytesLeft;

    switch (Oid) 
    {

        case OID_802_3_MULTICAST_LIST:

            TRACE (TL_V, TM_Rq,(" Miniport Set OID_802_3_MULTICAST_LIST"));

            if (OidLength % sizeof(MAC_ADDRESS))
            {
                StatusToReturn = NDIS_STATUS_INVALID_LENGTH;
                *BytesRead = 0;
                *BytesNeeded = 0;
                break;
            }
            
            if (OidLength > (MCAST_LIST_SIZE * sizeof(MAC_ADDRESS)))
            {
                StatusToReturn = NDIS_STATUS_MULTICAST_FULL;
                *BytesRead = 0;
                *BytesNeeded = 0;
                break;
            }
            
            NdisZeroMemory(
                    &pMiniport->info.McastAddrs[0], 
                    MCAST_LIST_SIZE * sizeof(MAC_ADDRESS)
                    );
            NdisMoveMemory(
                    &pMiniport->info.McastAddrs[0], 
                    InfoBuffer,
                    OidLength
                    );
            pMiniport->info.McastAddrCount = OidLength / sizeof(MAC_ADDRESS);


            break;

        case OID_GEN_CURRENT_PACKET_FILTER:

            TRACE (TL_V, TM_Rq,(" Miniport Set OID_GEN_CURRENT_PACKET_FILTER"));
            //
            // Verify length
            //
            if (OidLength != sizeof(ULONG)) 
            {
                StatusToReturn = NDIS_STATUS_INVALID_LENGTH;
                *BytesRead = 0;
                *BytesNeeded = sizeof(ULONG);
                ASSERT (0);
                break;
            }

            BytesLeft = sizeof (ULONG);
            //
            // Store the new value.
            //
            NdisMoveMemory(&Filter, InfoBuffer, BytesLeft );

            //
            // Don't allow promisc mode, because we can't support that.
            //
            if (Filter & NDIS_PACKET_TYPE_PROMISCUOUS)
            {
                StatusToReturn = NDIS_STATUS_NOT_SUPPORTED;
                break;
            }

            StatusToReturn   = epvcSetPacketFilter(pMiniport, Filter, &SR);

            break;

        case OID_802_5_CURRENT_FUNCTIONAL:
        case OID_802_5_CURRENT_GROUP:
            TRACE (TL_V, TM_Rq,(" Miniport Set OID_802_5_CURRENT_GROUP"));

            // XXX just accept whatever for now ???
            
            break;

        case OID_GEN_CURRENT_LOOKAHEAD:
            TRACE (TL_V, TM_Rq,(" Miniport Set OID_GEN_CURRENT_LOOKAHEAD"));

            //
            // Verify length
            //
            if (OidLength != 4) 
            {
                StatusToReturn = NDIS_STATUS_INVALID_LENGTH;
                *BytesRead = 0;
                *BytesNeeded = 0;
                break;
            }

            //
            // Store the new value.
            //
            NdisMoveMemory(&LookAhead, InfoBuffer, 4);

            ASSERT (pMiniport->pAdapter != NULL);
            
            if (LookAhead <= pAdapter->info.MaxAAL5PacketSize)
            {
                pMiniport->info.CurLookAhead = LookAhead;
                TRACE (TL_V, TM_Mp, ("New Lookahead size %x \n",pMiniport->info.CurLookAhead )); 
            }
            else 
            {
                StatusToReturn = NDIS_STATUS_INVALID_LENGTH;
            }

            break;

        case OID_GEN_NETWORK_LAYER_ADDRESSES:
            TRACE (TL_V, TM_Rq,(" Miniport Set OID_GEN_NETWORK_LAYER_ADDRESSES"));
            StatusToReturn = epvcMpSetNetworkAddresses(
                                pMiniport,
                                InformationBuffer,
                                InformationBufferLength,
                                &SR,
                                BytesRead,
                                BytesNeeded);
            break;
                                
        default:

            StatusToReturn = NDIS_STATUS_INVALID_OID;

            *BytesRead = 0;
            *BytesNeeded = 0;

            break;

    }

    if (StatusToReturn == NDIS_STATUS_SUCCESS) 
    {
        *BytesRead = BytesLeft;
        *BytesNeeded = 0;
        DUMPDW( TL_V, TM_Rq, InformationBuffer, *BytesRead );
    }
    

    TRACE(TL_T, TM_Mp, ("<==EpvcMpSetInformation Status %x",StatusToReturn));   
    RM_ASSERT_CLEAR(&SR);
    return StatusToReturn;
}




VOID
epvcMPLocalRequestComplete (
    PEPVC_NDIS_REQUEST pEpvcRequest, 
    NDIS_STATUS Status
    )
/*++

Routine Description:

    Miniport's local Request Completion handler for the occasion
    when a locally allocated NdisRequest was sent to the miniport below us.

    We look up to see if a request to our miniport edge initiated this request.
    If so, we complete the Set/Query

    Assumes that the epvcRequest was allocated from the HEAP

Arguments:
    pEpvcRequest - Locally allocated Request structure
    
Return Value:
--*/
{
    ENTER("epvcMPLocalRequestComplete ", 0x77d107ae)
    PEPVC_I_MINIPORT pMiniport = pEpvcRequest->pMiniport;

    RM_DECLARE_STACK_RECORD (SR);
    //
    // First complete the request that we have pended
    //

    do
    {
    
        if (pMiniport == NULL || pEpvcRequest->fPendedRequest == FALSE)
        {
            //
            // No pended request to complete
            //
            break;
        }

        if (pEpvcRequest->fSet  == TRUE)
        {
            NdisMSetInformationComplete (pMiniport->ndis.MiniportAdapterHandle,
                                         Status);
        }
        else
        {
            NdisMQueryInformationComplete (pMiniport->ndis.MiniportAdapterHandle,
                                         Status);


        }


    } while (FALSE);

    if (pMiniport != NULL)
    {
        //
        // Deref the miniport
        //
        epvcUnlinkFromExternal( &pMiniport->Hdr,  //pHdr
                                       0xaa625b37, // Luid
                                       (UINT_PTR)pEpvcRequest,// External entity
                                       EPVC_ASSOC_MINIPORT_REQUEST,         // AssocID
                                       &SR
                                       );
    }


    //
    // now free the memory that was allocated. 
    //
    NdisFreeMemory (pEpvcRequest, sizeof (*pEpvcRequest), 0);


}





NDIS_STATUS
epvcMpSetNetworkAddresses(
    IN  PEPVC_I_MINIPORT        pMiniport,
    IN  PVOID                   InformationBuffer,
    IN  ULONG                   InformationBufferLength,
    IN  PRM_STACK_RECORD        pSR,
    OUT PULONG                  BytesRead,
    OUT PULONG                  BytesNeeded
)
/*++

Routine Description:

    Called when the protocol above us wants to let us know about
    the network address(es) assigned to this interface. If this is TCP/IP,
    then we reformat and send a request to the ATM Call Manager to set
    its atmfMyIpNmAddress object. We pick the first IP address given to us.

Arguments:

    pMiniport                   - Pointer to the ELAN

    InformationBuffer       - Holds the data to be set.

    InformationBufferLength - The length of InformationBuffer.

    BytesRead               - If the call is successful, returns the number
                              of bytes read from InformationBuffer.

    BytesNeeded             - If there is not enough data in InformationBuffer
                              to satisfy the OID, returns the amount of storage
                              needed.

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_PENDING
    NDIS_STATUS_INVALID_LENGTH
--*/
{
    ENTER("epvcMpSetNetworkAddresses" , 0x385441e2)
    NETWORK_ADDRESS_LIST UNALIGNED *        pAddrList = NULL;
    NETWORK_ADDRESS UNALIGNED *             pAddr = NULL;
    NETWORK_ADDRESS_IP UNALIGNED *          pIpAddr= NULL;
    ULONG                                   Size;
    PUCHAR                                  pNetworkAddr = NULL;
    NDIS_HANDLE                             NdisAdapterHandle;
    NDIS_HANDLE                             NdisAfHandle;
    NDIS_STATUS                             Status;
    PEPVC_ADAPTER                           pAdapter = (PEPVC_ADAPTER)pMiniport->pAdapter;

    //
    //  Initialize.
    //
    *BytesRead = 0;
    Status = NDIS_STATUS_SUCCESS;

    pAddrList = (NETWORK_ADDRESS_LIST UNALIGNED *)InformationBuffer;

    do
    {

        *BytesNeeded = sizeof(*pAddrList) -
                        FIELD_OFFSET(NETWORK_ADDRESS_LIST, Address) +
                        sizeof(NETWORK_ADDRESS) -
                        FIELD_OFFSET(NETWORK_ADDRESS, Address);

        if (InformationBufferLength < *BytesNeeded)
        {
            Status = NDIS_STATUS_INVALID_LENGTH;
            break;
        }

        if (pAddrList->AddressType != NDIS_PROTOCOL_ID_TCP_IP)
        {
            // Not interesting.
            break;
        }

        if (pAddrList->AddressCount <= 0)
        {
            Status = NDIS_STATUS_INVALID_DATA;
            break;
        }

        pAddr = (NETWORK_ADDRESS UNALIGNED *)&pAddrList->Address[0];

        if ((pAddr->AddressLength > InformationBufferLength - *BytesNeeded) ||
            (pAddr->AddressLength == 0))
        {
            Status = NDIS_STATUS_INVALID_LENGTH;
            break;
        }

        if (pAddr->AddressType != NDIS_PROTOCOL_ID_TCP_IP)
        {
            // Not interesting.
            break;
        }

        if (pAddr->AddressLength < sizeof(NETWORK_ADDRESS_IP))
        {
            Status = NDIS_STATUS_INVALID_LENGTH;
            break;
        }

        pIpAddr = (NETWORK_ADDRESS_IP UNALIGNED *)&pAddr->Address[0];

        //
        //  Allocate an NDIS request to send down to the call manager.
        //
        Size = sizeof(pIpAddr->in_addr);
        Status = epvcAllocateMemoryWithTag(&pNetworkAddr, Size, TAG_DEFAULT );

        if ((FAIL(Status) == TRUE) || pNetworkAddr == NULL)
        {
            Status = NDIS_STATUS_RESOURCES;
            pNetworkAddr = NULL;
            break;
        }

        //
        //  Copy the network address in.
        //
        NdisMoveMemory(pNetworkAddr, &pIpAddr->in_addr, sizeof(pIpAddr->in_addr));

        TRACE (TL_V, TM_Mp, (" Set network layer addr: length %d\n", pAddr->AddressLength));
#if DBG
        if (pAddr->AddressLength >= 4)
        {
            TRACE(TL_V, TM_Mp, ("Network layer addr: %d.%d.%d.%d\n",
                    pNetworkAddr[0],
                    pNetworkAddr[1],
                    pNetworkAddr[2],
                    pNetworkAddr[3]));
        }
#endif // DBG

        //
        //  Send off the request.
        //
        { 
            PEPVC_NDIS_REQUEST pRequest;        

            do
            {
        
                Status = epvcAllocateMemoryWithTag (&pRequest, sizeof(*pRequest), TAG_DEFAULT) ;

                if (Status != NDIS_STATUS_SUCCESS)
                {
                    pRequest = NULL;
                    break;
                }



                //
                // There is no failure code path in prepareandsendrequest.
                // Our completion handler will get called and free the memory
                //
                Status = epvcPrepareAndSendNdisRequest(
                                                       pAdapter,
                                                       pRequest,
                                                       epvcMPLocalRequestComplete,
                                                       OID_ATM_MY_IP_NM_ADDRESS,
                                                       pNetworkAddr,
                                                       sizeof(pIpAddr->in_addr),
                                                       NdisRequestSetInformation,
                                                       pMiniport,
                                                       TRUE, // We have Pended a Request
                                                       TRUE, // The Pended request is a Set 
                                                       pSR
                                                       );
                                
                

            } while (FALSE);
            
        }
        break;
    }
    while (FALSE);

    EXIT();
    return (Status);
}






VOID
epvcSetupMakeCallParameters(
    PEPVC_I_MINIPORT pMiniport, 
    PCO_CALL_PARAMETERS *ppCallParameters
    )
/*++

Routine Description:

    Sets up the Call parameters after reading the information
    from the miniport block

Arguments:
    pMiniport - Miniport in question
    ppCallParameter - Location of Call Parameters

Return Value:
    return value *ppCallParamter is NULL on Failure

--*/
{
    ULONG                               RequestSize = 0;
    NDIS_STATUS                         Status = NDIS_STATUS_FAILURE;
    PCO_CALL_PARAMETERS                 pCallParameters = NULL;
    PCO_CALL_MANAGER_PARAMETERS         pCallMgrParameters = NULL;
    PCO_MEDIA_PARAMETERS                pMediaParameters = NULL;
    PATM_MEDIA_PARAMETERS               pAtmMediaParameters = NULL;

    do
    {
        Status = epvcAllocateMemoryWithTag( &pCallParameters,
                                       CALL_PARAMETER_SIZE,
                                       TAG_DEFAULT);

        if (Status != NDIS_STATUS_SUCCESS || pCallParameters  == NULL)
        {
                pCallParameters = NULL;     
                Status = NDIS_STATUS_RESOURCES;
                break;
        }

        NdisZeroMemory (pCallParameters, CALL_PARAMETER_SIZE);

        //
        //  Distribute space and link up pointers amongst the various
        //  structures for the PVC.
        //
        //  pCallParameters------->+----------------------------+
        //                         | CO_CALL_PARAMETERS         |
        //  pCallMgrParameters---->+----------------------------+
        //                         | CO_CALL_MANAGER_PARAMETERS |
        //  pMediaParameters------>+----------------------------+
        //                         | CO_MEDIA_PARAMETERS        |
        //  pAtmMediaParameters--->+----------------------------+
        //                         | ATM_MEDIA_PARAMETERS       |
        //                         +----------------------------+
        //

        pCallMgrParameters = (PCO_CALL_MANAGER_PARAMETERS)
                                ((PUCHAR)pCallParameters +
                                sizeof(CO_CALL_PARAMETERS));
        pCallParameters->CallMgrParameters = pCallMgrParameters;
        pCallMgrParameters->CallMgrSpecific.ParamType = 0;  
        pCallMgrParameters->CallMgrSpecific.Length = 0;
        pMediaParameters = (PCO_MEDIA_PARAMETERS)
            pCallMgrParameters->CallMgrSpecific.Parameters;
        pCallParameters->MediaParameters = pMediaParameters;
        pAtmMediaParameters = (PATM_MEDIA_PARAMETERS)
                                pMediaParameters->MediaSpecific.Parameters;


        //
        //  Call Manager generic flow paramters:
        //
        pCallMgrParameters->Transmit.TokenRate = 
                pMiniport->pAdapter->info.LinkSpeed.Outbound/8*100; // cnvt decibits to bytes
        pCallMgrParameters->Transmit.PeakBandwidth = 
                pMiniport->pAdapter->info.LinkSpeed.Outbound/8*100; // cnvt decibits to bytes
        pCallMgrParameters->Transmit.ServiceType = SERVICETYPE_BESTEFFORT;

        pCallMgrParameters->Receive.TokenRate = 
                pMiniport->pAdapter->info.LinkSpeed.Inbound/8*100;  // cnvt decibits to bytes
        pCallMgrParameters->Receive.PeakBandwidth = 
                pMiniport->pAdapter->info.LinkSpeed.Inbound/8*100;  // cnvt decibits to bytes
        pCallMgrParameters->Receive.ServiceType = SERVICETYPE_BESTEFFORT;

        //
        //  use 1516 per spec
        //
        pCallMgrParameters->Transmit.TokenBucketSize = 
            pCallMgrParameters->Transmit.MaxSduSize = 
            pCallMgrParameters->Receive.TokenBucketSize = 
            pCallMgrParameters->Receive.MaxSduSize = 
                 1516;

        //
        //  PVC Generic and ATM-specific Media Parameters
        //
        pMediaParameters->Flags = TRANSMIT_VC | RECEIVE_VC;
        pMediaParameters->MediaSpecific.ParamType = ATM_MEDIA_SPECIFIC;
        pMediaParameters->MediaSpecific.Length = sizeof(ATM_MEDIA_PARAMETERS);

        pAtmMediaParameters->ConnectionId.Vpi = pMiniport->config.vpi;  //0
        pAtmMediaParameters->ConnectionId.Vci = pMiniport->config.vci;  

        TRACE (TL_I, TM_Mp, ("Miniport Configuration vci %x ,vpi %x", 
                             pMiniport->config.vci ,
                             pMiniport->config.vpi ));

        ASSERT (pMiniport->MaxAcceptablePkt > 1000);
        
        pAtmMediaParameters->AALType = AAL_TYPE_AAL5;
        pAtmMediaParameters->Transmit.PeakCellRate = 
            LINKSPEED_TO_CPS(pMiniport->pAdapter->info.LinkSpeed.Outbound);
        pAtmMediaParameters->Transmit.MaxSduSize = pMiniport->MaxAcceptablePkt    ;
        pAtmMediaParameters->Transmit.ServiceCategory = 
            ATM_SERVICE_CATEGORY_UBR;
        pAtmMediaParameters->Receive.PeakCellRate = 
            LINKSPEED_TO_CPS(pMiniport->pAdapter->info.LinkSpeed.Outbound);
        pAtmMediaParameters->Receive.MaxSduSize = pMiniport->MaxAcceptablePkt   ;
        pAtmMediaParameters->Receive.ServiceCategory = 
            ATM_SERVICE_CATEGORY_UBR;

        //
        //  Set PVC flag here
        //
        pCallParameters->Flags |= PERMANENT_VC;


                                
    } while (FALSE);

    if (Status == NDIS_STATUS_SUCCESS && pCallParameters != NULL)
    {
        //
        // Set up the return value here
        //
        *ppCallParameters = pCallParameters;

    }
    else
    {
        //
        // Clear the Failure case
        //
        *ppCallParameters = NULL;
    }
}   





VOID
epvcRefRecvPkt(
    PNDIS_PACKET        pNdisPacket,
    PRM_OBJECT_HEADER   pHdr // either an adapter or a miniport
    )
{

    // The following macros are just so that we can make 
    // the proper debug association
    // depending on how closely we are tracking outstanding  packets.
    //
    #define OUR_EXTERNAL_ENTITY ((UINT_PTR) pNdisPacket)
    #define szEPVCASSOC_EXTLINK_INDICATED_PKT_FORMAT "    indicated pkt 0x%p\n"

        //
        // If ARPDBG_REF_EVERY_PKT
        //      We add an "external" link for EVERY packet. We'll later remove this
        //      reference when the send completes for this packet.
        // else
        //      Only a transition from zero to non-zero outstanding sends, we
        //      add an "external" link. We'll later remove this link when the
        //      transition from non-zero to zero happens.
        //

    #if RM_EXTRA_CHECKING

        RM_DECLARE_STACK_RECORD(sr)

        epvcLinkToExternal (
            pHdr,                           // pHdr
            0x92036e12,                             // LUID
            OUR_EXTERNAL_ENTITY,                    // External entity
            EPVC_ASSOC_EXTLINK_INDICATED_PKT,           // AssocID
            szEPVCASSOC_EXTLINK_INDICATED_PKT_FORMAT ,
            &sr
            );

    #else   // !RM_EXTRA_CHECKING

        RmLinkToExternalFast(pHdr);

    #endif // !RM_EXTRA_CHECKING

    
    #undef  OUR_EXTERNAL_ENTITY
    #undef  szEPVCASSOC_EXTLINK_INDICATED_PKT_FORMAT 

    #if RM_EXTRA_CHECKING

        RM_ASSERT_CLEAR(&sr);

    #endif
}



VOID
epvcDerefRecvPkt (
    PNDIS_PACKET pNdisPacket,
    PRM_OBJECT_HEADER pHdr
    )
{
    // The following macros are just so that we can make 
    // the proper debug association
    // depending on how closely we are tracking outstanding send packets.
    //
    #if RM_EXTRA_CHECKING


        RM_DECLARE_STACK_RECORD(sr)
    
        epvcUnlinkFromExternal(
                pHdr,                           // pHdr
                0x110ad55b,                             // LUID
                (UINT_PTR)pNdisPacket,                  // External entity
                EPVC_ASSOC_EXTLINK_INDICATED_PKT,           // AssocID
                &sr
                );
    #else   // !RM_EXTRA_CHECKING

        RmUnlinkFromExternalFast (pHdr);

    #endif // !RM_EXTRA_CHECKING

    #if RM_EXTRA_CHECKING

        RM_ASSERT_CLEAR(&sr);

    #endif



}

VOID
epvcDerefSendPkt (
    PNDIS_PACKET pNdisPacket,
    PRM_OBJECT_HEADER pHdr
    )
{
    // The following macros are just so that we can make 
    // the proper debug association
    // depending on how closely we are tracking outstanding send packets.
    //
    #if RM_EXTRA_CHECKING

        RM_DECLARE_STACK_RECORD(sr)
    
        epvcUnlinkFromExternal(
                pHdr,                           // pHdr
                0xf43e0a10,                             // LUID
                (UINT_PTR)pNdisPacket,                  // External entity
                EPVC_ASSOC_EXTLINK_PKT_TO_SEND,         // AssocID
                &sr
                );
    #else   // !RM_EXTRA_CHECKING

        RmUnlinkFromExternalFast (pHdr);

    #endif // !RM_EXTRA_CHECKING


    #if RM_EXTRA_CHECKING

        RM_ASSERT_CLEAR(&sr);

    #endif



}


VOID
epvcRefSendPkt(
    PNDIS_PACKET        pNdisPacket,
    PRM_OBJECT_HEADER   pHdr // either an adapter or a miniport
    )
{

    // The following macros are just so that we can make 
    // the proper debug association
    // depending on how closely we are tracking outstanding send packets.
    //
    #define OUR_EXTERNAL_ENTITY ((UINT_PTR) pNdisPacket)
    #define szEPVCASSOC_EXTLINK_DEST_TO_PKT_FORMAT "    send pkt 0x%p\n"


    #if RM_EXTRA_CHECKING

        RM_DECLARE_STACK_RECORD(sr)

        epvcLinkToExternal (
            pHdr,                           // pHdr
            0xabd17475,                             // LUID
            OUR_EXTERNAL_ENTITY,                    // External entity
            EPVC_ASSOC_EXTLINK_PKT_TO_SEND,         // AssocID
            szEPVCASSOC_EXTLINK_DEST_TO_PKT_FORMAT ,
            &sr
            );

    #else   // !RM_EXTRA_CHECKING

        RmLinkToExternalFast(pHdr);

    #endif // !RM_EXTRA_CHECKING

    
    #undef  OUR_EXTERNAL_ENTITY
    #undef  szEPVCASSOC_EXTLINK_DEST_TO_PKT_FORMAT 

    #if RM_EXTRA_CHECKING

        RM_ASSERT_CLEAR(&sr);

    #endif
}


VOID
epvcExtractPktInfo (
    PEPVC_I_MINIPORT        pMiniport,
    PNDIS_PACKET            pPacket ,
    PEPVC_SEND_STRUCT       pSendStruct
    )
/*++

Routine Description:


Arguments:


Return Value:
    
--*/
{

    pSendStruct->pOldPacket = pPacket;
    pSendStruct->pMiniport = pMiniport;

    epvcSetSendPktStats();

}



NDIS_STATUS
epvcSendRoutine(
    IN PEPVC_I_MINIPORT pMiniport, 
    IN PNDIS_PACKET Packet,
    PRM_STACK_RECORD pSR
    )
/*++

Routine Description:

    This routine does all the hard work.
    It responds to arps if necessary..
    It removes Ethernet Headers if necessary
    It allocates a new packet if necessary 
    It sends the new packet on the wire


Arguments:
    pMiniport - Miniport in question
    Packet - Packet to be sent

Return Value:
    Returns Pending, otherwise expects the calling 
    routine to complete the packet
    
--*/
{
    NDIS_STATUS             Status = NDIS_STATUS_FAILURE;
    PNDIS_PACKET            pNewPkt = NULL;
    EPVC_SEND_STRUCT        SendStruct;

    TRACE (TL_T, TM_Send, ("==>epvcSendRoutine") );

    EPVC_ZEROSTRUCT (&SendStruct);

    do
    {
        epvcExtractPktInfo (pMiniport, Packet, &SendStruct  );

        //
        // if we are doing IP encapsulation, then respond 
        // to the Arp
        //

        if (pMiniport->fDoIpEncapsulation == TRUE) 
        {
            
            //
            // We need to do some special processing for this packet
            //
            SendStruct.fIsThisAnArp = \
                     epvcCheckAndReturnArps (pMiniport, 
                                            Packet ,
                                            &SendStruct, 
                                            pSR);

                                 

            if (SendStruct.fIsThisAnArp  == TRUE  )
            {
                Status = NDIS_STATUS_SUCCESS;
                break ; // Arps are not sent to the atm driver
            }

            if (SendStruct.fNotIPv4Pkt == TRUE)
            {
                // This is not an IPv4 packet. Fail the send.
                Status = NDIS_STATUS_FAILURE;
                break;
            }
        }



        //
        // Allocate a new packet to be sent 
        //
        epvcGetSendPkt(pMiniport, 
                       Packet,
                       &SendStruct,
                       pSR);

        if (SendStruct.pNewPacket == NULL)
        {
            ASSERTAndBreak (SendStruct.pNewPacket != NULL);
        }
        //
        // SendStruct.pNewPacket is guaranteed to have the NdisBuffers Set up

        //
        // Remove Ethernet Header - if necessary
        //
        Status = epvcRemoveEthernetHeader (&SendStruct, pSR);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS)
        }

        //
        // Add Ethernet Padding - if necessary
        //
        Status = epvcAddEthernetTail (&SendStruct, pSR);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS)
        }

        //
        // Add Ethernet Pad 0x00 0x00 to head of packet - if necessary
        //
        Status = epvcAddEthernetPad (&SendStruct, pSR);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS)
        }

        //
        // Add LLC Encapsulation - if necessary
        //
        Status = epvcAddLLCEncapsulation (pMiniport , Packet, SendStruct.pNewPacket, pSR);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            ASSERTAndBreak (Status == NDIS_STATUS_SUCCESS)
        }

        //
        // set the context information for the send complete
        //
        epvcSetPacketContext (&SendStruct, pSR);

        //
        // Only Send if successful
        //
        epvcDumpPkt (SendStruct.pNewPacket);


        Status = epvcAdapterSend(pMiniport,
                                 SendStruct.pNewPacket,
                                 pSR);


    } while (FALSE);

    if (Status != NDIS_STATUS_PENDING &&   // We had a failure
        SendStruct.pNewPacket != NULL )  // but we were able to get a packet
    {
        epvcFreeSendPkt (pMiniport, &SendStruct);
    }

    TRACE (TL_T, TM_Send, ("<==epvcSendRoutine") );
    return Status;
}


VOID
EpvcSendPackets(
    IN  NDIS_HANDLE             MiniportAdapterContext,
    IN  PPNDIS_PACKET           PacketArray,
    IN  UINT                    NumberOfPackets
    )
/*++

Routine Description:

    Send Packet Array handler. Either this or our SendPacket handler is called
    based on which one is enabled in our Miniport Characteristics.


Arguments:

    MiniportAdapterContext  Pointer to our adapter
    PacketArray             Set of packets to send
    NumberOfPackets         Self-explanatory

Return Value:

    None

--*/
{
    PEPVC_I_MINIPORT    pMiniport = (PEPVC_I_MINIPORT)MiniportAdapterContext;
    
    NDIS_STATUS         Status = NDIS_STATUS_FAILURE;
    UINT                i;
    RM_DECLARE_STACK_RECORD (SR);

    DBGMARK(0xdaab68c3);

    TRACE (TL_T, TM_Send, ("==>EpvcSendPackets pMiniport %p, pPktArray %p, Num %x",
                              pMiniport, PacketArray, NumberOfPackets));

    for (i = 0; i < NumberOfPackets; i++)
    {
        PEPVC_PKT_CONTEXT           Rsvd;
        PNDIS_PACKET    Packet = NULL; 
    
        Packet = PacketArray[i];

        epvcValidatePacket (Packet);

        Status= epvcSendRoutine (pMiniport, Packet, &SR);

        if (Status != NDIS_STATUS_PENDING)
        {
            epvcMSendComplete(pMiniport, Packet , Status);
        }

    }

    TRACE (TL_T, TM_Send, ("<==EpvcSendPackets "));
    RM_ASSERT_CLEAR(&SR);

    return;
}   


VOID
epvcFreeSendPkt(
    PEPVC_I_MINIPORT pMiniport,
    IN PEPVC_SEND_STRUCT pSendStruct
    )
/*++
Routine Description:

    Pops the packet stack if stacks were used or free the new packet after
    copying the per packet info

Arguments:
    pMiniport - which the packet was sent to
    pSentPkt - The packet that is being sent.
    ppPkt - the new packet that was allocated or the old one if a stack was available
    
--*/

{
    ENTER ("epvcFreeSendPkt", 0xff3ce0fd)
    PNDIS_PACKET pOldPkt = pSendStruct->pOldPacket;
    PNDIS_PACKET pNewPkt = pSendStruct->pNewPacket;
    
    TRACE (TL_T, TM_Send, ("==>epvcFreeSendPkt pNewPkt %x, pPOldPkt ",pNewPkt, pOldPkt));

    //
    // Remove the ethernet padding - if necessary
    //
    epvcRemoveEthernetPad (pMiniport, pNewPkt);

    //
    // Remove the Ethernet Tail-  if necessary
    //
    epvcRemoveEthernetTail(pMiniport, pNewPkt, &pSendStruct->Context);

    //
    // If the two packets are the same, then we used Packet Stacks
    //

    if (pNewPkt != NULL && pSendStruct->fUsingStacks== FALSE)
    {
            NdisIMCopySendCompletePerPacketInfo (pOldPkt, pNewPkt);

            epvcFreePacket(pNewPkt,&pMiniport->PktPool.Send);

            pNewPkt = pSendStruct->pNewPacket = NULL;

    }       





    TRACE (TL_T, TM_Send, ("<==epvcFreeSendPkt pNewPkt %x, pPOldPkt ",pNewPkt, pOldPkt));
    EXIT()
    return;
}


VOID
epvcGetSendPkt (
    IN PEPVC_I_MINIPORT pMiniport,
    IN PNDIS_PACKET pSentPkt,
    OUT PEPVC_SEND_STRUCT pSendStruct,
    IN PRM_STACK_RECORD pSR
    )
/*++
Routine Description:

    Allocates an NdisPkt or pushes a Pkt Stack to get a valid NdisPkt that 
    can be sent to the adapter below.

Arguments:
    pMiniport - which the packet was sent to
    pSentPkt - The packet that is being sent.
    ppPkt - the new packet that was allocated or the old one if a stack was available
    
--*/

{
    ENTER ("epvcGetSendPkt", 0x5734054f)

    NDIS_STATUS             Status = NDIS_STATUS_FAILURE;
    PNDIS_PACKET            pNewPkt  = NULL;
    BOOLEAN                 Remaining = FALSE;
    PVOID                   MediaSpecificInfo = NULL;
    UINT                    MediaSpecificInfoSize = 0;

    
    TRACE (TL_T, TM_Send, ("==>epvcGetSendPkt  pSentPkt %x",pSentPkt));


    do
    {
        
#if PKT_STACKS

        //
        // Packet stacks: Check if we can use the same packet for sending down.
        //
        pStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
        if (Remaining)
        {
            //
            // We can reuse "Packet".
            //
            // NOTE: if we needed to keep per-packet information in packets
            // sent down, we can use pStack->IMReserved[].
            //
            
            pNewPkt = pSentPkt;
            pSendStruct->pPktStack = pStack;
            
            pSendStruct->fUsingStacks  = TRUE;
            break;
            
        }
#endif

        pSendStruct->fUsingStacks  = FALSE;

        epvcAllocatePacket(&Status,
                           &pNewPkt,
                           &pMiniport->PktPool.Send);

        if (Status == NDIS_STATUS_SUCCESS)
        {
            PNDIS_PACKET_EXTENSION  Old, New;
            PEPVC_PKT_CONTEXT Rsvd = NULL;


            Rsvd = (PEPVC_PKT_CONTEXT)(pNewPkt->ProtocolReserved);
            Rsvd->pOriginalPacket = pSentPkt;

            pNewPkt->Private.Flags = NdisGetPacketFlags(pSentPkt);

            pNewPkt->Private.Head = pSentPkt->Private.Head;
            pNewPkt->Private.Tail = pSentPkt->Private.Tail;

            //
            // Copy the OOB Offset from the original packet to the new
            // packet.
            //
            NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(pNewPkt),
                           NDIS_OOB_DATA_FROM_PACKET(pSentPkt),
                           sizeof(NDIS_PACKET_OOB_DATA));
            //
            // Copy relevant parts of the per packet info into the new packet
            //
            NdisIMCopySendPerPacketInfo(pNewPkt, pSentPkt);

            //
            // Copy the Media specific information
            //
            NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(pSentPkt,
                                                &MediaSpecificInfo,
                                                &MediaSpecificInfoSize);

            if (MediaSpecificInfo || MediaSpecificInfoSize)
            {
                NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(pNewPkt,
                                                    MediaSpecificInfo,
                                                    MediaSpecificInfoSize);
            }

        
        }
        else
        {
            pNewPkt = NULL;
        }

    } while (FALSE);

    
    pSendStruct->pNewPacket = pNewPkt;

    TRACE (TL_T, TM_Send, ("<==epvcGetSendPkt  pSentPkt %p ppNewPkt %p",pSentPkt, pSendStruct->pNewPacket ));
    EXIT()
    return;
}


NDIS_STATUS
epvcAdapterSend(
    IN PEPVC_I_MINIPORT pMiniport,
    IN PNDIS_PACKET pPkt,
    PRM_STACK_RECORD pSR
    )
{
    BOOLEAN         fDoSend = FALSE;
    PEPVC_ADAPTER   pAdapter  = pMiniport->pAdapter;
    NDIS_STATUS     Status = NDIS_STATUS_FAILURE;

    ENTER("epvcAdapterSend", 0x5b014909)


    TRACE (TL_T, TM_Send, (" ==>epvcAdapterSend" ) )

    do
    {
        //
        // Check to see if we have a valid Send Case
        //
        LOCKOBJ (pMiniport, pSR);
        
        fDoSend = MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded);

        if (fDoSend == FALSE)
        {
            TRACE (TL_V, TM_Send,("Send - MakeCall Not Succeeded"));
        }

        //
        // Add an association while holding the lock
        //
        if (fDoSend == TRUE)
        {
            epvcRefSendPkt(pPkt, &pMiniport->Hdr);
        }
        
        UNLOCKOBJ (pMiniport, pSR);

        if (fDoSend == TRUE)
        {
            epvcCoSendPackets(pMiniport->vc.VcHandle,
                                       &pPkt,
                                       1    
                                       );

            Status = NDIS_STATUS_PENDING;                                      
        }
        else
        {
            Status = NDIS_STATUS_FAILURE;
        }



    } while (FALSE);



    TRACE (TL_T, TM_Send, (" <==epvcAdapterSend fDoSend %x, Status %x", fDoSend, Status ) )
    return Status;
}



VOID
epvcFormulateArpResponse (
    IN PEPVC_I_MINIPORT pMiniport, 
    IN PEPVC_ARP_CONTEXT pArpContext,
    IN PRM_STACK_RECORD pSR
    )
/*++
Routine Description:

    This allocates an Arp Packet, looks at the Arp Request, formulates
    a response and sends it up back to the protocol

Arguments:
    pMiniport - which the packet was sent to
    pArpContext - Contains all the information relating to the Arp. 
                  the Context Is Allocated on the stack

Return:

--*/    
{
    ENTER("epvcFormulateArpResponse",  0x7a763fce)
    PEPVC_ARP_PACKET pResponse = NULL;
    NDIS_STATUS Status = NDIS_STATUS_FAILURE;
    PTASK_ARP pTask = NULL;


    TRACE (TL_T, TM_Send, ("==>epvcFormulateArpResponse pMiniport %x, pArpContext %x",
                             pMiniport, pArpContext))
    do
    {
        //
        // Allocate a buffer from a lookaside list 
        //

        Status = epvcAllocateTask(
                &pMiniport->Hdr,        // pParentObject,
                epvcTaskRespondToArp,   // pfnHandler,
                0,                          // Timeout,
                "Task: Arp Response",   // szDescription
                &(PRM_TASK)pTask,
                pSR
                );

        if (FAIL(Status))
        {
            pTask = NULL;
            break;
        }

        //
        // Set up Arp Response
        //

        pResponse = &pTask->Pkt;
        EPVC_ZEROSTRUCT (pResponse);

        {
            //
            // Construct the Ethernet Header 
            //
        
            PEPVC_ETH_HEADER  pRespHeader = &pResponse->Header;
            PEPVC_ETH_HEADER  pSrcHeader = (PEPVC_ETH_HEADER)pArpContext->pEthHeader;

            ASSERT (pSrcHeader != NULL);
            ASSERT (pRespHeader  != NULL);

            //
            // set up the Eth header
            //
            NdisMoveMemory (&pRespHeader->eh_daddr, 
                            &pSrcHeader->eh_saddr, 
                            ARP_802_ADDR_LENGTH ) ;

            NdisMoveMemory ( &pRespHeader->eh_saddr,                            
                             &pMiniport->info.MacAddressDummy, 
                             ARP_802_ADDR_LENGTH );

            pRespHeader->eh_type = pSrcHeader->eh_type;  // copy 08 06 over
                                                        
            
        }           


        
        {

            //
            // Construct the Arp Response
            //

            PEPVC_ARP_BODY pRespBody = &pResponse->Body;
            PEPVC_ARP_BODY pSrcBody = pArpContext ->pBody;

            ASSERT (pRespBody != NULL);


            ASSERT (pSrcBody  != NULL);


            
            pRespBody->hw = pSrcBody->hw;                                       // Hardware address space. = 00 01

            pRespBody->pro = pSrcBody->pro;                                 // Protocol address space. = 08 00

            pRespBody->hlen = ARP_802_ADDR_LENGTH; // 6

            pRespBody->plen = sizeof (IP_ADDR); // 4
            
            pRespBody->opcode = net_short(ARP_RESPONSE);                        // Opcode.


            pRespBody->SenderHwAddr= pMiniport->info.MacAddressDummy;           // Source HW address.

            pRespBody->SenderIpAddr = pSrcBody->DestIPAddr ;                    // Source protocol address.

            pRespBody->DestHwAddr = pSrcBody->SenderHwAddr;                     // Destination HW address.

            pRespBody->DestIPAddr = pSrcBody->SenderIpAddr;                     // Destination protocol address.

        }



        //
        // So we have the packet ready for transmission.
        //

        RmStartTask ((PRM_TASK)pTask, 0 , pSR);

    } while (FALSE);
    
    TRACE (TL_T, TM_Send, ("<==epvcFormulateArpResponse "))

}



NDIS_STATUS
epvcTaskRespondToArp(
    IN  struct _RM_TASK *           pTask,
    IN  RM_TASK_OPERATION           Code,
    IN  UINT_PTR                    UserParam,
    IN  PRM_STACK_RECORD            pSR
    )
/*++
Routine Description:

    This function queues a zero timeout timer and indicates a receive


Arguments:


Return:

--*/    
{
    ENTER("epvcTaskRespondToArp", 0xd05c4942)
    NDIS_STATUS         Status      = NDIS_STATUS_FAILURE;
    PEPVC_I_MINIPORT    pMiniport   = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask);
    PTASK_ARP           pTaskArp    = (PTASK_ARP) pTask;
    PEPVC_ADAPTER       pAdapter    = (PEPVC_ADAPTER)pMiniport->Hdr.pParentObject;


    enum 
    {
        Stage_Start =0, // default
        Stage_DoAllocations,
        Stage_QueuedTimer,
        Stage_PacketReturned,
        Stage_TaskCompleted,
        Stage_End       
    
    
    }; // To be used in pTask->Hdr.State to indicate the state of the Task

    TRACE ( TL_T, TM_Pt, ("==> epvcTaskRespondToArp %x",pTask->Hdr.State  ) );

    switch (pTask->Hdr.State)
    {
        case Stage_Start:
        {
            LOCKOBJ (pMiniport, pSR);
            
            if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->arps.pTask)) == FALSE)
            {
                PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->arps.pTask);
                
                RmTmpReferenceObject (&pOtherTask->Hdr, pSR);

                //
                // Set The state so we restart this code after main task completes 
                //

                pTask->Hdr.State = Stage_Start;
                UNLOCKOBJ(pMiniport, pSR);

                

                RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR);

                RmTmpDereferenceObject(&pOtherTask->Hdr,pSR);
                Status = NDIS_STATUS_PENDING;
                break;
            }

            //
            // We are the primary task
            //
            //
            // Check to see if the miniport is still active. 
            // If it is halting, then we don't need to do any work
            //
            if (MiniportTestFlag(pMiniport,  fMP_MiniportInitialized) == FALSE)
            {
                //
                // Our work had been done. So break out and complete the task
                //
                Status = NDIS_STATUS_SUCCESS;
                
                pTask->Hdr.State = Stage_TaskCompleted;
                UNLOCKOBJ(pMiniport, pSR);
                break;
            }


            UNLOCKOBJ(pMiniport, pSR);

            pTask->Hdr.State = Stage_DoAllocations;

            FALL_THROUGH
        }

        case Stage_DoAllocations:
        {
            PNDIS_BUFFER pBuffer = NULL;
            
            TRACE (TL_V, TM_Send, ("epvcTaskRespondToArp Stage_DoAllocations Task %p", pTask) );

            //
            // Allocate An NDis Buffer
            //
            epvcAllocateBuffer(&Status,
                               &pBuffer,
                               NULL,  // Pool Handle
                               (PVOID)&pTaskArp->Pkt,
                                sizeof(pTaskArp->Pkt) ); //Length

            ASSERT (sizeof(pTaskArp->Pkt)  == 0x2a);
            
            if (FAIL(Status) == TRUE)                               
            {
                pBuffer = NULL;
                pTask->Hdr.State = Stage_TaskCompleted;


                ASSERTAndBreak (!FAIL(Status));
                break;
            }
            


            //
            // Allocate An Ndis Packet
            //


            epvcAllocatePacket (&Status,
                                &pTaskArp->pNdisPacket,
                                &pMiniport->PktPool.Recv);

            if (FAIL(Status) == TRUE)
            {
                pTask->Hdr.State = Stage_TaskCompleted;
                pTaskArp->pNdisPacket = NULL;

                //
                // Undo allocations 
                //
                epvcFreeBuffer (pBuffer);

                ASSERTAndBreak( !FAIL(Status) );
                
            }

            //
            //  Set up the Ndis Buffer within the NdisPacket
            //
            {
                PNDIS_PACKET_PRIVATE    pPktPriv = &pTaskArp->pNdisPacket->Private;

                pPktPriv->Head = pBuffer;
                pPktPriv->Tail = pBuffer;
                pBuffer->Next = NULL;
            }

            //
            // Set up the Arp response
            //



            //
            // Queue the timer
            //

            NdisMInitializeTimer ( &pTaskArp->Timer,
                                   pMiniport->ndis.MiniportAdapterHandle,
                                   epvcArpTimer,
                                   pTaskArp );

            pTask->Hdr.State = Stage_QueuedTimer;

            //
            // Now prepare to be called back througha timer to do the 
            // receive indication
            //
            RmSuspendTask(pTask, 0,pSR);
            Status = NDIS_STATUS_PENDING;
            
            NdisMSetTimer (&pTaskArp->Timer, 0); // Zero timeout

            break;
        }

        case Stage_QueuedTimer:
        {

            TRACE (TL_V, TM_Send, ("epvcTaskRespondToArp Stage_QueuedTimer Task %p", pTask) );

            //
            // The miniport could have been halted during the timer 
            //
            if (MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == FALSE)
            {
                
                pTask->Hdr.State = Stage_TaskCompleted;
                ASSERTAndBreak(MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == TRUE);
            }

            NDIS_SET_PACKET_HEADER_SIZE(pTaskArp->pNdisPacket       ,
                                      sizeof (pMiniport->RcvEnetHeader)) ; 
            
            NDIS_SET_PACKET_STATUS (pTaskArp->pNdisPacket, NDIS_STATUS_RESOURCES);

            pTask->Hdr.State = Stage_PacketReturned;


            epvcMIndicateReceivePacket (pMiniport,
                                        &pTaskArp->pNdisPacket,
                                        1 );


            FALL_THROUGH
        }

        case Stage_PacketReturned:      
        {
                            
            pTask->Hdr.State = Stage_TaskCompleted;
            Status = NDIS_STATUS_SUCCESS;
            break;
            

        }

        case Stage_TaskCompleted:
        case Stage_End      :
        {
            Status = NDIS_STATUS_SUCCESS;
            break;
        }
        default:
        {
            ASSERTEX(!"Unknown task op", pTask);
        }

    }

    if (pTask->Hdr.State == Stage_TaskCompleted)
    {
        //
        // Free the packet 
        //
        pTask->Hdr.State = Stage_End;
        
        if (pTaskArp->pNdisPacket != NULL)
        {
            //
            // Free the buffer
            //
            PNDIS_PACKET_PRIVATE pPrivate = & pTaskArp->pNdisPacket->Private;
            
            if (pPrivate -> Head != NULL)
            {
                
                epvcFreeBuffer (pPrivate->Head );
                pPrivate->Head = pPrivate->Tail = NULL;
            }

            //
            // free the arp packet
            //
            epvcFreePacket (pTaskArp->pNdisPacket , &pMiniport->PktPool.Recv);
            
            pTaskArp->pNdisPacket = NULL;
        }

        LOCKOBJ (pMiniport, pSR);

        epvcClearPrimaryTask  (&(PRM_TASK)(pMiniport->arps.pTask));

        UNLOCKOBJ (pMiniport, pSR);
            

        Status = NDIS_STATUS_SUCCESS;

    }
    TRACE ( TL_T, TM_Pt, ("<== epvcTaskRespondToArp %x",Status) );

    return Status;
}


VOID
epvcArpTimer(
    IN  PVOID                   SystemSpecific1,
    IN  PVOID                   FunctionContext,
    IN  PVOID                   SystemSpecific2,
    IN  PVOID                   SystemSpecific3
    )
/*++
Routine Description:

    Resume the epvcTaskRespondToArp Task


Arguments:


Return:

--*/    
{
    ENTER ("epvcArpTimer",0xf2adae0e)
    PRM_TASK pTask =  (PRM_TASK) FunctionContext;
    
    RM_DECLARE_STACK_RECORD (SR);


    RmResumeTask (pTask,0,&SR);


    EXIT()
}


BOOLEAN
epvcCheckAndReturnArps (
    IN PEPVC_I_MINIPORT pMiniport, 
    IN PNDIS_PACKET pPkt,
    IN PEPVC_SEND_STRUCT pSendStruct,
    IN PRM_STACK_RECORD pSR
    )
/*++
Routine Description:
    Looks at the packet that is being sent. If it is an Arp request, 
    then it formulates a responses and queues a timer of timeout zero to
    return the Arp

Arguments:
    pMiniport - which the packet was sent to
    pPkt - the packet being sent


Return:
    True - if this is an Arp Request. 
--*/    
{
    ENTER("epvcCheckAndReturnArps ", 0xb8e6a3c4)
    EPVC_ARP_CONTEXT ArpContext;
    TRACE (TL_T, TM_Send, ("==>epvcCheckAndReturnArps "));

    
    EPVC_ZEROSTRUCT (&ArpContext);
    
    do
    {

        ArpContext.pFirstBuffer  = pPkt->Private.Head;


        //
        // Do some sanity checks 
        //
        if (ArpContext.pFirstBuffer == NULL)
        {
            break;
        }

        NdisQueryBufferSafe( ArpContext.pFirstBuffer , 
                             &(PVOID)ArpContext.pEthHeader, 
                             &ArpContext.BufferLength, 
                             LowPagePriority );

        if (ArpContext.pEthHeader == NULL)
        {
            break;
        }

        //
        // It the is not an ARP request then ignore it -- 
        // during testing only
        //
        if (ArpContext.pEthHeader->eh_daddr.Byte[0] == 0xff &&
            ArpContext.pEthHeader->eh_daddr.Byte[1] == 0xff )
        {
            pSendStruct->fNonUnicastPacket = TRUE;      
        }
        
        
        
        if (ARP_ETYPE_ARP != net_short(ArpContext.pEthHeader->eh_type))
        {
            //
            //  This is not an Arp packet. Is this an IPv4 packet
            //
            if (IP_PROT_TYPE != net_short(ArpContext.pEthHeader->eh_type))
            {
                // If this is not an IPv4 packet, then mark it so that it can
                // be discarded
                pSendStruct->fNotIPv4Pkt = TRUE;
            }

           break;                            
        }

        //
        // We'll parse the structure using pre-defined structs
        //
        ArpContext.pArpPkt =  (PEPVC_ARP_PACKET)ArpContext.pEthHeader;

        ASSERT (ArpContext.BufferLength >= sizeof (EPVC_ARP_PACKET));

        if (ArpContext.BufferLength < sizeof (EPVC_ARP_PACKET))
        {
            //
            // TODO : Add Code to handle this case.
            // 
            break;
        }
        
        ArpContext.pBody =  (PEPVC_ARP_BODY)&ArpContext.pArpPkt->Body; 

        TRACE (TL_V, TM_Send, ("Received an ARP %p, Body %x\n", ArpContext.pEthHeader, ArpContext.pBody));


        //
        // Validate the Opcode, the prot type,  hard size, prot size
        //

        if (ARP_REQUEST  != net_short (ArpContext.pBody->opcode ))
        {
            //
            // This is not an Arp request
            //
            break;
        }


        if (IP_PROT_TYPE != net_short(ArpContext.pBody->pro) ||
            ARP_802_ADDR_LENGTH != ArpContext.pBody->hlen ||
            sizeof (IP_ADDR) != ArpContext.pBody->plen )
        {
            //
            // these are just sanity checks
            //
            ASSERT (!"Invalid ARP Packet");
            break;

        }

        //
        // We have a valid ArpRequest
        //
        ArpContext.fIsThisAnArp  = TRUE;

        //
        // If tcp/ip is arping for itself, then do not respond... but return
        //  TRUE, so that this packet is not sent on the wire
        //
        
        if (ArpContext.pArpPkt->Body.SenderIpAddr == ArpContext.pArpPkt->Body.DestIPAddr)
        {
            break;
        }

        //
        // Formulate and indicate an Arp Response
        //
        
        epvcFormulateArpResponse (pMiniport, &ArpContext, pSR);
        

    } while (FALSE);

    EXIT()

    return ArpContext.fIsThisAnArp ;
    TRACE (TL_T, TM_Send, ("<==epvcCheckAndReturnArps "));

}


NDIS_STATUS 
epvcRemoveEthernetHeader(
    PEPVC_SEND_STRUCT pSendStruct,  
    IN PRM_STACK_RECORD pSR
    )
/*++
Routine Description:

    Expects that the new packet is already set up with 
    the Ndis Bufferz

Arguments:
    pSendStruct - Contains all the arguments that are needed.

Return:
    True - if this is an Arp Request. 
--*/    
{
    ENTER ("epvcAddLLCEncapsulation" , 0x3ec589c9) 

    BOOLEAN             fUsedPktStack   = pSendStruct->fUsingStacks;
    NDIS_STATUS         NdisStatus      = NDIS_STATUS_FAILURE;
    PNDIS_PACKET        pNewPkt         = pSendStruct->pNewPacket; 
    PEPVC_I_MINIPORT    pMiniport       = pSendStruct->pMiniport;
    
    TRACE (TL_T, TM_Send, ("==>epvcRemoveEthernetHeader  "));




    do
    {
        ULONG BufferLength = 0; 
        PNDIS_BUFFER pBuffer = NULL;

        if (pMiniport->fDoIpEncapsulation == FALSE)
        {
            NdisStatus      = NDIS_STATUS_SUCCESS;

            break; // we are done
        }

        //
        // There are three ways we can be given a ether net header
        // 1. In a seperate MDL - most often 
        // 2. As part of a large MDL - We need to adhust the Virtual address
        // 3. EthernetHeader is seperated across multiple 
        //                      MDLs - not implemented or expected
        //

        pBuffer  = pNewPkt->Private.Head;

        BufferLength = NdisBufferLength (pBuffer);

        if (BufferLength < sizeof (EPVC_ETH_HEADER) )
        {
            
            ASSERTAndBreak (BufferLength >= sizeof (EPVC_ETH_HEADER)) ; // we are done 
        

        }

        //
        // At this point the first  buffer is going to be replaced so keep a record of it
        //
        pSendStruct->Context.Stack.ipv4Send.pOldHeadNdisBuffer = pBuffer;

        //
        // New we check to see if all we need to do is make the 
        // Packet->Private.Head point to the next MDL
        //
        if (BufferLength == sizeof (EPVC_ETH_HEADER))
        {
            //
            // These are error conditions that should not 
            // be handled in our software
            //
            ASSERT (pBuffer->Next != NULL); // no tcp header after the Eth header

            pNewPkt->Private.Head = pBuffer->Next;

            NdisStatus = NDIS_STATUS_SUCCESS;

            break ; // we are done

        }
        
        if (BufferLength > sizeof (EPVC_ETH_HEADER))
        {
            //
            // Allocate a new NDIS Buffer pointing to start of the IP header w
            // within the current Head (pBuffer)
            //
            PNDIS_BUFFER    pNewBuffer = NULL;
            PUCHAR          pIpHeader = NdisBufferVirtualAddress(pBuffer);
            UINT            LenRemaining = BufferLength - sizeof (EPVC_ETH_HEADER);

            if (pIpHeader == NULL)
            {
                //
                // we did not get the virtual address from the system.
                // Start to fail this packet
                //
                ASSERTAndBreak(pIpHeader != NULL);

            }

            //
            // Now move the Ip Header past the Ethernet Header (where it currently points to)
            //
            pIpHeader += sizeof (EPVC_ETH_HEADER)  ;

            //
            // Now allocate the new NdisBuffer
            //
            epvcAllocateBuffer ( &NdisStatus,
                                 &pNewBuffer,
                                 NULL,
                                 pIpHeader,
                                 LenRemaining);

            if (NdisStatus != NDIS_STATUS_SUCCESS) 
            {
                pNewBuffer  = NULL;
                ASSERTAndBreak (!"Ndis Buffer Allocation failed");
            }

            //
            // Make the New Buffer the Head of the new packet
            //
            // We might have to make it the tail if there is 
            // only one ndis buffer in the packet
            //
            if (pNewPkt->Private.Head  == pNewPkt->Private.Tail)
            {
                pNewPkt->Private.Tail = pNewBuffer;
            }

            pNewBuffer->Next= pNewPkt->Private.Head->Next;
            pNewPkt->Private.Head = pNewBuffer;
            

            NdisStatus = NDIS_STATUS_SUCCESS;

            break ; // we are done
        }



    } while (FALSE);


    TRACE (TL_T, TM_Send, ("<==epvcRemoveEthernetHeader  "));

    return NdisStatus ;

}


VOID
epvcSetPacketContext (
    IN PEPVC_SEND_STRUCT pSendStruct, 
    PRM_STACK_RECORD pSR
    )
/*++
Routine Description:

    No allocations, just add a few pointers and exit

Arguments:
    pSendStruct - Contains all the arguments that are needed.

Return:
    None:
    
--*/    
    
{

    PNDIS_PACKET        pPkt = pSendStruct->pNewPacket;
    PEPVC_PKT_CONTEXT pContext = NULL;
    PEPVC_STACK_CONTEXT pStack = NULL;
    //
    // first point the context to  the correct place 
    // in the new ndis pakcet
    //

    if (pSendStruct->fUsingStacks == TRUE)
    {   
        pStack = (PEPVC_STACK_CONTEXT)(&pSendStruct->pPktStack->IMReserved[0]);
    }
    else
    {
        PEPVC_PKT_CONTEXT pContext = NULL;

        pContext = (PEPVC_PKT_CONTEXT   )(&pPkt->ProtocolReserved[0]);

        pContext->pOriginalPacket = pSendStruct->pOldPacket;

        pStack = &pContext->Stack;

    }


    //
    // Update the packet
    //
    ASSERT (sizeof (pStack) <= (2 *sizeof (PVOID)  ));

    //
    // Now copy the stack portion of the context over
    // into the packet
    //
    *pStack = pSendStruct->Context.Stack;

    
}



NDIS_STATUS
epvcAddLLCEncapsulation (
    PEPVC_I_MINIPORT pMiniport , 
    PNDIS_PACKET pOldPkt,
    PNDIS_PACKET pNewPkt,
    PRM_STACK_RECORD pSR
    )
/*++
Routine Description:

    Expects that the new packet is already set up with 
    the Ndis Bufferz

Arguments:
    pSendStruct - Contains all the arguments that are needed.

Return:
    True - if this is an Arp Request. 
--*/    
{
    ENTER ("epvcAddLLCEncapsulation" , 0x3ec589c9) 
    BOOLEAN         fDoSend = TRUE;
    BOOLEAN         fUsedPktStack = (pOldPkt == pNewPkt);
    NDIS_STATUS     NdisStatus = NDIS_STATUS_SUCCESS;
    PNDIS_BUFFER    pNewBuffer = NULL;
    
    TRACE (TL_T, TM_Send, ("==>epvcAddLLCEncapsulation "));

    do
    {
        if (pMiniport->fAddLLCHeader == FALSE)
        {
            break; // we are done
        }
        

        //
        // Allocate an MDL that points to the LLC Header
        //
        epvcAllocateBuffer ( &NdisStatus,
                             &pNewBuffer,
                             NULL,
                             pMiniport->pLllcHeader,
                             pMiniport->LlcHeaderLength);

        if (NdisStatus != NDIS_STATUS_SUCCESS)
        {
            pNewBuffer = NULL;
            break;
        }
        

        //
        // Insert the New Buffer as the Head of the new Packet
        //
        pNewBuffer->Next = pNewPkt->Private.Head;
        pNewPkt->Private.Head = pNewBuffer;

        pNewPkt->Private.ValidCounts= FALSE;

        NdisStatus = NDIS_STATUS_SUCCESS;
        
    } while (FALSE);

    if (NdisStatus != NDIS_STATUS_SUCCESS)
    {   
        if (pNewBuffer!= NULL)
        {
            epvcFreeBuffer (pNewBuffer);
            pNewBuffer = NULL;
        }

    }

    TRACE (TL_T, TM_Send, ("<==epvcAddLLCEncapsulation "));

    return NdisStatus ;
}



NDIS_STATUS
epvcRemoveSendEncapsulation (
    PEPVC_I_MINIPORT pMiniport , 
    PNDIS_PACKET pNewPkt
    )
{

    return NDIS_STATUS_SUCCESS;
}


NDIS_STATUS
epvcRemoveRecvEncapsulation (
    PEPVC_I_MINIPORT pMiniport , 
    PNDIS_PACKET pNewPkt
    )
{

    return NDIS_STATUS_SUCCESS;
}


VOID
epvcDumpPkt (
    IN PNDIS_PACKET pPkt
    )
{

    PNDIS_BUFFER pPrevBuffer;

    do
    {
        PNDIS_BUFFER pBuffer = NULL;
    
        if (g_bDumpPackets == FALSE)
        {
            
            break;

        }

        pBuffer = pPkt->Private.Head;

        //
        // Now iterate through all the buffers 
        // and print out the packet. 
        //
        TRACE (TL_A, TM_Mp, ("pPkt %p, Head %p, tail %p\n ", 
                pPkt, pPkt->Private.Head, pPkt->Private.Tail));

        //
        // As we always expect the first buffer to be present
        // I do not check
        //
        do
        {
            PVOID pVa = NULL;
            ULONG Len = 0;
            pPrevBuffer = NULL;
            
            Len = NdisBufferLength (pBuffer);

            pVa = NdisBufferVirtualAddress(pBuffer);

            pPrevBuffer = pBuffer;
            pBuffer = pBuffer->Next;

            
            if (pVa == NULL)
            {
                continue;
            }

            DbgPrint ("Mdl %p, Va %p. Len %x\n", pPrevBuffer, pVa,Len);
            Dump( (CHAR* )pVa, Len, 0, 1 );                           

 
        } while (pBuffer != NULL);

    } while (FALSE);
}



NDIS_STATUS
epvcMiniportReadConfig(
    IN PEPVC_I_MINIPORT pMiniport,
    NDIS_HANDLE     WrapperConfigurationContext,
    PRM_STACK_RECORD pSR
    )
{   
    NDIS_STATUS     Status = NDIS_STATUS_SUCCESS;
    NDIS_HANDLE     ConfigurationHandle;
    PMP_REG_ENTRY   pRegEntry;
    UINT            i;
    UINT            value;
    PUCHAR          pointer;
    PNDIS_CONFIGURATION_PARAMETER ReturnedValue;
    PUCHAR          NetworkAddress;
    UINT            Length;

    // Open the registry for this pMiniport
    NdisOpenConfiguration(
        &Status,
        &ConfigurationHandle,
        WrapperConfigurationContext);
    if(Status != NDIS_STATUS_SUCCESS)
    {
        TRACE (TL_I, TM_Mp,("NdisOpenConfiguration failed\n"));
        return Status;
    }

    // read all the registry values 
    for(i = 0, pRegEntry = NICRegTable; i < NIC_NUM_REG_PARAMS; i++, pRegEntry++)
    {
        pointer = (PUCHAR) pMiniport + pRegEntry->FieldOffset;


        // Get the configuration value for a specific parameter.  Under NT the
        // parameters are all read in as DWORDs.
        NdisReadConfiguration(
            &Status,
            &ReturnedValue,
            ConfigurationHandle,
            &pRegEntry->RegName,
            NdisParameterInteger);


        // If the parameter was present, then check its value for validity.
        if(Status == NDIS_STATUS_SUCCESS)
        {
            // Check that param value is not too small or too large
            if(ReturnedValue->ParameterData.IntegerData < pRegEntry->Min ||
                ReturnedValue->ParameterData.IntegerData > pRegEntry->Max)
            {
                value = pRegEntry->Default;
            }
            else
            {
                value = ReturnedValue->ParameterData.IntegerData;
            }

            TRACE (TL_I, TM_Mp, ("= 0x%x", value));
        }
        else if(pRegEntry->bRequired)
        {
            TRACE (TL_I, TM_Mp,(" -- failed"));

            ASSERT(FALSE);

            Status = NDIS_STATUS_FAILURE;
            break;
        }
        else
        {
            value = pRegEntry->Default;
            TRACE (TL_I, TM_Mp,("= 0x%x (default)", value));
            Status = NDIS_STATUS_SUCCESS;
        }

        // Store the value in the pMiniport structure.
        switch(pRegEntry->FieldSize)
        {
            case 1:
                *((PUCHAR) pointer) = (UCHAR) value;
                break;

            case 2:
                *((PUSHORT) pointer) = (USHORT) value;
                break;

            case 4:
                *((PULONG) pointer) = (ULONG) value;
                break;

            default:
                TRACE (TL_I,TM_Mp, ("Bogus field size %d", pRegEntry->FieldSize));
                break;
        }
    }

    // Read NetworkAddress registry value 
    // Use it as the current address if any

    // Close the registry
    NdisCloseConfiguration(ConfigurationHandle);

    TRACE (TL_I, TM_Mp,("vci %d\n", pMiniport->config.vci));
    TRACE (TL_I, TM_Mp,("vpi %d\n", pMiniport->config.vpi));
    TRACE (TL_I, TM_Mp,("Encap Type %x\n", pMiniport->Encap));
    
    TRACE (TL_T, TM_Mp, ("<-- NICReadRegParameters, Status=%x", Status));

    return Status;
}

ULONG gDbgMpFlags =0;;
ULONG gRmFlags = 0;

NDIS_STATUS
epvcTaskCloseAddressFamily(
    IN  struct _RM_TASK *           pTask,
    IN  RM_TASK_OPERATION           Code,
    IN  UINT_PTR                    UserParam,
    IN  PRM_STACK_RECORD            pSR
    )

/*++

Routine Description:

    This is the task used to close the Af. It Deinitializes the miniport
    then calls the Close Miniport Task to Close the Af.

Arguments:

    ProtocolBindingContext  Pointer to the adapter structure
    Status                  Completion status

Return Value:

    None.

--*/

{
    ENTER ("epvcTaskCloseAddressFamily", 0x20a02c3f)
    NDIS_STATUS         Status      = NDIS_STATUS_FAILURE;
    PEPVC_I_MINIPORT    pMiniport   = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask);
    PTASK_AF            pAfTask     = (PTASK_AF) pTask;
    BOOLEAN             fNeedToHalt  = FALSE;
    BOOLEAN             fNeedToCancel = FALSE;
    BOOLEAN             fCloseAf  = FALSE;
    ULONG               State;

    enum 
    {
        Stage_Start =0, // default
        Stage_MiniportHalted,
        Stage_CloseAfComplete,
        Stage_TaskCompleted,
        Stage_End       
    
    }; // To be used in pTask->Hdr.State to indicate the state of the Task

    

    TRACE ( TL_T, TM_Pt, ("==> epvcTaskCloseAddressFamily  State %x", pTask->Hdr.State) );

    State = pTask->Hdr.State;
    
    switch(State)
    {
        case Stage_Start:
        {
            //
            // Check to see if the miniport has already opened an address family.
            // If so exit
            //
            LOCKOBJ (pMiniport, pSR );

            gDbgMpFlags  = pMiniport->Hdr.State;
            
            if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->af.pCloseAfTask)) == FALSE)
            {
                PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->af.pCloseAfTask);
                

                TRACE (TL_I, TM_Mp, (" Task is not primary\n"));

                RmTmpReferenceObject (&pOtherTask->Hdr, pSR);
                
            
                //
                // Set The state so we restart this code after main task completes 
                //

                pTask->Hdr.State = Stage_Start;
                UNLOCKOBJ(pMiniport, pSR);

                

                RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR);

                RmTmpDereferenceObject(&pOtherTask->Hdr,pSR);
                Status = NDIS_STATUS_PENDING;
                break;
            }

            //
            // We are the primary task
            //
            ASSERT (pMiniport->af.pCloseAfTask == pAfTask);
            //
            // Check to see if our work is already done
            //


            if (MiniportTestFlag (pMiniport, fMP_AddressFamilyOpened) == FALSE)
            {
                //
                // quietly exit as the address family is already closed
                //
                UNLOCKOBJ(pMiniport, pSR);
                ASSERT (MiniportTestFlag (pMiniport, fMP_AddressFamilyOpened) == TRUE);
                State = Stage_TaskCompleted;   // we're finished.
                Status = NDIS_STATUS_FAILURE; // Exit
                gRmFlags  = pTask->Hdr.RmState;
                break;
            }

            
            

            //
            // Now do we need to halt the miniport.
            //
            if (MiniportTestFlag (pMiniport, fMP_MiniportInitialized) == TRUE)
            {
                //
                // Our Halt Handler has not been called,
                //
                fNeedToHalt = TRUE;
                
            }
            else
            {
                //
                // We are not in the middle of a halt, so this probably
                // an unbind or Af_Close before the Init Handler.
                //
                // This task is not part of the halt code path
                //
                ASSERT (pAfTask->Cause != TaskCause_MiniportHalt);
                fNeedToCancel = TRUE;
            
            }

            if (fNeedToHalt || fNeedToCancel)
            {
                MiniportClearFlag (pMiniport, fMP_DevInstanceInitialized);

            }

            UNLOCKOBJ(pMiniport,pSR);

            
            //
            // Call Ndis to Deinitialize the miniport, The miniport is already Refed
            //
            TRACE ( TL_T, TM_Pt, ("epvcTaskCloseAddressFamily  ----") );

            if (TRUE == fNeedToHalt )
            {
                epvcIMDeInitializeDeviceInstance (pMiniport);
            }

            if (TRUE == fNeedToCancel )
            {
                epvcCancelDeviceInstance (pMiniport, pSR);
            }

            //
            // If the dev instance was not halted, then this thread has to close the Af
            //
            pTask->Hdr.State = Stage_MiniportHalted;
            
            if (fNeedToHalt == TRUE )
            {
                //
                // otherwise the work is over, the halt will close the address family
                //
                State = Stage_TaskCompleted;
                break;
            }

                
            FALL_THROUGH
        }
        
        case Stage_MiniportHalted:
        {   
            //
            // If the Af is still open, and the miniport halt was never fired, then 
            // issue the close Af
            //

            fCloseAf = (MiniportTestFlag(pMiniport, fMP_AddressFamilyOpened) == TRUE);
                                

            if (fCloseAf == TRUE)
            {
 
                PRM_TASK pAfTask = NULL;
                //
                // We need to start a task to complete the Close Call And DeleteVC
                //

                Status = epvcAllocateTask(
                    &pMiniport->Hdr,            // pParentObject,
                    epvcTaskCloseIMiniport, // pfnHandler,
                    0,                          // Timeout,
                    "Task: Close Miniport", // szDescription
                    &pAfTask ,
                    pSR
                    );

                if (FAIL(Status))
                {   
                    State = Stage_TaskCompleted;

                    pAfTask  = NULL;
                    ASSERT (Status == NDIS_STATUS_SUCCESS);
                    break;
                }

                ((PTASK_AF)pAfTask)->Cause = TaskCause_AfCloseRequest;

                //
                // Now we will pend the halt on the completion of the delete VC
                // task
                //
                pTask->Hdr.State = Stage_CloseAfComplete;

                
                
                RmPendTaskOnOtherTask(pTask,
                                      0,
                                      pAfTask,
                                      pSR
                                      );

                //
                // Start the Af TearDown
                //
                RmStartTask (pAfTask , 0, pSR);

                //
                // Exit - We expect to complete this task in another thread
                //
                Status = NDIS_STATUS_PENDING;
                break;
                                                    

            }
            else // Close Af == FALSE
            {
                State = Stage_TaskCompleted;
            }
            
            
            
            break;
                    
        }
        case Stage_CloseAfComplete:
        {
            State = Stage_TaskCompleted ;
            break;
        
        }

        case Stage_End:
        {
            Status = NDIS_STATUS_SUCCESS;
            break;
        }

        default:
        {
            ASSERTEX(!"Unknown task op", pTask);
        }
        

    }


    if (Stage_TaskCompleted == State )
    {
        pTask->Hdr.State = Stage_End;
        Status = NDIS_STATUS_SUCCESS;


        //
        // Clear the task here
        //
        
        LOCKOBJ(pMiniport, pSR);

        pMiniport->af.pCloseAfTask= NULL;

        UNLOCKOBJ(pMiniport, pSR);

        //
        // Set the complete event here
        //
            
        if (pAfTask->Cause == TaskCause_ProtocolUnbind)
        {
            epvcSetEvent (&pAfTask->CompleteEvent);

        }


    }


    RM_ASSERT_NOLOCKS(pSR);

    TRACE ( TL_T, TM_Pt, ("<== epvcTaskCloseAddressFamily Status %x", Status) );
    
    EXIT();
    return Status;


    
}


VOID
epvcInitializeMiniportLookasideLists (
    IN PEPVC_I_MINIPORT pMiniport
    )
/*++

Routine Description:

    Initialize all the lookaside lists in the adapter block

Arguments:


Return Value:

    None.

--*/
    
{
    USHORT DefaultDepth = 15;
    extern const UINT MaxEthernetFrameSize ;

    TRACE( TL_T, TM_Mp, ( "==> nicInitializeMiniportLookasideLists pMiniport %x ", pMiniport ) );


    switch (pMiniport->Encap) 
    {
        case IPV4_ENCAP_TYPE:
        case IPV4_LLC_SNAP_ENCAP_TYPE:
        {

            epvcInitializeLookasideList ( &pMiniport->arps.LookasideList,
                                        sizeof (EPVC_TASK),
                                        TAG_TASK,
                                        DefaultDepth );                                


            
            epvcInitializeLookasideList ( &pMiniport->rcv.LookasideList,
                                        sizeof (EPVC_IP_RCV_BUFFER),
                                        TAG_RCV ,
                                        DefaultDepth );                                




            break;
        }
    
        case ETHERNET_ENCAP_TYPE:
        case ETHERNET_LLC_SNAP_ENCAP_TYPE:
        {

            break;
        }

        default: 
        {


        }




    }

    TRACE( TL_T, TM_Mp, ( "<== nicInitializeMiniportLookasideLists  " ) );

}



VOID
epvcDeleteMiniportLookasideLists (
    IN PEPVC_I_MINIPORT pMiniport
    )
/*++

Routine Description:

    Delete all the lookaside lists in the adapter block

Arguments:


Return Value:

    None.

--*/

{
    TRACE( TL_T, TM_Mp, ( "== epvcDeleteMiniportLookasideLists pMiniport %x ", pMiniport) );


    //
    // Deletes the lookaside lists if they have been allocated
    //
    epvcDeleteLookasideList (&pMiniport->rcv.LookasideList);

    epvcDeleteLookasideList (&pMiniport->arps.LookasideList);




}



NDIS_STATUS
epvcInitializeMiniportPacketPools (
    IN PEPVC_I_MINIPORT pMiniport
    )

/*++

Routine Description:

    Initializr all the packet pools in the miniport 

Arguments:


Return Value:

    None.

--*/
    
{
    NDIS_STATUS Status = NDIS_STATUS_FAILURE;
    
    TRACE( TL_T, TM_Mp, ( "==> epvcInitializeMiniportPacketPools  pMiniport %x ", pMiniport ) );

    do
    {

        epvcAllocatePacketPool (&Status,
                                &pMiniport->PktPool.Send,
                                MIN_PACKET_POOL_SIZE,
                                MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE,
                                sizeof(EPVC_PKT_CONTEXT));

        if (Status != NDIS_STATUS_SUCCESS)
        {
            EPVC_ZEROSTRUCT (&pMiniport->PktPool.Send);
            ASSERT (Status == NDIS_STATUS_SUCCESS);
            break;
        }

 

        epvcAllocatePacketPool (&Status,
                                &pMiniport->PktPool.Recv,
                                MIN_PACKET_POOL_SIZE,
                                MAX_PACKET_POOL_SIZE - MIN_PACKET_POOL_SIZE,
                                PROTOCOL_RESERVED_SIZE_IN_PACKET);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            EPVC_ZEROSTRUCT (&pMiniport->PktPool.Recv);
            ASSERT (Status == NDIS_STATUS_SUCCESS);
            break;
        }

    } while ( FALSE);

    if (Status != NDIS_STATUS_SUCCESS)
    {
        epvcDeleteMiniportPacketPools (pMiniport);
        
    }

    TRACE( TL_T, TM_Mp, ( "<== epvcInitializeMiniportPacketPools  Status %x ", Status ) );

    return Status;
}



VOID
epvcDeleteMiniportPacketPools (
    IN PEPVC_I_MINIPORT pMiniport
    )
/*++

Routine Description:

    Delete all the packet pools in the miniport block

Arguments:


Return Value:

    None.

--*/
    
{
    
    TRACE( TL_T, TM_Mp, ( "== epvcDeleteMiniportPacketPools  pMiniport %x ", pMiniport ) );



        //
        // Freeing packet pools
        //
        if (pMiniport->PktPool.Recv.Handle != NULL)
        {
            epvcFreePacketPool (&pMiniport->PktPool.Recv);
        }

        if (pMiniport->PktPool.Send.Handle != NULL)
        {
            epvcFreePacketPool (&pMiniport->PktPool.Send);

        }
}



VOID
epvcInitializeMiniportParameters(
    PEPVC_I_MINIPORT pMiniport
    )
{

    //ipv4 - 0
    //ipv4 with llc header = 1
    //Ethernet - 2
    //Ethernet with llc header- 3

    //
    // Defaults for all flags are FALSE
    //
        
    pMiniport->fDoIpEncapsulation = FALSE;
    pMiniport->fAddLLCHeader  = FALSE;

    
    switch (pMiniport->Encap )
    {

        case IPV4_ENCAP_TYPE:
        {
            pMiniport->fDoIpEncapsulation = TRUE;
            pMiniport->MinAcceptablePkt =sizeof (IPHeader) ;
            pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE -EPVC_ETH_HEADERSIZE ;

            break;
        }

        case IPV4_LLC_SNAP_ENCAP_TYPE:
        {
            pMiniport->fAddLLCHeader = TRUE;
            pMiniport->fDoIpEncapsulation = TRUE;
            pMiniport->pLllcHeader = &LLCSnapIpv4[0];
            pMiniport->LlcHeaderLength = sizeof(LLCSnapIpv4);
            pMiniport->MinAcceptablePkt = sizeof (IPHeader) + sizeof(LLCSnapIpv4);
            pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE  + sizeof(LLCSnapIpv4)-EPVC_ETH_HEADERSIZE ;

            break;
        }

        case ETHERNET_LLC_SNAP_ENCAP_TYPE:
        {
            pMiniport->fAddLLCHeader = TRUE;
            pMiniport->pLllcHeader = &LLCSnapEthernet[0];
            pMiniport->LlcHeaderLength = sizeof(LLCSnapEthernet);
            pMiniport->MinAcceptablePkt = MIN_ETHERNET_SIZE + sizeof(LLCSnapEthernet);
            pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE +sizeof(LLCSnapEthernet);

            break;
        }

        case ETHERNET_ENCAP_TYPE:
        {

            pMiniport->MinAcceptablePkt = MIN_ETHERNET_SIZE;
            pMiniport->MaxAcceptablePkt = EPVC_MAX_FRAME_SIZE + EPVC_ETH_ENCAP_SIZE;
            break;
        }
            
        default: 
        {
            ASSERT (!"Not supported - defaulting to Ethernet Encapsulation");
            
        }



    }

}





NDIS_STATUS
epvcTaskHaltMiniport(
    IN  struct _RM_TASK *           pTask,
    IN  RM_TASK_OPERATION           Code,
    IN  UINT_PTR                    UserParam,
    IN  PRM_STACK_RECORD            pSR
    )
/*++

Routine Description:

    Task handler for opening address families on an underlying adapters.
    The number of address families instantiated is determined by the 
    configuration read in the registry

Arguments:
    
    UserParam   for (Code ==  RM_TASKOP_START)          : UnbindContext

--*/
{
    ENTER("epvcTaskHaltMiniport", 0xaac34d81)
    NDIS_STATUS         Status      = NDIS_STATUS_FAILURE;
    PEPVC_I_MINIPORT    pMiniport   = (PEPVC_I_MINIPORT ) RM_PARENT_OBJECT(pTask);
    PEPVC_ADAPTER       pAdapter = pMiniport->pAdapter;
    PTASK_HALT          pTaskHalt = (PTASK_HALT)pTask;
    BOOLEAN             fTaskCompleted = FALSE; 
    ULONG               State;  

    enum 
    {
        Stage_Start =0, // default
        Stage_DeleteVc,
        Stage_CloseAfComplete, 
        Stage_TaskCompleted,
        Stage_End       
    
    }; // To be used in pTask->Hdr.State to indicate the state of the Task

    TRACE(TL_T, TM_Mp, ("==>epvcTaskHaltMiniport State %x", pTask->Hdr.State));

    State = pTask->Hdr.State;
    
    switch (pTask->Hdr.State)
    {   
        case Stage_Start:
        {
            TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_Start"));


            //
            // Check to see if the miniport has already halting.
            // If so exit
            //
            LOCKOBJ (pMiniport, pSR );

            
            if (epvcIsThisTaskPrimary ( pTask, &(PRM_TASK)(pMiniport->pnp.pTaskHalt)) == FALSE)
            {
                PRM_TASK pOtherTask = (PRM_TASK)(pMiniport->pnp.pTaskHalt);

                RmTmpReferenceObject (&pOtherTask->Hdr, pSR);

                //
                // Set The state so we restart this code after main task completes 
                //

                pTask->Hdr.State = Stage_Start;
                UNLOCKOBJ(pMiniport, pSR);

                

                RmPendTaskOnOtherTask (pTask, 0, pOtherTask, pSR);

                RmTmpDereferenceObject(&pOtherTask->Hdr,pSR);
                Status = NDIS_STATUS_PENDING;
                break;
            }

            // We are the primary task and we have the lock
            //
            
            ASSERT (pMiniport->pnp.pTaskHalt == pTaskHalt);
            //
            // Lets close the Call and Delete the Vc
            //
            UNLOCKOBJ (pMiniport, pSR);
            

            if (MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded) == TRUE)
            {
                PRM_TASK pVcTask = NULL;
                //
                // We need to start a task to complete the Close Call And DeleteVC
                //

                Status = epvcAllocateTask(
                    &pMiniport->Hdr,            // pParentObject,
                    epvcTaskVcTeardown, // pfnHandler,
                    0,                          // Timeout,
                    "Task: TearDown Vc",    // szDescription
                    &pVcTask ,
                    pSR
                    );

                if (FAIL(Status))
                {
                    fTaskCompleted = TRUE;

                    pVcTask  = NULL;
                    ASSERT (Status == NDIS_STATUS_SUCCESS);
                    break;
                }

                //
                // Now we will pend the halt on the completion of the delete VC
                // task
                //
                pTask->Hdr.State = Stage_DeleteVc;


                
                RmPendTaskOnOtherTask(pTask,
                                      0,
                                      pVcTask,
                                      pSR
                                      );

                //
                // Start the Vc TearDown
                //
                RmStartTask (pVcTask , 0, pSR);

                //
                // Exit - We expect to complete this task in another thread
                //
                Status = NDIS_STATUS_PENDING;
                break;

            }
            else //if (MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded) == TRUE)
            {
                pTask->Hdr.State = Stage_DeleteVc;
                //
                // Continue On - the Vc has already been deleted
                //

            }
            

        }

        case Stage_DeleteVc:
        {
            //
            // Now we check to see if the address family is still
            // open for this miniport
            //
            TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_DeleteVc"));


            if (MiniportTestFlag(pMiniport, fMP_AddressFamilyOpened) == TRUE)
            {
                PRM_TASK pAfTask = NULL;
                //
                // We need to start a task to complete the Close Call And DeleteVC
                //

                Status = epvcAllocateTask(
                    &pMiniport->Hdr,            // pParentObject,
                    epvcTaskCloseIMiniport, // pfnHandler,
                    0,                          // Timeout,
                    "Task: Close Miniport", // szDescription
                    &pAfTask ,
                    pSR
                    );

                if (FAIL(Status))
                {
                    fTaskCompleted = TRUE;

                    pAfTask  = NULL;
                    ASSERT (Status == NDIS_STATUS_SUCCESS);
                    break;
                }

                ((PTASK_AF)pAfTask)->Cause = TaskCause_MiniportHalt;

                //
                // Now we will pend the halt on the completion of the delete VC
                // task
                //
                pTask->Hdr.State = Stage_CloseAfComplete;

                
                
                RmPendTaskOnOtherTask(pTask,
                                      0,
                                      pAfTask,
                                      pSR
                                      );

                //
                // Start the Af TearDown
                //
                RmStartTask (pAfTask , 0, pSR);

                //
                // Exit - We expect to complete this task in another thread
                //
                Status = NDIS_STATUS_PENDING;
                break;

            }
            else //if (MiniportTestFlag (pMiniport, fMP_MakeCallSucceeded) == TRUE)
            {

                pTask->Hdr.State = Stage_CloseAfComplete;

                //
                // Continue On - the Af has already been deleted
                //

            }
            
    
        }
        case Stage_CloseAfComplete: 
        {
            //
            // Free all miniport resources here .- packet pools etc.
            //
            TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_CloseAfComplete"));

            //
            // Freeing Lookaside lists
            //
            epvcDeleteMiniportLookasideLists (pMiniport);

            //
            // Freeing packet pools
            //
            epvcDeleteMiniportPacketPools(pMiniport);
            
            //
            // If the miniport is halting we do not shut down the protocol's adapter 
            // object
            //
            fTaskCompleted = TRUE;
            Status = NDIS_STATUS_SUCCESS;
            break;


        }

        case Stage_TaskCompleted:
        {
            ASSERT(0);
            break;
        }
        case Stage_End:     
        {
            TRACE (TL_V, TM_Mp, (" Task Halt miniport Stage_End"));
            Status = NDIS_STATUS_SUCCESS;
            break;
        }
        default:
        {
            ASSERT (pTask->Hdr.State <= Stage_End);
        }


    } // end of switch 


    //
    // if this thread has completed the postprocessing,
    // then signal the event.
    //

    if (TRUE == fTaskCompleted)
    {
        BOOLEAN fSetWaitEvent = FALSE;
        TRACE (TL_V, TM_Mp, ("Task Halt Miniport - Stage End"));
        pTask->Hdr.State = Stage_End;
        if (FAIL(Status))
        {

            ASSERT (0);
        }

        LOCKOBJ (pMiniport, pSR);

        pMiniport->pnp.pTaskHalt = NULL;

        if (MiniportTestFlag (pMiniport, fMP_WaitingForHalt)== TRUE)
        {
            MiniportClearFlag (pMiniport, fMP_WaitingForHalt);
            fSetWaitEvent = TRUE;
        }
        
        UNLOCKOBJ (pMiniport, pSR);

        // 
        //  This first event is for the MiniportHalt handler 
        // which fired off this task
        // 
        epvcSetEvent (&pTaskHalt->CompleteEvent);

        //
        // This second event is for the epvcMiniportDoUnbind
        // which wants to wait until the Halt is complete ,
        // before it shuts off the lower binding to the phy. adapter
        //
        if (fSetWaitEvent)
        {
            epvcSetEvent (&pMiniport->pnp.HaltCompleteEvent);
        }
  

        Status = NDIS_STATUS_SUCCESS;
    }

    
    TRACE(TL_T, TM_Mp, ("<==epvcTaskHaltMiniport Status %x", Status));



    EXIT()
    RM_ASSERT_NOLOCKS(pSR);
    return Status;
}


NDIS_STATUS 
epvcAddEthernetTail(
    PEPVC_SEND_STRUCT pSendStruct,  
    IN PRM_STACK_RECORD pSR
    )
/*++
Routine Description:

    Makes sure the ethernet packet is greater than 64 bytes

Arguments:
    pSendStruct - Contains all the arguments that are needed.

Return:
    Success -   if the padding was not needed or the MDL 
                was successfully appended
--*/    
{
    ENTER ("epvcAddEthernetTail" , 0x3ec589c9) 

    NDIS_STATUS         NdisStatus      = NDIS_STATUS_FAILURE;
    PNDIS_PACKET        pNewPkt         = pSendStruct->pNewPacket; 
    PEPVC_I_MINIPORT    pMiniport       = pSendStruct->pMiniport;
    ULONG               PacketLength   = 0;
    ULONG               LengthRemaining = 0;
    PNDIS_BUFFER        pNewTailBuffer = NULL;
    PNDIS_BUFFER        pLastBuffer;
    
    TRACE (TL_T, TM_Send, ("==>epvcAddEthernetTail"));



    do
    {
        ULONG BufferLength = 0; 
        PNDIS_BUFFER pBuffer = NULL;

        if (pMiniport->fDoIpEncapsulation == TRUE)
        {
            NdisStatus      = NDIS_STATUS_SUCCESS;

            break; // we are done
        }

        //
        // Check the length of the Ethernet packet
        //
        NdisQueryPacketLength(pNewPkt, &PacketLength);

        //
        // Is the packet length greater than 64
        //
        if (PacketLength >= MINIMUM_ETHERNET_LENGTH)
        {
            NdisStatus= NDIS_STATUS_SUCCESS;
            break;
        }

        //
        // Add padding to fill up the minimum Ethernet frame length.
        // This is a new buffer that is appended to the original
        // NDIS_BUFFER chain.
        //
        LengthRemaining = MINIMUM_ETHERNET_LENGTH - PacketLength;

        NdisAllocateBuffer(&NdisStatus, &pNewTailBuffer, NULL, &gPaddingBytes,LengthRemaining);

        if (NdisStatus != NDIS_STATUS_SUCCESS || pNewTailBuffer == NULL)
        {
            pNewTailBuffer = NULL;
            NdisStatus = NDIS_STATUS_RESOURCES;
            break;
        }

        //
        // Append the new buffer to the tail of the packet.
        //

        //
        // Locate the last NDIS_BUFFER in the packet. Do it the hard
        // way since Packet->Private.Tail is not reliable:
        //
        pLastBuffer = pNewPkt->Private.Head;

        while (pLastBuffer->Next != NULL)
        {
            pLastBuffer = pLastBuffer->Next;
        }

    
        //
        // Save a pointer to this last MDL so that we can set its
        // Next field back to NULL when we complete this send.
        //
        pSendStruct->Context.Stack.EthernetSend.pOldLastNdisBuffer = pLastBuffer;

        //
        // Append the new buffer to the tail of the chain.
        //
        pLastBuffer->Next = pNewTailBuffer;
        pNewTailBuffer->Next = NULL;


        //
        // Update our packet.
        //
        pNewPkt->Private.Tail = pNewTailBuffer;
        pNewPkt->Private.ValidCounts = FALSE;
        
        NdisStatus = NDIS_STATUS_SUCCESS;

        break ; // we are done

    } while (FALSE);

    if (NdisStatus != NDIS_STATUS_SUCCESS && pNewTailBuffer != NULL)
    {
        NdisFreeBuffer (pNewTailBuffer);
    }


    TRACE (TL_T, TM_Send, ("<==epvcAddEthernetTail  "));

    return NdisStatus ;

}



VOID
epvcRemoveEthernetTail (
    IN PEPVC_I_MINIPORT pMiniport,
    IN PNDIS_PACKET pPacket,
    IN PEPVC_PKT_CONTEXT pContext
    )
/*++
Routine Description:

    Removes the extra MDL that was added to make 
    this packet greater than MINIUMUM_ETHERNET_SIZE

    Used for Ethernet , Eth +LLC Encapsulations only 
    
Arguments:
    pMiniport - Miniport structure
    pPacket - Packet allocated by EPVC
    pContext - Context of the packet - used to store the original last mdl

    
Return:
    None
--*/    
{
    PNDIS_BUFFER pOldLastNdisBuffer = NULL;

    do
    {

        //
        // Ethernet encapsulation ? If not, then exit
        //
            
        if (pMiniport->fDoIpEncapsulation == TRUE)
        {
            break; // there was no ethernet encapsulation, so exit
        }

        //                    
        // if there is no old buffer, then we can exit
        //
        pOldLastNdisBuffer = pContext->Stack.EthernetSend.pOldLastNdisBuffer;
        
        if (pOldLastNdisBuffer == NULL)
        {
            break;
        }

        //
        // Free the last buffer in the packet (this is the padding
        // we added for a runt packet).
        //
        NdisFreeBuffer(pPacket->Private.Tail);

        //
        // Set the Next pointer of the original "last buffer" to NULL.
        //
        pOldLastNdisBuffer->Next = NULL;
        
                
    } while (FALSE);

}



NDIS_STATUS 
epvcAddEthernetPad(
    PEPVC_SEND_STRUCT pSendStruct,  
    IN PRM_STACK_RECORD pSR
    )
/*++
Routine Description:

    Makes sure the ethernet packet w/o LLC header has a pad of 
    0x00, 0x00 

Arguments:
    pSendStruct - Contains all the arguments that are needed.

Return:
    Success -   if the padding was not needed or the MDL 
                was successfully added
--*/    
{
    ENTER ("epvcAddEthernetPad" , 0x3ec589c9) 

    NDIS_STATUS         NdisStatus      = NDIS_STATUS_FAILURE;
    PNDIS_PACKET        pNewPkt         = pSendStruct->pNewPacket; 
    PEPVC_I_MINIPORT    pMiniport       = pSendStruct->pMiniport;
    PNDIS_BUFFER        pPaddingBuffer = NULL;
    
    TRACE (TL_T, TM_Send, ("==>epvcAddEthernetPad"));



    do
    {
        ULONG BufferLength = 0; 
        PNDIS_BUFFER pBuffer = NULL;

        if (pMiniport->Encap != ETHERNET_ENCAP_TYPE)
        {
            NdisStatus      = NDIS_STATUS_SUCCESS;
            break; // we are done
        }

        //
        // It is pure Ethernet. We need to precede the packet
        // with a 00,00
        //

        NdisAllocateBuffer(&NdisStatus, 
                        &pPaddingBuffer, 
                        NULL, 
                        &gPaddingBytes,
                        ETHERNET_PADDING_LENGTH);

        if (NdisStatus != NDIS_STATUS_SUCCESS || pPaddingBuffer == NULL)
        {
            pPaddingBuffer = NULL;
            NdisStatus = NDIS_STATUS_RESOURCES;
            break;
        }

        //
        // no more allocations - we cannot fail from here
        //
        NdisStatus = NDIS_STATUS_SUCCESS;

        //
        // Add  the new buffer to the head of the packet
        //
        NdisChainBufferAtFront(pNewPkt,pPaddingBuffer);

 
        break ; // we are done
        


    } while (FALSE);

    if (NdisStatus != NDIS_STATUS_SUCCESS && pPaddingBuffer != NULL)
    {
        NdisFreeBuffer (pPaddingBuffer);
    }


    TRACE (TL_T, TM_Send, ("<==epvcAddEthernetPad  "));

    return NdisStatus ;

}


VOID
epvcRemoveEthernetPad (
    IN PEPVC_I_MINIPORT pMiniport,
    IN PNDIS_PACKET pPacket
    )
/*++
Routine Description:

    Removes the padding  that was added to the 
    head of the packet Ethernet Head
    
    Used for Ethernet Encapsulation only 
    
Arguments:
    pMiniport - Miniport structure
    pPacket - Packet
    
Return:
    None
--*/    
{
    PNDIS_BUFFER pPaddingBuffer= NULL;

    do
    {

        if (pMiniport->Encap != ETHERNET_ENCAP_TYPE)
        {
            break; // we are done
        }

        //                    
        // it is in pure ethernet mode - remove the Padding
        //

        //
        // First - a simple sanity check
        //
        {
            PNDIS_BUFFER pBuffer = pPacket->Private.Head;
            ULONG PaddingLength = NdisBufferLength(pBuffer);
            
            if (PaddingLength !=ETHERNET_PADDING_LENGTH)
            {
                // this is not our MDL 
                ASSERT (PaddingLength !=ETHERNET_PADDING_LENGTH);
                break;
            }
        } 
        
        //
        // Free the padding buffer at the front of the Packet
        //
        
        NdisUnchainBufferAtFront(pPacket,&pPaddingBuffer );

        NdisFreeBuffer (pPaddingBuffer );
        
        
    } while (FALSE);


}



VOID
epvcCancelDeviceInstance(
    IN PEPVC_I_MINIPORT pMiniport ,
    IN PRM_STACK_RECORD pSR
    )
/*++

Routine Description:

    This function cancels an outstanding Device Instance. 
    If the NDIS call fails. it waits for an event in the miniprot to fire. 
    After that it goes ahead and DeInitializes the Device Instance
    
Arguments:
    pMiniport - Miniport in question.

Return Value:
    Success
--*/
{
    ENTER("epvcCancelDeviceInstance", 0x0e42d778)
    NDIS_STATUS Status = NDIS_STATUS_SUCCESS;        
    UINT iteration =0;
    BOOLEAN bWaitSuccess = FALSE;
    BOOLEAN fNeedToInitEvent = FALSE;

    do
    {
        LOCKOBJ (pMiniport, pSR);

        // Prepare the event, and mark the structure as being Canceled
        epvcResetEvent (&pMiniport->pnp.DeInitEvent);

        // Set the flag to mark it as cancelled           
        MiniportSetFlag (pMiniport, fMP_MiniportCancelInstance);

        UNLOCKOBJ (pMiniport, pSR);

        // Cancel the device instance
        Status = epvcIMCancelInitializeDeviceInstance(pMiniport);
                                                      

        if (Status == NDIS_STATUS_SUCCESS)
        {
            break;
        }   

            
        //
        // If the Cancel has not Succeeded then we should wait for 
        // the Initialize to complete
        //
        {
            BOOLEAN bWaitSuccessful;

            
            bWaitSuccessful = epvcWaitEvent (&pMiniport->pnp.DeInitEvent,WAIT_INFINITE);                                    


            if (bWaitSuccessful == FALSE)
            {
                ASSERT (bWaitSuccessful == TRUE);
            }
            

        }
        //
        // If cancel fails. Wait for the miniport to be initialized
        //
        
        ASSERT (pMiniport->ndis.MiniportAdapterHandle != NULL);

        //
        // If cancel fails. Wait for the miniport to be initialized
        //

        TRACE (TL_N, TM_Mp, ("Call DeInit after Cancel failed %p , ",pMiniport));
        
        epvcIMDeInitializeDeviceInstance (pMiniport);
        
        Status = NDIS_STATUS_SUCCESS;

    } while (FALSE);

    LOCKOBJ(pMiniport, pSR);

    MiniportClearFlag (pMiniport, fMP_MiniportCancelInstance);

    UNLOCKOBJ (pMiniport, pSR);

    return ;
}