Sample Code

Windows Driver Samples/ NDIS MUX Intermediate Driverand Notify Object/ C++/ driver/ 60/ miniport.c/

/*++
Copyright (c) 1992-2000  Microsoft Corporation

Module Name:

    miniport.c

Abstract:

    NDIS Miniport Entry points and utility functions for the NDIS
    MUX Intermediate Miniport sample. The driver exposes zero or more
    Virtual Ethernet LANs (VELANs) as NDIS miniport instances over
    each lower (protocol-edge) binding to an underlying adapter.

Environment:

    Kernel mode.

Revision History:


--*/

#include "precomp.h"
#pragma hdrstop

#define MODULE_NUMBER           MODULE_MINI

NDIS_OID VElanSupportedOids[] =
{
    OID_GEN_SUPPORTED_LIST,
    OID_GEN_HARDWARE_STATUS,
    OID_GEN_MEDIA_SUPPORTED,
    OID_GEN_MEDIA_IN_USE,
    OID_GEN_MAXIMUM_LOOKAHEAD,
    OID_GEN_MAXIMUM_FRAME_SIZE,
    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_VENDOR_ID,
    OID_GEN_VENDOR_DESCRIPTION,
    OID_GEN_VENDOR_DRIVER_VERSION,
    OID_GEN_CURRENT_PACKET_FILTER,
    OID_GEN_CURRENT_LOOKAHEAD,
    OID_GEN_DRIVER_VERSION,
    OID_GEN_MAXIMUM_TOTAL_SIZE,
    OID_GEN_PROTOCOL_OPTIONS,
    OID_GEN_MAC_OPTIONS,
    OID_GEN_MEDIA_CONNECT_STATUS,
    OID_GEN_MAXIMUM_SEND_PACKETS,
    OID_GEN_XMIT_OK,
    OID_GEN_RCV_OK,
    OID_GEN_XMIT_ERROR,
    OID_GEN_RCV_ERROR,
    OID_GEN_RCV_NO_BUFFER,
    OID_GEN_RCV_CRC_ERROR,
    OID_GEN_TRANSMIT_QUEUE_LENGTH,
    OID_GEN_STATISTICS,
    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_802_3_XMIT_DEFERRED,
    OID_802_3_XMIT_MAX_COLLISIONS,
    OID_802_3_RCV_OVERRUN,
    OID_802_3_XMIT_UNDERRUN,
    OID_802_3_XMIT_HEARTBEAT_FAILURE,
    OID_802_3_XMIT_TIMES_CRS_LOST,
    OID_802_3_XMIT_LATE_COLLISIONS,
    OID_PNP_CAPABILITIES,
    OID_PNP_SET_POWER,
    OID_PNP_QUERY_POWER,
    OID_PNP_ADD_WAKE_UP_PATTERN,
    OID_PNP_REMOVE_WAKE_UP_PATTERN,
#if IEEE_VLAN_SUPPORT
    OID_GEN_VLAN_ID,
#endif    
    OID_PNP_ENABLE_WAKE_UP
};


NDIS_STATUS
MPInitialize(
    IN  NDIS_HANDLE                     MiniportAdapterHandle,
    IN  NDIS_HANDLE                     MiniportDriverContext,
    IN  PNDIS_MINIPORT_INIT_PARAMETERS  MiniportInitParameters
    )
/*++

Routine Description:

    This is the Miniport Initialize routine which gets called as a
    result of our call to NdisIMInitializeDeviceInstanceEx.
    The context parameter which we pass there is the VELan structure
    which we retrieve here.

Arguments:

    MiniportAdapterHandle       NDIS handle for this miniport
    MiniportDriverContext       Handle passed to NDIS when we registered the driver
    MiniportInitParameters      Miniport initialization parameters such
                                as our device context, resources, etc.

Return Value:

    NDIS_STATUS_SUCCESS unless something goes wrong

--*/
{
    PVELAN                          pVElan;
    UINT                            i;
    NDIS_STATUS                     Status = NDIS_STATUS_FAILURE;
    NDIS_HANDLE                     ConfigurationHandle;
    PVOID                           NetworkAddress;
    LOCK_STATE                      LockState;
    NET_IFINDEX                     HigherLayerIfIndex, LowerLayerIfIndex;
    NDIS_MINIPORT_ADAPTER_ATTRIBUTES MiniportAttributesContent;
    const PNDIS_MINIPORT_ADAPTER_ATTRIBUTES  MiniportAttributes = &MiniportAttributesContent;
    
 

#if IEEE_VLAN_SUPPORT
    NDIS_STRING                     strVlanId = NDIS_STRING_CONST("VlanID");
    PNDIS_CONFIGURATION_PARAMETER   Params;
#endif


    //NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES   RegistrationAttributes;
    //NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES        GeneralAttributes;
    NDIS_CONFIGURATION_OBJECT                       ConfigObject;


    UNREFERENCED_PARAMETER(MiniportDriverContext);
    
    //
    // Start off by retrieving our virtual miniport context (VELAN) and 
    // storing the Miniport handle in it.
    //

    pVElan = MiniportInitParameters->IMDeviceInstanceContext;

    DBGPRINT(MUX_LOUD, ("==> MPInitialize: VELAN %p\n", pVElan));

    ASSERT(pVElan != NULL);
    ASSERT(pVElan->pAdapt != NULL);
    NdisZeroMemory(MiniportAttributes,sizeof(NDIS_MINIPORT_ADAPTER_ATTRIBUTES));

    do
    {
        pVElan->MiniportAdapterHandle = MiniportAdapterHandle;
        //
        // Create an ioctl interface
        //
        (VOID)PtRegisterDevice();

        //
        // register this miniport with NDIS
        //

        //NdisZeroMemory(&RegistrationAttributes, sizeof(NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES));
        //NdisZeroMemory(&GeneralAttributes, sizeof(NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES));

        //
        // setting registration attributes
        //
        MiniportAttributesContent.RegistrationAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES;
        MiniportAttributesContent.RegistrationAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES_REVISION_1;
        MiniportAttributesContent.RegistrationAttributes.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_REGISTRATION_ATTRIBUTES);

        MiniportAttributesContent.RegistrationAttributes.MiniportAdapterContext = (NDIS_HANDLE)pVElan;


        MiniportAttributesContent.RegistrationAttributes.AttributeFlags = NDIS_MINIPORT_ATTRIBUTES_NO_HALT_ON_SUSPEND;

        MiniportAttributesContent.RegistrationAttributes.CheckForHangTimeInSeconds = 0;
        MiniportAttributesContent.RegistrationAttributes.InterfaceType = 0;
        

        
        NDIS_DECLARE_MINIPORT_ADAPTER_CONTEXT(VELAN);
        Status = NdisMSetMiniportAttributes(MiniportAdapterHandle,
                                            MiniportAttributes);

        

        if (Status != NDIS_STATUS_SUCCESS)
        {
            break;
        }
        

        //
        // Access configuration parameters for this miniport.
        //
        ConfigObject.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
        ConfigObject.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
        ConfigObject.Header.Size = sizeof(NDIS_CONFIGURATION_OBJECT);
        ConfigObject.NdisHandle = pVElan->MiniportAdapterHandle;
        ConfigObject.Flags = 0;

        Status = NdisOpenConfigurationEx(
                    &ConfigObject,
                    &ConfigurationHandle);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            break;
        }


        NdisReadNetworkAddress(
            &Status,
            &NetworkAddress,
            &i,
            ConfigurationHandle);

        //
        // If there is a NetworkAddress override, use it 
        //
        if (((Status == NDIS_STATUS_SUCCESS) 
                && (i == ETH_LENGTH_OF_ADDRESS))
                && ((!ETH_IS_MULTICAST(NetworkAddress)) 
                && (ETH_IS_LOCALLY_ADMINISTERED (NetworkAddress))))
        {
            
            ETH_COPY_NETWORK_ADDRESS(
                        pVElan->CurrentAddress,
                        NetworkAddress);
        }
        else
        {
            MPGenerateMacAddr(pVElan);
        }

        //
        // ignore error reading the network address
        //
        Status = NDIS_STATUS_SUCCESS;
   
#if IEEE_VLAN_SUPPORT
        //
        // Read VLAN ID
        //
        NdisReadConfiguration(
                &Status,
                &Params,
                ConfigurationHandle,
                &strVlanId,
                NdisParameterInteger);
        if (Status == NDIS_STATUS_SUCCESS)
        {
            //
            // Check for out of bound
            //
            if (Params->ParameterData.IntegerData > VLAN_ID_MAX)
            {
                pVElan->VlanId = VLANID_DEFAULT;
            }
            else
            {
                pVElan->VlanId = Params->ParameterData.IntegerData;
            }
        }

        else
        {
            
            pVElan->VlanId = VLANID_DEFAULT;
            Status = NDIS_STATUS_SUCCESS;
        }
#endif    


        NdisCloseConfiguration(ConfigurationHandle);



        //
        // set up generic attributes
        //

        MiniportAttributesContent.GeneralAttributes.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES;
        MiniportAttributesContent.GeneralAttributes.Header.Revision = NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES_REVISION_1;
        MiniportAttributesContent.GeneralAttributes.Header.Size = sizeof(NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES);   

        MiniportAttributesContent.GeneralAttributes.MediaType = VELAN_MEDIA_TYPE;
        MiniportAttributesContent.GeneralAttributes.MtuSize = pVElan->pAdapt->BindParameters.MtuSize;
        MiniportAttributesContent.GeneralAttributes.MaxXmitLinkSpeed = pVElan->pAdapt->BindParameters.MaxXmitLinkSpeed;
        MiniportAttributesContent.GeneralAttributes.MaxRcvLinkSpeed = pVElan->pAdapt->BindParameters.MaxRcvLinkSpeed;
        MiniportAttributesContent.GeneralAttributes.XmitLinkSpeed = pVElan->pAdapt->BindParameters.XmitLinkSpeed;
        MiniportAttributesContent.GeneralAttributes.RcvLinkSpeed = pVElan->pAdapt->BindParameters.RcvLinkSpeed;
        

        MUX_ACQUIRE_ADAPT_READ_LOCK(pVElan->pAdapt, &LockState);
        
        //
        // Miniport below has indicated some status indication
        //
        MiniportAttributesContent.GeneralAttributes.MediaConnectState = pVElan->pAdapt->LastIndicatedLinkState.MediaConnectState;
        MiniportAttributesContent.GeneralAttributes.MediaDuplexState = pVElan->pAdapt->LastIndicatedLinkState.MediaDuplexState;
        MiniportAttributesContent.GeneralAttributes.XmitLinkSpeed = pVElan->pAdapt->LastIndicatedLinkState.XmitLinkSpeed;
        MiniportAttributesContent.GeneralAttributes.RcvLinkSpeed = pVElan->pAdapt->LastIndicatedLinkState.RcvLinkSpeed;
        
        pVElan->LastIndicatedStatus = NDIS_STATUS_LINK_STATE;

        pVElan->LastIndicatedLinkState = pVElan->pAdapt->LastIndicatedLinkState;

        
        MiniportAttributesContent.GeneralAttributes.LookaheadSize = pVElan->pAdapt->BindParameters.LookaheadSize;
        MiniportAttributesContent.GeneralAttributes.MaxMulticastListSize = pVElan->pAdapt->BindParameters.MaxMulticastListSize;
        MiniportAttributesContent.GeneralAttributes.MacAddressLength = pVElan->pAdapt->BindParameters.MacAddressLength;
        
        MiniportAttributesContent.GeneralAttributes.PhysicalMediumType = pVElan->pAdapt->BindParameters.PhysicalMediumType ;
        MiniportAttributesContent.GeneralAttributes.AccessType = pVElan->pAdapt->BindParameters.AccessType ; 
        MiniportAttributesContent.GeneralAttributes.DirectionType = pVElan->pAdapt->BindParameters.DirectionType;         
        MiniportAttributesContent.GeneralAttributes.ConnectionType = pVElan->pAdapt->BindParameters.ConnectionType ; 
        MiniportAttributesContent.GeneralAttributes.IfType = pVElan->pAdapt->BindParameters.IfType ; 
        MiniportAttributesContent.GeneralAttributes.IfConnectorPresent = FALSE; // RFC 2665 TRUE if physical adapter

        if (pVElan->pAdapt->BindParameters.RcvScaleCapabilities)
        {
            MiniportAttributesContent.GeneralAttributes.RecvScaleCapabilities = pVElan->pAdapt->BindParameters.RcvScaleCapabilities;
        }
        else
        {
            MiniportAttributesContent.GeneralAttributes.RecvScaleCapabilities = NULL;
        }
        
        MiniportAttributesContent.GeneralAttributes.MacOptions = NDIS_MAC_OPTION_NO_LOOPBACK;


#if IEEE_VLAN_SUPPORT
        MiniportAttributesContent.GeneralAttributes.MacOptions |= (NDIS_MAC_OPTION_8021P_PRIORITY |
                                        NDIS_MAC_OPTION_8021Q_VLAN);

#endif
        
        MiniportAttributesContent.GeneralAttributes.SupportedPacketFilters = pVElan->pAdapt->BindParameters.SupportedPacketFilters;

        MiniportAttributesContent.GeneralAttributes.SupportedStatistics = NDIS_STATISTICS_XMIT_OK_SUPPORTED |
                                                NDIS_STATISTICS_RCV_OK_SUPPORTED |
                                                NDIS_STATISTICS_XMIT_ERROR_SUPPORTED |
                                                NDIS_STATISTICS_RCV_ERROR_SUPPORTED |
                                                NDIS_STATISTICS_RCV_CRC_ERROR_SUPPORTED |
                                                NDIS_STATISTICS_RCV_NO_BUFFER_SUPPORTED |
                                                NDIS_STATISTICS_TRANSMIT_QUEUE_LENGTH_SUPPORTED |
                                                NDIS_STATISTICS_GEN_STATISTICS_SUPPORTED;

        
        NdisMoveMemory(&MiniportAttributesContent.GeneralAttributes.CurrentMacAddress,
                       &pVElan->CurrentAddress,
                       ETH_LENGTH_OF_ADDRESS);

        NdisMoveMemory(&MiniportAttributesContent.GeneralAttributes.PermanentMacAddress,
                       &pVElan->PermanentAddress,
                       ETH_LENGTH_OF_ADDRESS);

        MiniportAttributesContent.GeneralAttributes.PowerManagementCapabilities = &pVElan->pAdapt->PowerManagementCapabilities;
        MiniportAttributesContent.GeneralAttributes.SupportedOidList = VElanSupportedOids;
        MiniportAttributesContent.GeneralAttributes.SupportedOidListLength = sizeof(VElanSupportedOids);
        MUX_RELEASE_ADAPT_READ_LOCK(pVElan->pAdapt, &LockState);
        Status = NdisMSetMiniportAttributes(MiniportAdapterHandle,MiniportAttributes);

        pVElan->MiniportInitPending = FALSE;
    } while (FALSE);

    
    //
    // If we had received an UnbindAdapter notification on the underlying
    // adapter, we would have blocked that thread waiting for the IM Init
    // process to complete. Wake up any such thread.
    //
    // See PtUnbindAdapter for more details.
    //
    //

    if (Status == NDIS_STATUS_SUCCESS)
    {
        //
        // we should set this to FALSE only if we successfully initialized the adapter
        // otherwise the unbind routine will wait forever for this VElan to go away
        //
        pVElan->MiniportInitPending = FALSE;
        //
        // Save the IfIndex for this VELAN
        //
        HigherLayerIfIndex = MiniportInitParameters->IfIndex;
        LowerLayerIfIndex = pVElan->pAdapt->BindParameters.BoundIfIndex;
            
        Status = NdisIfAddIfStackEntry(HigherLayerIfIndex,
                                       LowerLayerIfIndex);

        if (Status == NDIS_STATUS_SUCCESS)
        {
            pVElan->IfIndex = HigherLayerIfIndex;                
        }

        //
        // Ignore if the add fails
        //
        Status = NDIS_STATUS_SUCCESS;

    }
    else
    {        
        pVElan->MiniportAdapterHandle = NULL;
    }

    if (pVElan->MiniportInitPending == TRUE)
    {
        pVElan->MiniportInitPending = FALSE;
    }
    
    // TODO: check to see if we can set the init event in a failure case?
    
    NdisSetEvent(&pVElan->MiniportInitEvent);

    DBGPRINT(MUX_LOUD, ("<== MPInitialize: VELAN %p, Status %x\n", pVElan, Status));

    return Status;
}

NDIS_STATUS
MPQueryInformation(
    IN    PVELAN                    pVElan,
    IN    PNDIS_OID_REQUEST         NdisRequest
    )
/*++

Routine Description:

    This function is called to handle the query request specified by NdisRequest
    All query requests are first handled right here, since this is a virtual
    device (not pass-through).

Arguments:

    MiniportAdapterContext      Pointer to the adapter structure
    NdisRequest                 Specify the query request.

Return Value:

    NDIS_STATUS_SUCCESS         
    NDIS_STATUS_NOT_SUPPORTED
    Return code from the MPForwardOidRequest below.

--*/
{
    NDIS_STATUS                 Status = NDIS_STATUS_SUCCESS;
    NDIS_HARDWARE_STATUS        HardwareStatus = NdisHardwareStatusReady;
    NDIS_MEDIUM                 Medium = VELAN_MEDIA_TYPE;
    UCHAR                       VendorDesc[] = VELAN_VENDOR_DESC;
    ULONG                       ulInfo;
    ULONG64                     ulInfo64;
    USHORT                      usInfo;
    PVOID                       pInfo = (PVOID)&ulInfo;
    ULONG                       ulInfoLen = sizeof(ulInfo), NeededLength = 0;
    // Should we forward the request to the miniport below?
    BOOLEAN                     bForwardRequest = FALSE;

    NDIS_OID                    Oid;
    PVOID                       InformationBuffer;
    ULONG                       InformationBufferLength;
    PULONG                      BytesWritten;
    PULONG                      BytesNeeded;
    NDIS_STATISTICS_INFO        StatisticsInfo;


    DBGPRINT(MUX_LOUD, ("==> MPQueryInformation: VElan %p, Request %p\n",pVElan, NdisRequest));
    
    Oid = NdisRequest->DATA.QUERY_INFORMATION.Oid;
    InformationBuffer = NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer;
    InformationBufferLength = NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength;
    BytesWritten = (ULONG*) &(NdisRequest->DATA.QUERY_INFORMATION.BytesWritten);
    BytesNeeded = (ULONG*) &(NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded);
    
    
    // Initialize the result
    *BytesWritten = 0;
    *BytesNeeded = 0;

    switch (Oid)
    {
        case OID_GEN_SUPPORTED_LIST:
            pInfo = (PVOID) VElanSupportedOids;
            ulInfoLen = sizeof(VElanSupportedOids);
            break;

        case OID_GEN_SUPPORTED_GUIDS:
            //
            // Do NOT forward this down, otherwise we will
            // end up with spurious instances of private WMI
            // classes supported by the lower driver(s).
            //
            Status = NDIS_STATUS_NOT_SUPPORTED;
            break;

        case OID_GEN_HARDWARE_STATUS:
            pInfo = (PVOID) &HardwareStatus;
            ulInfoLen = sizeof(NDIS_HARDWARE_STATUS);
            break;

        case OID_GEN_MEDIA_SUPPORTED:
        case OID_GEN_MEDIA_IN_USE:
            pInfo = (PVOID) &Medium;
            ulInfoLen = sizeof(NDIS_MEDIUM);
            break;

        case OID_GEN_CURRENT_LOOKAHEAD:
        case OID_GEN_MAXIMUM_LOOKAHEAD:         
            ulInfo = pVElan->LookAhead;
            pInfo = (PVOID) &ulInfo;
            break;            
            
        case OID_GEN_MAXIMUM_FRAME_SIZE:
            ulInfo = ETH_MAX_PACKET_SIZE - ETH_HEADER_SIZE;
#if IEEE_VLAN_SUPPORT
            ulInfo -= VLAN_TAG_HEADER_SIZE;
            
#endif
            pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_MAXIMUM_TOTAL_SIZE:
        case OID_GEN_TRANSMIT_BLOCK_SIZE:
        case OID_GEN_RECEIVE_BLOCK_SIZE:
            ulInfo = (ULONG) ETH_MAX_PACKET_SIZE;
#if IEEE_VLAN_SUPPORT
            ulInfo -= VLAN_TAG_HEADER_SIZE;
#endif    
            pInfo = (PVOID) &ulInfo;
            break;
            
        case OID_GEN_MAC_OPTIONS:
            ulInfo = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | 
                     NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
                     NDIS_MAC_OPTION_NO_LOOPBACK;
#if IEEE_VLAN_SUPPORT
            ulInfo |= (NDIS_MAC_OPTION_8021P_PRIORITY |
                       NDIS_MAC_OPTION_8021Q_VLAN);        
#endif
            pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_LINK_SPEED:
            bForwardRequest = TRUE;
            break;

        case OID_GEN_TRANSMIT_BUFFER_SPACE:
            ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusySends;
#if IEEE_VLAN_SUPPORT
            ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusySends;
#endif          
            pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_RECEIVE_BUFFER_SPACE:
            ulInfo = ETH_MAX_PACKET_SIZE * pVElan->MaxBusyRecvs;
#if IEEE_VLAN_SUPPORT
            ulInfo -= VLAN_TAG_HEADER_SIZE * pVElan->MaxBusyRecvs;        
#endif            
             pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_VENDOR_ID:
            ulInfo = VELAN_VENDOR_ID;
            pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_VENDOR_DESCRIPTION:
            pInfo = VendorDesc;
            ulInfoLen = sizeof(VendorDesc);
            break;
            
        case OID_GEN_VENDOR_DRIVER_VERSION:
            ulInfo = VELAN_VENDOR_ID;
            pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_DRIVER_VERSION:
            usInfo = (USHORT) VELAN_DRIVER_VERSION;
            pInfo = (PVOID) &usInfo;
            ulInfoLen = sizeof(USHORT);
            break;

        case OID_802_3_PERMANENT_ADDRESS:
            pInfo = pVElan->PermanentAddress;
            ulInfoLen = ETH_LENGTH_OF_ADDRESS;
            break;

        case OID_802_3_CURRENT_ADDRESS:
            pInfo = pVElan->CurrentAddress;
            ulInfoLen = ETH_LENGTH_OF_ADDRESS;
            break;

        case OID_802_3_MAXIMUM_LIST_SIZE:
            ulInfo = VELAN_MAX_MCAST_LIST;
            pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_MAXIMUM_SEND_PACKETS:
            ulInfo = VELAN_MAX_SEND_PKTS;
            pInfo = (PVOID) &ulInfo;
            break;

        case OID_GEN_MEDIA_CONNECT_STATUS:
            //
            // Get this from the adapter below.
            //
            bForwardRequest = TRUE;
            break;

        case OID_PNP_QUERY_POWER:
            // simply succeed this.
            ulInfoLen = sizeof(ULONG);
            ulInfo = 0;
            break;

        case OID_PNP_CAPABILITIES:
        case OID_PNP_WAKE_UP_PATTERN_LIST:
            //
            // Pass down these power management/PNP OIDs.
            //
            bForwardRequest = TRUE;
            break;

        case OID_GEN_XMIT_OK:
            ulInfo64 = pVElan->GoodTransmits;
            pInfo = &ulInfo64;
            if (InformationBufferLength >= sizeof(ULONG64) ||
                InformationBufferLength == 0)
            {
                ulInfoLen = sizeof(ULONG64);
            }
            else
            {
                ulInfoLen = sizeof(ULONG);
            }
            NeededLength = sizeof(ulInfo64);

            break;
    
        case OID_GEN_RCV_OK:
            ulInfo64 = pVElan->GoodReceives;
            pInfo = &ulInfo64;
            if (InformationBufferLength >= sizeof(ULONG64) ||
                InformationBufferLength == 0)
            {
                ulInfoLen = sizeof(ULONG64);
            }
            else
            {
                ulInfoLen = sizeof(ULONG);
            }

            NeededLength = sizeof(ulInfo64);
            
            break;
    
        case OID_GEN_XMIT_ERROR:
            ulInfo = pVElan->TxAbortExcessCollisions +
                pVElan->TxDmaUnderrun +
                pVElan->TxLostCRS +
                pVElan->TxLateCollisions+
                pVElan->TransmitFailuresOther;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_GEN_RCV_ERROR:
            ulInfo = pVElan->RcvCrcErrors +
                pVElan->RcvAlignmentErrors +
                pVElan->RcvDmaOverrunErrors +
                pVElan->RcvRuntErrors;
#if IEEE_VLAN_SUPPORT
            ulInfo +=
                (pVElan->RcvVlanIdErrors +
                pVElan->RcvFormatErrors);           
#endif
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_GEN_RCV_NO_BUFFER:
            ulInfo = pVElan->RcvResourceErrors;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_GEN_RCV_CRC_ERROR:
            ulInfo = pVElan->RcvCrcErrors;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_GEN_TRANSMIT_QUEUE_LENGTH:
            ulInfo = pVElan->RegNumTcb;
            pInfo = (PVOID) &ulInfo;
            break;
        
        case OID_GEN_STATISTICS:
            ulInfoLen = sizeof (NDIS_STATISTICS_INFO);
            NdisZeroMemory(&StatisticsInfo, sizeof(NDIS_STATISTICS_INFO));

            StatisticsInfo.Header.Revision = NDIS_OBJECT_REVISION_1;
            StatisticsInfo.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
            StatisticsInfo.Header.Size = sizeof(NDIS_STATISTICS_INFO);
            StatisticsInfo.SupportedStatistics = NDIS_STATISTICS_FLAGS_VALID_RCV_DISCARDS          |
                                                 NDIS_STATISTICS_FLAGS_VALID_RCV_ERROR             |
                                                 NDIS_STATISTICS_FLAGS_VALID_XMIT_ERROR;
                    
            StatisticsInfo.ifInDiscards = pVElan->RcvCrcErrors +
                                          pVElan->RcvAlignmentErrors +
                                          pVElan->RcvResourceErrors +
                                          pVElan->RcvDmaOverrunErrors +
                                          pVElan->RcvRuntErrors;

#if IEEE_VLAN_SUPPORT
            StatisticsInfo.ifInDiscards += (pVElan->RcvVlanIdErrors +
                                            pVElan->RcvFormatErrors);
#endif
            
            StatisticsInfo.ifInErrors = StatisticsInfo.ifInDiscards -
                                            pVElan->RcvResourceErrors;

            
            StatisticsInfo.ifOutErrors = pVElan->TxAbortExcessCollisions +
                                         pVElan->TxDmaUnderrun +
                                         pVElan->TxLostCRS +
                                         pVElan->TxLateCollisions;

            pInfo = &StatisticsInfo;
            break;

        case OID_802_3_RCV_ERROR_ALIGNMENT:
            ulInfo = pVElan->RcvAlignmentErrors;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_ONE_COLLISION:
        	ulInfo = pVElan->OneRetry;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_MORE_COLLISIONS:
        	ulInfo = pVElan->MoreThanOneRetry;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_DEFERRED:
        	ulInfo = pVElan->TxOKButDeferred;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_MAX_COLLISIONS:
            ulInfo = pVElan->TxAbortExcessCollisions;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_RCV_OVERRUN:
            ulInfo = pVElan->RcvDmaOverrunErrors;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_UNDERRUN:
            ulInfo = pVElan->TxDmaUnderrun;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_HEARTBEAT_FAILURE:
            ulInfo = pVElan->TxLostCRS;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_TIMES_CRS_LOST:
            ulInfo = pVElan->TxLostCRS;
            pInfo = (PVOID) &ulInfo;
            break;
    
        case OID_802_3_XMIT_LATE_COLLISIONS:
            ulInfo = pVElan->TxLateCollisions;
            pInfo = (PVOID) &ulInfo;
            break;
   
#if IEEE_VLAN_SUPPORT            
        case OID_GEN_VLAN_ID:
            ulInfo = pVElan->VlanId;
            pInfo = (PVOID) &ulInfo;
            break;

#endif

        default:
            Status = NDIS_STATUS_NOT_SUPPORTED;
            break;
    }

    if (bForwardRequest == FALSE)
    {
        //
        // No need to forward this request down.
        //
        if (Status == NDIS_STATUS_SUCCESS)
        {
            if (ulInfoLen <= InformationBufferLength)
            {
                // Copy result into InformationBuffer
                *BytesWritten = ulInfoLen;
                if(ulInfoLen)
                {
                    NdisMoveMemory(InformationBuffer, pInfo, ulInfoLen);
                    
                    if (NeededLength > ulInfoLen)
                    {
                        *BytesNeeded = NeededLength;
                    }
                }
             

            }
            else
            {
                // too short
                *BytesNeeded = (NeededLength > ulInfoLen ? NeededLength : ulInfoLen);
             
                Status = NDIS_STATUS_BUFFER_TOO_SHORT;
            }
        }
    }
    else
    {
     

        //
        // Send this request to the binding below.
        //
        Status = MPForwardOidRequest(pVElan,NdisRequest);
    }

    if ((Status != NDIS_STATUS_SUCCESS) &&
        (Status != NDIS_STATUS_PENDING))
    {
        DBGPRINT(MUX_WARN, ("MPQueryInformation VELAN %p, OID 0x%08x, Status = 0x%08x\n",
                    pVElan, Oid, Status));
    }

 

    DBGPRINT(MUX_LOUD, ("<== MPQueryInformation: VElan %p, Request %p returning %08lx\n",pVElan, NdisRequest, Status));
    
    return(Status);

}


NDIS_STATUS
MPSetInformation(
    IN    PVELAN                    pVElan,
    IN    PNDIS_OID_REQUEST         NdisRequest
    )
/*++

Routine Description:

    This is the handler for an set request operation. Relevant
    requests are forwarded down to the lower miniport for handling.

Arguments:

    MiniportAdapterContext      Pointer to the adapter structure
    NdisRequest                 Specify the set request

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_NOT_SUPPORTED
    NDIS_STATUS_INVALID_LENGTH
    Return code from the MPForwardOidRequest below.

--*/
{
    NDIS_STATUS             Status = NDIS_STATUS_SUCCESS;
    ULONG                   PacketFilter;
    NDIS_DEVICE_POWER_STATE NewDeviceState;

    NDIS_OID                Oid;
    PVOID                   InformationBuffer;
    ULONG                   InformationBufferLength;
    PULONG                  BytesRead;
    PULONG                  BytesNeeded;
    
    // Should we forward the request to the miniport below?
    BOOLEAN                 bForwardRequest = FALSE;
    NDIS_STATUS_INDICATION  StatusIndication;

    DBGPRINT(MUX_LOUD, ("==> MPSetInformation: VElan %p, Request %p\n", pVElan, NdisRequest));
    
    NdisZeroMemory(&StatusIndication, sizeof(NDIS_STATUS_INDICATION));
    Oid = NdisRequest->DATA.SET_INFORMATION.Oid;
    InformationBuffer = NdisRequest->DATA.SET_INFORMATION.InformationBuffer;
    InformationBufferLength = NdisRequest->DATA.SET_INFORMATION.InformationBufferLength;
    BytesRead = (ULONG*) &(NdisRequest->DATA.SET_INFORMATION.BytesRead);
    BytesNeeded = (ULONG*) &(NdisRequest->DATA.SET_INFORMATION.BytesNeeded);

    *BytesRead = 0;
    *BytesNeeded = 0;

    switch (Oid)
    {
        //
        // Let the miniport below handle these OIDs:
        //
        case OID_PNP_ADD_WAKE_UP_PATTERN:
        case OID_PNP_REMOVE_WAKE_UP_PATTERN:
        case OID_PNP_ENABLE_WAKE_UP:
            bForwardRequest = TRUE;
            break;

        case OID_PNP_SET_POWER:
            //
            // Store new power state and succeed the request.
            //
            *BytesNeeded = sizeof(NDIS_DEVICE_POWER_STATE);
            if (InformationBufferLength < *BytesNeeded)
            {
                Status = NDIS_STATUS_INVALID_LENGTH;
                break;
            }
           
            NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE)InformationBuffer);
            
            //
            // Check if the VELAN adapter goes from lower power state to D0
            // 
            if ((MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) 
                    && (!MUX_IS_LOW_POWER_STATE(NewDeviceState)))
            {
                //
                // Indicate the media status is necessary
                // 
                if (pVElan->LastIndicatedStatus != pVElan->LatestUnIndicateStatus)
                {
                    
                    StatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
                    StatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
                    StatusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION);
                    
                    StatusIndication.SourceHandle = pVElan->MiniportAdapterHandle;
                    StatusIndication.StatusCode = pVElan->LatestUnIndicateStatus;
                    if (pVElan->LatestUnIndicateStatus == NDIS_STATUS_LINK_STATE)
                    {
                        StatusIndication.StatusBuffer = &pVElan->LatestUnIndicateLinkState;
                        StatusIndication.StatusBufferSize = sizeof(NDIS_LINK_STATE);
                            
                    }
                    else
                    {
                        StatusIndication.StatusBuffer = NULL;
                        StatusIndication.StatusBufferSize = 0;
                    }
                    
                    NdisMIndicateStatusEx(pVElan->MiniportAdapterHandle, &StatusIndication);
                                        
                    pVElan->LastIndicatedStatus = pVElan->LatestUnIndicateStatus;

                    if (pVElan->LatestUnIndicateStatus == NDIS_STATUS_LINK_STATE)
                    {
                        pVElan->LastIndicatedLinkState = pVElan->LatestUnIndicateLinkState;
                    }
                        
                }
                else
                {
                    if (pVElan->LastIndicatedStatus == NDIS_STATUS_LINK_STATE)
                    {
                        if (!NdisEqualMemory(&pVElan->LatestUnIndicateLinkState,
                                             &pVElan->LastIndicatedLinkState,
                                             sizeof(NDIS_LINK_STATE)))
                        {
                        
                            StatusIndication.Header.Type = NDIS_OBJECT_TYPE_STATUS_INDICATION;
                            StatusIndication.Header.Revision = NDIS_STATUS_INDICATION_REVISION_1;
                            StatusIndication.Header.Size = sizeof(NDIS_STATUS_INDICATION);
                    
                            StatusIndication.SourceHandle = pVElan->MiniportAdapterHandle;
                            StatusIndication.StatusCode = pVElan->LatestUnIndicateStatus;
                            StatusIndication.StatusBuffer = &pVElan->LatestUnIndicateLinkState;
                            StatusIndication.StatusBufferSize = sizeof(NDIS_LINK_STATE);
                            
                            NdisMIndicateStatusEx(pVElan->MiniportAdapterHandle, &StatusIndication);

                            pVElan->LastIndicatedStatus = pVElan->LatestUnIndicateStatus;
                            pVElan->LastIndicatedLinkState = pVElan->LatestUnIndicateLinkState;
                        }
                    }
                } 
            }
            //
            // Check if the VELAN adapter goes from D0 to lower power state
            // 
            if ((!MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState)) 
                    && (MUX_IS_LOW_POWER_STATE(NewDeviceState)))
            {
                //
                //  Initialize LastUnIndicateStatus 
                // 
                pVElan->LatestUnIndicateStatus = pVElan->LastIndicatedStatus;

                if (pVElan->LastIndicatedStatus == NDIS_STATUS_LINK_STATE)
                {
                    pVElan->LatestUnIndicateLinkState = pVElan->LastIndicatedLinkState;
                }
            }
            
            NdisMoveMemory(&pVElan->MPDevicePowerState,
                           InformationBuffer,
                           *BytesNeeded);

            DBGPRINT(MUX_INFO, ("SetInfo: VElan %p, new miniport power state --- %d\n",
                    pVElan, pVElan->MPDevicePowerState));

            break;

        case OID_802_3_MULTICAST_LIST:
            Status = MPSetMulticastList(pVElan,
                                        InformationBuffer,
                                        InformationBufferLength,
                                        BytesRead,
                                        BytesNeeded);
            break;

        case OID_GEN_CURRENT_PACKET_FILTER:
            if (InformationBufferLength != sizeof(ULONG))
            {
                Status = NDIS_STATUS_INVALID_LENGTH;
                *BytesNeeded = sizeof(ULONG);
                break;
            }

            NdisMoveMemory(&PacketFilter, InformationBuffer, sizeof(ULONG));
            *BytesRead = sizeof(ULONG);

            Status = MPSetPacketFilter(pVElan,
                                       PacketFilter);
            break;

        case OID_GEN_CURRENT_LOOKAHEAD:
            if (InformationBufferLength < sizeof(ULONG))
            {
                Status = NDIS_STATUS_INVALID_LENGTH;
                *BytesNeeded = sizeof(ULONG);
                break;
            }
#if IEEE_VLAN_SUPPORT
            //
            // In order to simplify parsing and to avoid excessive
            // copying, we need the tag header also to be present in the
            // lookahead buffer. Make sure that the driver below
            // includes that.
            //
            if (*(UNALIGNED PULONG)InformationBuffer < VLAN_TAG_HEADER_SIZE)
            {
                pVElan->RestoreLookaheadSize = TRUE;
                *(UNALIGNED PULONG)InformationBuffer += VLAN_TAG_HEADER_SIZE;
            }
#endif            
            bForwardRequest = TRUE;
            break;
            
#if IEEE_VLAN_SUPPORT
        case OID_GEN_VLAN_ID:
            if (InformationBufferLength == sizeof(ULONG))
            {
                NdisMoveMemory((&pVElan->VlanId), InformationBuffer, sizeof(ULONG));

            } 
            else
            {
                *BytesNeeded = sizeof(ULONG);
                 Status = NDIS_STATUS_INVALID_LENGTH;
            }

            break;
#endif
            
        default:
            Status = NDIS_STATUS_NOT_SUPPORTED;
            break;

    }
    
    if (bForwardRequest == FALSE)
    {
        if (Status == NDIS_STATUS_SUCCESS)
        {
            *BytesRead = InformationBufferLength;
        }
    }
    else
    {
        //
        // Send this request to the binding below.
        //
        Status = MPForwardOidRequest(pVElan,NdisRequest);
    }

    DBGPRINT(MUX_LOUD, ("<== MPSetInformation: VElan %p, Request %p returning %08lx\n",pVElan, NdisRequest, Status));
    
    return(Status);
}

NDIS_STATUS
MPMethodRequest(
    IN    PVELAN                  pVElan,
    IN    PNDIS_OID_REQUEST       NdisRequest
    )
/*++
Routine Description:

    WMI method request handler

Arguments:

    MiniportAdapterContext          Pointer to the adapter structure
    NdisRequest                     Pointer to the request sent down by NDIS

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_NOT_SUPPORTED
   

--*/
{
    NDIS_OID        Oid;
    ULONG           MethodId;
    PVOID           InformationBuffer;
    ULONG           InputBufferLength;
    ULONG           OutputBufferLength;
    ULONG           BytesNeeded;
    NDIS_STATUS     Status = NDIS_STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(pVElan);
    
    DBGPRINT(MUX_LOUD, ("==> MPMethodRequest: VElan %p, Request %p\n", pVElan, NdisRequest));


    Oid = NdisRequest->DATA.METHOD_INFORMATION.Oid;
    InformationBuffer = (PVOID)(NdisRequest->DATA.METHOD_INFORMATION.InformationBuffer);
    InputBufferLength = NdisRequest->DATA.METHOD_INFORMATION.InputBufferLength;
    OutputBufferLength = NdisRequest->DATA.METHOD_INFORMATION.OutputBufferLength;
    MethodId = NdisRequest->DATA.METHOD_INFORMATION.MethodId;
    UNREFERENCED_PARAMETER(Oid);
    UNREFERENCED_PARAMETER(InformationBuffer);
    UNREFERENCED_PARAMETER(InputBufferLength);
    UNREFERENCED_PARAMETER(OutputBufferLength);
    UNREFERENCED_PARAMETER(MethodId);


    BytesNeeded = 0;

    switch(Oid)
    {
        default:
            Status = NDIS_STATUS_NOT_SUPPORTED;
            break;
    }


    DBGPRINT(MUX_LOUD, ("<== MPMethodRequest: VElan %p, Request %p returning %08lx\n",pVElan, NdisRequest, Status));

    return Status;

}



NDIS_STATUS
MPOidRequest(
    IN    NDIS_HANDLE             MiniportAdapterContext,
    IN    PNDIS_OID_REQUEST       NdisRequest
    )
/*++
Routine Description:

    MiniportRequest dispatch handler

Arguments:

    MiniportAdapterContext      Pointer to the adapter structure
    NdisRequest                 Pointer to NDIS_OID_REQUEST sent down by NDIS.

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_NOT_SUPPORTED
    NDIS_STATUS_XXX

--*/
{
    PVELAN                  pVElan = (PVELAN)MiniportAdapterContext;
    NDIS_REQUEST_TYPE       RequestType;
    NDIS_STATUS             Status;

    DBGPRINT(MUX_LOUD,("==> MPOidRequest: Request %p\n", NdisRequest));

    RequestType = NdisRequest->RequestType;

    switch(RequestType)
    {
        case NdisRequestMethod:
            Status = MPMethodRequest(pVElan, NdisRequest);
            break;

        case NdisRequestSetInformation:
            Status = MPSetInformation(pVElan, NdisRequest);
            break;

        case NdisRequestQueryInformation:
        case NdisRequestQueryStatistics:
            Status = MPQueryInformation(pVElan, NdisRequest);
            break;

        default:
            Status = NDIS_STATUS_NOT_SUPPORTED;
            break;
    }

    DBGPRINT(MUX_LOUD,("<== MPOidRequest: Request %p, Status %08lx\n", NdisRequest, Status));

    return Status;
}



VOID
MPHalt(
    IN    NDIS_HANDLE                MiniportAdapterContext,
    IN    NDIS_HALT_ACTION           HaltAction
    )
/*++

Routine Description:

    Halt handler. Add any further clean-up for the VELAN to this
    function.

    We wait for all pending I/O on the VELAN to complete and then
    unlink the VELAN from the adapter.

Arguments:

    MiniportAdapterContext    Pointer to the pVElan
    HaltAction                The reason adapter is being halted 

Return Value:

    None.

--*/
{
    PVELAN            pVElan = (PVELAN)MiniportAdapterContext;
    NET_IFINDEX       LowerLayerIfIndex;

    UNREFERENCED_PARAMETER(HaltAction);
    

    DBGPRINT(MUX_LOUD, ("==> MPHalt: VELAN %p\n", pVElan));

    //
    // Mark the VELAN so that we don't send down any new requests or
    // sends to the adapter below, or new receives/indications to
    // protocols above.
    //
    pVElan->MiniportHalting = TRUE;

    //
    // Update the packet filter on the underlying adapter if needed.
    //
    if (pVElan->PacketFilter != 0)
    {
        MPSetPacketFilter(pVElan, 0);
    }

    //
    // Wait for any outstanding sends or requests to complete.
    //
    while (pVElan->OutstandingSends)
    {
        DBGPRINT(MUX_INFO, ("MPHalt: VELAN %p has %d outstanding sends\n",
                            pVElan, pVElan->OutstandingSends));
        NdisMSleep(20000);
    }

    //
    // Wait for all outstanding indications to be completed and
    // any pended receive packets to be returned to us.
    //
    while (pVElan->OutstandingReceives)
    {
        DBGPRINT(MUX_INFO, ("MPHalt: VELAN %p has %d outstanding receives\n",
                            pVElan, pVElan->OutstandingReceives));
        NdisMSleep(20000);
    }


    //
    // Delete the ioctl interface that was created when the miniport
    // was created.
    //
    (VOID)PtDeregisterDevice();


    //
    // Delete stack entry for this Velan
    //
    if (pVElan->IfIndex != 0)
    {
        LowerLayerIfIndex = pVElan->pAdapt->BindParameters.BoundIfIndex;
        
        NdisIfDeleteIfStackEntry(pVElan->IfIndex,
                                 LowerLayerIfIndex);
        pVElan->IfIndex = 0;
    }
    

    //
    // Unlink the VELAN from its parent ADAPT structure. This will
    // dereference the VELAN.
    //
    pVElan->MiniportAdapterHandle = NULL;
    PtUnlinkVElanFromAdapter(pVElan);
    
    DBGPRINT(MUX_LOUD, ("<== MPHalt: pVElan %p\n", pVElan));
}


NDIS_STATUS
MPForwardOidRequest(
    IN PVELAN                       pVElan,
    IN PNDIS_OID_REQUEST            Request
    )
/*++

Routine Description:

    Utility routine that forwards an NDIS request made on a VELAN to the
    lower binding. Since at most a single request can be pended on a VELAN,
    we use the pre-allocated request structure embedded in the VELAN struct.

Arguments:

    pVElan                Pointer to a VElan Adapter
    Request               Pointer to an NDIS request to be forwarded to the below adapter.   

Return Value:

    NDIS_STATUS_PENDING if a request was sent down.
    NDIS_STATUS_XXX     Otherwise.

--*/
{
    NDIS_STATUS                  Status;
    PMUX_NDIS_REQUEST            pMuxNdisRequest = &pVElan->Request;

    PADAPT                       pAdapt = pVElan->pAdapt;
    

    DBGPRINT(MUX_LOUD, ("==> MPForwardOidRequest: VELAN %p, Request %p\n", pVElan, Request));

    do
    {
        MUX_INCR_PENDING_SENDS(pVElan);

        //
        // If the miniport below is going away, fail the request
        // 
        NdisAcquireSpinLock(&pVElan->Lock);
        if (pVElan->DeInitializing == TRUE)
        {
            NdisReleaseSpinLock(&pVElan->Lock);
            MUX_DECR_PENDING_SENDS(pVElan);
            Status = NDIS_STATUS_FAILURE;
            break;
        }
        NdisReleaseSpinLock(&pVElan->Lock);    

        //
        // If the virtual miniport edge is at a low power
        // state, fail this request.
        //
        if (MUX_IS_LOW_POWER_STATE(pVElan->MPDevicePowerState))
        {
            MUX_DECR_PENDING_SENDS(pVElan);
            Status = NDIS_STATUS_ADAPTER_NOT_READY;
            break;
        }
        
        NdisAcquireSpinLock(&pVElan->Lock);
        pMuxNdisRequest->Cancelled = FALSE;
        pMuxNdisRequest->OrigRequest = Request;
        pMuxNdisRequest->pCallback = PtCompleteForwardedRequest;
        pMuxNdisRequest->Request.RequestType = Request->RequestType;
        pMuxNdisRequest->Refcount = 1;
        NdisReleaseSpinLock(&pVElan->Lock);    

        pMuxNdisRequest->Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST;
        pMuxNdisRequest->Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1;
        pMuxNdisRequest->Request.Header.Size = sizeof(NDIS_OID_REQUEST);

        switch (Request->RequestType)
        {
            case NdisRequestQueryInformation:
            case NdisRequestQueryStatistics:
                pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.Oid = Request->DATA.QUERY_INFORMATION.Oid;
                pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBuffer = 
                                            Request->DATA.QUERY_INFORMATION.InformationBuffer;
                pMuxNdisRequest->Request.DATA.QUERY_INFORMATION.InformationBufferLength = 
                                            Request->DATA.QUERY_INFORMATION.InformationBufferLength;
                break;

            case NdisRequestSetInformation:
                pMuxNdisRequest->Request.DATA.SET_INFORMATION.Oid = Request->DATA.SET_INFORMATION.Oid;
                pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBuffer = 
                                            Request->DATA.SET_INFORMATION.InformationBuffer;
                pMuxNdisRequest->Request.DATA.SET_INFORMATION.InformationBufferLength = 
                                            Request->DATA.SET_INFORMATION.InformationBufferLength;
                break;

            case NdisRequestMethod:
            default:
                ASSERT(FALSE);
                break;
        }

        //
        // If the miniport below is going away
        //
        NdisAcquireSpinLock(&pVElan->Lock);
        if (pVElan->DeInitializing == TRUE)
        {
            pMuxNdisRequest->OrigRequest = NULL;
            NdisReleaseSpinLock(&pVElan->Lock);
            MUX_DECR_PENDING_SENDS(pVElan);
            Status = NDIS_STATUS_FAILURE;
            break;
        }
        
        // If the lower binding has been notified of a low
        // power state, queue this request; it will be picked
        // up again when the lower binding returns to D0.
        //
        if (MUX_IS_LOW_POWER_STATE(pVElan->pAdapt->PtDevicePowerState))
        {
            DBGPRINT(MUX_INFO, ("ForwardRequest: VELAN %p, Adapt %p power"
                                " state is %d, queueing OID %x\n",
                                pVElan, pVElan->pAdapt,
                                pVElan->pAdapt->PtDevicePowerState, 
                                Request->DATA.QUERY_INFORMATION.Oid));

            pVElan->QueuedRequest = TRUE;
            NdisReleaseSpinLock(&pVElan->Lock);
            Status = NDIS_STATUS_PENDING;
            break;
        }

        if (pMuxNdisRequest->Cancelled == TRUE)
        {
            NdisReleaseSpinLock(&pVElan->Lock);
            Status = NDIS_STATUS_REQUEST_ABORTED;
            PtRequestComplete(pVElan->pAdapt, &pMuxNdisRequest->Request, Status);
            break;
        }
        NdisReleaseSpinLock(&pVElan->Lock);
        
        NdisAcquireSpinLock(&pAdapt->Lock);

        pAdapt->OutstandingRequests ++;
        
        if ((pAdapt->Flags & MUX_BINDING_CLOSING)== MUX_BINDING_CLOSING)
        {
            NdisReleaseSpinLock(&pAdapt->Lock);        
            Status = NDIS_STATUS_CLOSING;           
        }
        else
        {
            NdisReleaseSpinLock(&pAdapt->Lock);        
            Status  = NdisOidRequest(pVElan->BindingHandle,
                                    &pMuxNdisRequest->Request);
        }
        if (Status != NDIS_STATUS_PENDING)
        {
            PtRequestComplete(pVElan->pAdapt, &pMuxNdisRequest->Request, Status);
            Status = NDIS_STATUS_PENDING;
            break;
        }
    }
    while (FALSE);

    DBGPRINT(MUX_LOUD, ("<== MPForwardOidRequest: VELAN %p, Request %p, Status %8x\n", pVElan, Request, Status));

#if IEEE_VELAN_SUPPORT
    if ((Status != NDIS_STATUS_PENDING)
            && (((Request->RequestType == NdisRequestSetInformation)
            && (Request->DATA.SET_INFORMATION.Oid == OID_GEN_CURRENT_LOOKAHEAD))
            && (pVElan->RestoreLookaheadSize == TRUE)))
    {
        pVElan->RestoreLookaheadSize = FALSE;
        *(UNALIGNED PULONG)(Request->DATA.SET_INFORMATION.InformationBuffer) -= VLAN_TAG_HEADER_SIZE;
    }
#endif
    
    return (Status);
}

NDIS_STATUS
MPSetPacketFilter(
    IN PVELAN               pVElan,
    IN ULONG                PacketFilter
    )
/*++
Routine Description:

    This routine will set up the VELAN so that it accepts packets 
    that match the specified packet filter.  The only filter bits   
    that can truly be toggled are for broadcast and promiscuous.

    The MUX driver always sets the lower binding to promiscuous
    mode, but we do some optimization here to avoid turning on
    receives too soon. That is, we set the packet filter on the lower
    binding to a non-zero value iff at least one of the VELANs
    has a non-zero filter value.
    
    NOTE: setting the lower binding to promiscuous mode can
    impact CPU utilization. The only reason we set the lower binding
    to promiscuous mode in this sample is that we need to be able
    to receive unicast frames directed to MAC address(es) that do not
    match the local adapter's MAC address. If VELAN MAC addresses
    are set to be equal to that of the adapter below, it is sufficient
    to set the lower packet filter to the bitwise OR'ed value of
    packet filter settings on all VELANs.
                                    

Arguments:

    pVElan - pointer to VELAN
    PacketFilter - the new packet filter 
    
Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_NOT_SUPPORTED
    
--*/
{
    NDIS_STATUS     Status = NDIS_STATUS_SUCCESS;
    PADAPT          pAdapt;
    PVELAN          pTmpVElan;
    PLIST_ENTRY     p;
    ULONG           AdapterFilter;
    BOOLEAN         bSendUpdate = FALSE;
    LOCK_STATE      LockState;

    DBGPRINT(MUX_LOUD, ("==> MPSetPacketFilter VELAN %p, Filter %x\n", pVElan, PacketFilter));
    
    do
    {
        //
        // Any bits not supported?
        //
        if (PacketFilter & ~VELAN_SUPPORTED_FILTERS)
        {
            Status = NDIS_STATUS_NOT_SUPPORTED;
            break;
        }
    
        AdapterFilter = 0;
        pAdapt = pVElan->pAdapt;

        //
        // Grab a Write lock on the adapter so that this operation
        // does not interfere with any receives that might be accessing
        // filter information.
        //
        MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState);

        //
        // Save the new packet filter value
        //
        pVElan->PacketFilter = PacketFilter;

        //
        // Compute the new combined filter for all VELANs on this
        // adapter.
        //
        for (p = pAdapt->VElanList.Flink;
             p != &pAdapt->VElanList;
             p = p->Flink)
        {
            pTmpVElan = CONTAINING_RECORD(p, VELAN, Link);
            AdapterFilter |= pTmpVElan->PacketFilter;
        }

        //
        // If all VELANs have packet filters set to 0, turn off
        // receives on the lower adapter, if not already done.
        //
        if ((AdapterFilter == 0) && (pAdapt->PacketFilter != 0))
        {
            bSendUpdate = TRUE;
            pAdapt->PacketFilter = 0;
        }
        else
        //
        // If receives had been turned off on the lower adapter, and
        // the new filter is non-zero, turn on the lower adapter.
        // We set the adapter to promiscuous mode in this sample
        // so that we are able to receive packets directed to
        // any of the VELAN MAC addresses.
        //
        if ((AdapterFilter != 0) && (pAdapt->PacketFilter == 0))
        {
            bSendUpdate = TRUE;
            pAdapt->PacketFilter = MUX_ADAPTER_PACKET_FILTER;
        }
        
        MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState);

        if (bSendUpdate)
        {
            PtRequestAdapterAsync(
                pAdapt,
                NdisRequestSetInformation,
                OID_GEN_CURRENT_PACKET_FILTER,
                &pAdapt->PacketFilter,
                sizeof(pAdapt->PacketFilter),
                PtDiscardCompletedRequest);
        }

    }
    while (FALSE);

    DBGPRINT(MUX_LOUD, ("<== MPSetPacketFilter VELAN %p, Status %x\n", pVElan, Status));
    
    return(Status);
}


NDIS_STATUS
MPSetMulticastList(
    IN PVELAN                                       pVElan,
    _In_reads_bytes_(InformationBufferLength) IN PVOID   InformationBuffer,
    IN ULONG                                        InformationBufferLength,
    OUT PULONG                                      pBytesRead,
    OUT PULONG                                      pBytesNeeded
    )
/*++

Routine Description:

    Set the multicast list on the specified VELAN miniport.
    We simply validate all information and copy in the multicast
    list.

    We don't forward the multicast list information down since
    we set the lower binding to promisc. mode.

Arguments:

    pVElan - VELAN on which to set the multicast list
    InformationBuffer - pointer to new multicast list
    InformationBufferLength - length in bytes of above list
    pBytesRead - place to return # of bytes read from the above
    pBytesNeeded - place to return expected min # of bytes

Return Value:

    NDIS_STATUS_SUCCESS
    NDIS_STATUS_INVALID_LENGTH
    NDIS_STATUS_MULTICAST_FULL

--*/
{
    NDIS_STATUS         Status;
    PADAPT              pAdapt;
    LOCK_STATE          LockState;

    DBGPRINT(MUX_LOUD, ("==> MPSetMulticastList VELAN %p\n", pVElan));
    //
    // Initialize.
    //
    *pBytesNeeded = sizeof(MUX_MAC_ADDRESS);
    *pBytesRead = 0;
    Status = NDIS_STATUS_SUCCESS;

    do
    {
        if (InformationBufferLength % sizeof(MUX_MAC_ADDRESS))
        {
            Status = NDIS_STATUS_INVALID_LENGTH;
            break;
        }

        if (InformationBufferLength > (VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS)))
        {
            Status = NDIS_STATUS_MULTICAST_FULL;
            *pBytesNeeded = VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS);
            break;
        }

        pAdapt = pVElan->pAdapt;

        //
        // Grab a Write lock on the adapter so that this operation
        // does not interfere with any receives that might be accessing
        // multicast list information.
        //
        MUX_ACQUIRE_ADAPT_WRITE_LOCK(pAdapt, &LockState);

        NdisZeroMemory(pVElan->McastAddrs,
                       VELAN_MAX_MCAST_LIST * sizeof(MUX_MAC_ADDRESS));
        
        NdisMoveMemory(&pVElan->McastAddrs[0],
                       InformationBuffer,
                       InformationBufferLength);
        
        pVElan->McastAddrCount = InformationBufferLength / sizeof(MUX_MAC_ADDRESS);
        
        MUX_RELEASE_ADAPT_WRITE_LOCK(pAdapt, &LockState);
    }
    while (FALSE);

    DBGPRINT(MUX_LOUD, ("<== MPSetMulticastList VELAN %p, Status %8x\n", pVElan, Status));
    
    return (Status);
}



PUCHAR
MacAddrToString(
    PVOID In
    )
/*++

Routine Description:

    Careful! Uses static storage for string. Used to simplify DbgPrints
    of MAC addresses.
    
Arguments:

    IN            Pointer to MAC address array

Return Value:

    A string format of the given mac address

--*/    
{
    static UCHAR String[20];
    static PCHAR HexChars = "0123456789abcdef";
    PUCHAR EthAddr = (PUCHAR) In;
    UINT i;
    PUCHAR s;
    
    for (i = 0, s = String; i < 6; i++, EthAddr++)
    {
#pragma prefast(suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW, "s is bounded by check above");    
        *s++ = HexChars[(*EthAddr) >> 4];
        *s++ = HexChars[(*EthAddr) & 0xf];
    }
    *s = '\0';
    
    return String; 
}


VOID
MPGenerateMacAddr(
    PVELAN                    pVElan
)
/*++

Routine Description:

    Generates a "virtual" MAC address for a VELAN.
    NOTE: this is only a sample implementation of selecting
    a MAC address for the VELAN. Other implementations are possible,
    including using the MAC address of the underlying adapter as
    the MAC address of the VELAN.
    
Arguments:

    pVElan  - Pointer to velan structure

Return Value:

    None

--*/
{
    ETH_COPY_NETWORK_ADDRESS(
            pVElan->CurrentAddress,
            pVElan->PermanentAddress);
    
    DBGPRINT(MUX_LOUD, ("%d CurrentAddress %s\n",
        pVElan->VElanNumber, MacAddrToString(&pVElan->CurrentAddress)));
    DBGPRINT(MUX_LOUD, ("%d PermanentAddress  %s\n",
        pVElan->VElanNumber, MacAddrToString(&pVElan->PermanentAddress)));

}



VOID
MPDevicePnPEvent(
    IN NDIS_HANDLE              MiniportAdapterContext,
    IN PNET_DEVICE_PNP_EVENT    NetDevicePnPEvent
    )
/*++

Routine Description:

    This handler is called to notify us of PnP events directed to
    our miniport device object.

Arguments:

    MiniportAdapterContext - pointer to VELAN structure
    DevicePnPEvent - the event
    InformationBuffer - Points to additional event-specific information
    InformationBufferLength - length of above

Return Value:

    None
--*/
{
    // TBD - add code/comments about processing this.
    //
    
    DBGPRINT(MUX_LOUD, ("==> MPDevicePnPEvent: AdapterContext %08lp, DevicePnPEvent %x\n",MiniportAdapterContext, NetDevicePnPEvent->DevicePnPEvent));

	UNREFERENCED_PARAMETER(MiniportAdapterContext);
    UNREFERENCED_PARAMETER(NetDevicePnPEvent);
 

    DBGPRINT(MUX_LOUD, ("<== MPDevicePnPEvent: AdapterContext %08lp, DevicePnPEvent %x\n",MiniportAdapterContext, NetDevicePnPEvent->DevicePnPEvent));
    
    return;
}


VOID
MPAdapterShutdown(
    IN NDIS_HANDLE              MiniportAdapterContext,
    IN NDIS_SHUTDOWN_ACTION     ShutdownAction
    )
/*++

Routine Description:

    This handler is called to notify us of an impending system shutdown.
    Since this is not a hardware driver, there isn't anything specific
    we need to do about this.

Arguments:

    MiniportAdapterContext     pointer to VELAN structure
    ShutdownAction             Specify the reason to shut down the adapter

Return Value:

    None
--*/
{
    PVELAN      pVElan = (PVELAN)MiniportAdapterContext;

    DBGPRINT(MUX_LOUD,("==> MPAdapterShutdown: VElan %p, ShutdwonAction %x\n", pVElan, ShutdownAction));

    UNREFERENCED_PARAMETER(pVElan);
    UNREFERENCED_PARAMETER(ShutdownAction);

    DBGPRINT(MUX_LOUD,("<== MPAdapterShutdown: VElan %p, ShutdwonAction %x\n", pVElan, ShutdownAction));
    
    return;
}


VOID
MPUnload(
    IN    PDRIVER_OBJECT        DriverObject
    )
/*++

Routine Description:
    This handler is used to unload the miniport

Arguments:
    DriverObject            Pointer to the system's driver object structure 
                            for this driver.

Return Value:
    None


--*/
{
        
#if !DBG
    UNREFERENCED_PARAMETER(DriverObject);
#endif
    
    DBGPRINT(MUX_LOUD, ("==> MPUnload: DriverObj %p\n", DriverObject));
    if (ProtHandle != NULL)
    {
        NdisDeregisterProtocolDriver(ProtHandle);
    }
    NdisMDeregisterMiniportDriver(DriverHandle);

    NdisFreeSpinLock(&GlobalLock);
        
    DBGPRINT(MUX_LOUD, ("<== MPUnload: DriverObj %p\n", DriverObject));     
}

NDIS_STATUS
MPPause(
    IN  NDIS_HANDLE     MiniportAdapterContext,
    IN  PNDIS_MINIPORT_PAUSE_PARAMETERS  MiniportPauseParameters
    )
/*++

Routine Description:
    This handler is used to pause the miniport. During which, no NET_BUFFER_LIST
    will be indicated to the upper binding as well as status indications.

Arguments:
    MiniportAdapterContext      Pointer to our VELAN
    MiniportPauseParameters     Specify the pause parameters

Return Value:
    NDIS_STATUS_SUCCESS

--*/
{
    PVELAN             pVElan = (PVELAN)MiniportAdapterContext;
    NDIS_STATUS        Status = NDIS_STATUS_SUCCESS;

    DBGPRINT(MUX_LOUD, ("==> MPPause: VElan %p\n", pVElan));
    
    UNREFERENCED_PARAMETER(MiniportPauseParameters);

    DBGPRINT(MUX_LOUD,("==>MPPause  Adapter %08lp\n",MiniportAdapterContext));

    // Whilst the miniport is being paused, it cannot be restart

    NdisAcquireSpinLock(&pVElan->PauseLock);

    pVElan->Paused = TRUE;

    NdisReleaseSpinLock(&pVElan->PauseLock);

       

    DBGPRINT(MUX_LOUD,("<== MPPause,VElan %p, Status %8x\n", pVElan, Status));

    return Status;
}


NDIS_STATUS
MPRestart(
    IN  NDIS_HANDLE     MiniportAdapterContext,
    IN  PNDIS_MINIPORT_RESTART_PARAMETERS  MiniportRestartParameters
    )
/*++

Routine Description:
    This handler is used to restart the miniport.  When the miniport is
    back in the restart state, it can indicate NET_BUFFER_LISTs to the
    upper binding

Arguments:
    MiniportAdapterContext      Pointer to our VELAN
    MiniportRestartParameters

Return Value:
    NDIS_STATUS_SUCCESS

--*/
{
    PVELAN                            pVElan = (PVELAN)MiniportAdapterContext;
    NDIS_STATUS                       Status = NDIS_STATUS_SUCCESS;
    PNDIS_RESTART_ATTRIBUTES          NdisRestartAttributes;
    PNDIS_RESTART_GENERAL_ATTRIBUTES  NdisGeneralAttributes;
    
    UNREFERENCED_PARAMETER(MiniportRestartParameters);

    DBGPRINT(MUX_LOUD,("==> MPRestart  Adapter %p\n",MiniportAdapterContext));

     
    //
    // Here the driver can change its restart attributes 
    //
    NdisRestartAttributes = MiniportRestartParameters->RestartAttributes;

    //
    // If NdisRestartAttributes is not NULL, then miniport can modify generic attributes and add
    // new media specific info attributes at the end. Otherwise, NDIS restarts the miniport because 
    // of other reason, miniport should not try to modify/add attributes
    //
    if (NdisRestartAttributes != NULL)
    {

        ASSERT(NdisRestartAttributes->Oid == OID_GEN_MINIPORT_RESTART_ATTRIBUTES);
        
        NdisGeneralAttributes = (PNDIS_RESTART_GENERAL_ATTRIBUTES)NdisRestartAttributes->Data;
        UNREFERENCED_PARAMETER(NdisGeneralAttributes);
    
        //
        // Check to see if we need to change any attributes, for example, the driver can change the current
        // MAC address here. Or the driver can add media specific info attributes.
        //
    }
   
    NdisAcquireSpinLock(&pVElan->PauseLock);
    pVElan->Paused = FALSE;

    NdisReleaseSpinLock(&pVElan->PauseLock);
 

    DBGPRINT(MUX_LOUD,("<== MPRestart: Adapter %p, Status %8x\n", MiniportAdapterContext, Status));

    return Status;
}


VOID
MPSendNetBufferLists(
    IN NDIS_HANDLE      MiniportAdapterContext,
    IN PNET_BUFFER_LIST NetBufferLists,
    IN NDIS_PORT_NUMBER PortNumber,
    IN ULONG            SendFlags
    )
/*++

Routine Description:

    Send NET_BUFFER_LISTs to the lower binding

Arguments:
    MiniportAdapterContext          Pointer to our VELAN
    NetBufferLists                  Set of NET_BUFFER_LISTs to send
    SendFlags                       Specify the send flags
    DispatchLevel                   TRUE if IRQL == DISPATCH_LEVEL

Return Value:
    None

--*/
{
    PVELAN                      pVElan = (PVELAN)MiniportAdapterContext;
    PADAPT                      pAdapt = pVElan->pAdapt;
    PNET_BUFFER_LIST            CurrentNetBufferList = NULL;
    NDIS_STATUS                 Status = NDIS_STATUS_SUCCESS;
    PIM_NBL_ENTRY               SendContext;
    ULONG                       SendCompleteFlags = 0;
    BOOLEAN                     DispatchLevel = FALSE;
    
    DBGPRINT(MUX_VERY_LOUD,("==> MPSendNetBufferLists: MiniportAdapterContext  %p, NetBufferLists %p\n",MiniportAdapterContext,NetBufferLists));

    DispatchLevel = NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags);

    while(NetBufferLists != NULL)
    {
        CurrentNetBufferList = NetBufferLists;
        NetBufferLists = NET_BUFFER_LIST_NEXT_NBL(NetBufferLists);
        NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList) = NULL;

        MUX_ACQUIRE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel);
            
        if (pAdapt->BindingState != MuxAdapterBindingRunning)
        {
            Status = NDIS_STATUS_REQUEST_ABORTED;
            MUX_RELEASE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel);
                
            break;
        }
          
        pAdapt->OutstandingSends ++;
            
        MUX_RELEASE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel);
        
        do
        {
            Status = NdisAllocateNetBufferListContext(CurrentNetBufferList,
                                                      sizeof(IM_NBL_ENTRY),
                                                      0,
                                                      MUX_TAG);

            if (Status != NDIS_STATUS_SUCCESS)
            {
                break;
            }
            
            SendContext = (PIM_NBL_ENTRY)NET_BUFFER_LIST_CONTEXT_DATA_START(CurrentNetBufferList);
            NdisZeroMemory(SendContext, sizeof(IM_NBL_ENTRY));
            SendContext->PreviousSourceHandle = CurrentNetBufferList->SourceHandle;
            SendContext->pVElan = pVElan;


#ifdef IEEE_VLAN_SUPPORT
            SendContext->Flags = 0;

            Status = MPHandleSendTaggingNB(pVElan, CurrentNetBufferList);

            if (Status != NDIS_STATUS_SUCCESS)
            {
                NdisFreeNetBufferListContext(CurrentNetBufferList,
                                             sizeof(IM_NBL_ENTRY));
                break;
            }

#endif

            CurrentNetBufferList->SourceHandle = pAdapt->BindingHandle;

            MUX_INCR_PENDING_SENDS(pVElan);

            //
            // Remove this flag, so NDIS will not try to loopback the packets to mux
            //
            SendFlags &= ~NDIS_SEND_FLAGS_CHECK_FOR_LOOPBACK;

            NdisSendNetBufferLists(pAdapt->BindingHandle,
                                   CurrentNetBufferList,
                                   PortNumber,
                                   SendFlags);
                                   
        } while(FALSE);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            MUX_ACQUIRE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel);
            pAdapt->OutstandingSends --;
            
            if ((pAdapt->OutstandingSends == 0) && (pAdapt->PauseEvent != NULL))
            {
                NdisSetEvent(pAdapt->PauseEvent);
                pAdapt->PauseEvent = NULL;
            }
            
            MUX_RELEASE_SPIN_LOCK(&pAdapt->Lock, DispatchLevel);
            //
            // Handle failure case
            //
            NET_BUFFER_LIST_STATUS(CurrentNetBufferList) = Status;

            if (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags))
            {
                NDIS_SET_SEND_COMPLETE_FLAG(SendCompleteFlags, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
            }

            NdisMSendNetBufferListsComplete(pVElan->MiniportAdapterHandle,
                                            CurrentNetBufferList,
                                            SendCompleteFlags);

            Status = NDIS_STATUS_SUCCESS;
        }
    }

    if (Status != NDIS_STATUS_SUCCESS)
    {
        PNET_BUFFER_LIST          TempNetBufferList;

        for (TempNetBufferList = CurrentNetBufferList;
             TempNetBufferList != NULL;
             TempNetBufferList = NET_BUFFER_LIST_NEXT_NBL(TempNetBufferList))
        {
            NET_BUFFER_LIST_STATUS(TempNetBufferList) = Status;
        }
        if (NDIS_TEST_SEND_AT_DISPATCH_LEVEL(SendFlags))
        {
            NDIS_SET_SEND_COMPLETE_FLAG(SendCompleteFlags, NDIS_SEND_COMPLETE_FLAGS_DISPATCH_LEVEL);
        }

        NdisMSendNetBufferListsComplete(pVElan->MiniportAdapterHandle,
                                        CurrentNetBufferList,
                                        SendCompleteFlags);
    }

    DBGPRINT(MUX_VERY_LOUD,("<== MPSendNetBufferLists, MiniportAdapterContext  %p, NetBufferLists %p\n",MiniportAdapterContext,NetBufferLists));
}

VOID 
MPReturnNetBufferLists(
    IN NDIS_HANDLE      MiniportAdapterContext,
    IN PNET_BUFFER_LIST NetBufferLists,
    IN ULONG            ReturnFlags
    )
/*++

Routine Description:
    NDIS Miniport entry point called whenever protocols are done with
    a packet that we had indicated up and they had queued up for returning
    later.

Arguments:
    MiniportAdapterContext          Pointer to VELAN structure
    NetBufferLists                  NetBufferLists being returned
    Dispatch                        TRUE if IRQL == DISPATCH_LEVEL

Return Value:
    None

--*/
{
    PVELAN                  pVElan = (PVELAN)MiniportAdapterContext;
    PNET_BUFFER_LIST        CurrentNetBufferList = NULL;
    ULONG                   NumberOfNetBufferLists = 0;
#ifdef IEEE_VLAN_SUPPORT  
    NDIS_STATUS             Status;
#endif
    
    DBGPRINT(MUX_VERY_LOUD,("==> MPReturnNetBufferLists: MiniportAdapterContext %p, NetBufferList %p\n",MiniportAdapterContext,NetBufferLists));

    CurrentNetBufferList = NetBufferLists;

    while (CurrentNetBufferList)
    {
        NumberOfNetBufferLists++;

#ifdef IEEE_VLAN_SUPPORT
        //
        // Retreat the NBL before returning it to the miniport
        //
        Status = PtRestoreReceiveNBL(CurrentNetBufferList);

        ASSERT(Status == NDIS_STATUS_SUCCESS);

        //
        // Free the context that was allocated in PtReceiveNBL
        //
        NdisFreeNetBufferListContext(CurrentNetBufferList,
                                     sizeof(RECV_NBL_ENTRY));                    
#endif        

        CurrentNetBufferList = NET_BUFFER_LIST_NEXT_NBL(CurrentNetBufferList);
    }
    
    NdisReturnNetBufferLists(pVElan->BindingHandle,
                             NetBufferLists,
                             ReturnFlags);

    MUX_DECR_MULTIPLE_PENDING_RECEIVES(pVElan, NumberOfNetBufferLists);

    DBGPRINT(MUX_VERY_LOUD,("<== MPReturnNetBufferLists: MiniportAdapterContext %p, NetBufferList %p\n",MiniportAdapterContext,NetBufferLists));
}
    

VOID 
MPCancelSendNetBufferLists(
    IN NDIS_HANDLE MiniportAdapterContext,
    IN PVOID       CancelId
    )
/*++

Routine Description:

    The miniport entry point to hanadle cancellation of all send packets that
    match the given CancelId. If we have queued any packets that match this,
    then we should dequeue them and call NdisMSendCompleteNetBufferLists for
    all such packets, with a status of NDIS_STATUS_REQUEST_ABORTED.

    We should also call NdisCancelSendPackets in turn, on each lower binding
    that this adapter corresponds to. This is to let miniports below cancel
    any matching packets.

Arguments:

    MiniportAdapterContext          Pointer to VELAN structure
    CancelID                        ID of NetBufferLists to be cancelled

Return Value:
    None

--*/
{
    PVELAN                      pVElan = (PVELAN)MiniportAdapterContext;

    DBGPRINT(MUX_LOUD,("==> MPCancelSendNetBufferLists: VElan %p, CancelId %p\n", pVElan, CancelId));
    
    NdisCancelSendNetBufferLists(pVElan->pAdapt->BindingHandle,CancelId);
    
    DBGPRINT(MUX_LOUD,("<== MPCancelSendNetBufferLists: VElan %p, CancelId %p\n", pVElan, CancelId));
}

VOID 
MPCancelOidRequest(
    IN NDIS_HANDLE MiniportAdapterContext,
    IN PVOID       RequestId
    )
/*++

Routine Description:

    The miniport entry point to hanadle cancellation of a request. This function 
    checks to see if the CancelRequest should be terminated at this level
    or passed down to the next driver. 

Arguments:

    MiniportAdapterContext          Pointer to VELAN structure
    RequestId                       RequestId to be cancelled

Return Value:
    None

--*/
{
    PVELAN                      pVElan = (PVELAN)MiniportAdapterContext;
    PMUX_NDIS_REQUEST           pMuxNdisRequest = &pVElan->Request;
    BOOLEAN                     fCancelRequest = FALSE;
    
    DBGPRINT(MUX_LOUD, ("==> MPCancelOidRequest: VELAN %p, RequestId %p\n", pVElan, RequestId));
        
    NdisAcquireSpinLock(&pVElan->Lock);
    if (pMuxNdisRequest->OrigRequest != NULL)
    {
        if (pMuxNdisRequest->OrigRequest->RequestId == RequestId)
        {
            pMuxNdisRequest->Cancelled = TRUE;
            fCancelRequest = TRUE;
            pMuxNdisRequest->Refcount++;
        }

    }
    
    NdisReleaseSpinLock(&pVElan->Lock);    

    //
    // If we find the request, just send down the cancel, otherwise return because there is only 
    // one request pending from upper layer on the miniport
    //
    if (fCancelRequest)
    {
        NdisCancelOidRequest(pVElan->pAdapt->BindingHandle, &pMuxNdisRequest->Request);

        PtCompleteForwardedRequest(pVElan->pAdapt, 
                                    pMuxNdisRequest, 
                                    NDIS_STATUS_REQUEST_ABORTED);
    }
   
    DBGPRINT(MUX_LOUD, ("<== MPCancelOidRequest: VELAN %p, RequestId %p\n", pVElan, RequestId));
}



#ifdef IEEE_VLAN_SUPPORT

PMDL
MuxAllocateMdl(
    IN OUT PULONG               BufferSize
    )
/*++

Routine Description:
    This function is called by NDIS in order to allocate an MDL and memory when 
    there isn't unused data space in the net buffer when NdisRetreatNetBufferDataStart 
    is called

Arguments:
    BufferSize                      Pointer to allocation size being requested
    
Return Value:

NOTE: This function always returns NULL. This is so that MUX can allocate memory and MDL
      and save the required context about the NetBuffer in the allocated memory

--*/
{
    UNREFERENCED_PARAMETER(BufferSize);

    return NULL;
}

NDIS_STATUS 
MPHandleSendTaggingNB(
    IN PVELAN pVElan,
    IN PNET_BUFFER_LIST NetBufferList
    )
/*++

Routine Description:
    This function is called when the driver supports IEEE 802.1Q taggng. It checks
    the netbuffer to be sent on a VELAN and inserts a tag header if necessary.

Arguments:
    pVElan                          Pointer to VELAN structure
    NetBufferList                   A pointer to a NET_BUFFER_LIST

Return Value:
    NDIS_STATUS_SUCCESS
    NDIS_STATUS_XXX

NOTE: This functio doesn't handle vlan tagging in an efficient way, please wait for the next release
      to get a better implementation.

--*/
{
    NDIS_STATUS             Status;
    NDIS_NET_BUFFER_LIST_8021Q_INFO  NdisPacket8021qInfo;
    PUCHAR                  pEthFrame = NULL;
    PUCHAR                  pEthFrameNew = NULL;
    PUSHORT                 pTpid;
    PVLAN_TAG_HEADER        pTagHeader;
    PIM_NBL_ENTRY           SendContext;
    PNET_BUFFER             CurrentNetBuffer;
    PIM_SEND_NB_ENTRY       pNetBufferContext, LastNetBufferContext;
    PVOID                   pVa;
    PMDL                    Mdl, FirstMdl, SecondMdl, PrevMdl;
    ULONG                   BytesToSkip;
    ULONG                   BufferLength;  
    PVOID                   Storage;
    PNET_BUFFER             MdlAllocatedNetBuffers = NULL;

    DBGPRINT(MUX_LOUD, ("==> MPHandleSendTaggingNB: VELAN %p, NetBufferList %p\n", pVElan, NetBufferList));
    
    NdisPacket8021qInfo.Value = NET_BUFFER_LIST_INFO(NetBufferList, Ieee8021QNetBufferListInfo);
    SendContext = (PIM_NBL_ENTRY)NET_BUFFER_LIST_CONTEXT_DATA_START(NetBufferList);
    
    do
    {
        Status = NDIS_STATUS_SUCCESS;

        // If the vlan ID of the virtual miniport is 0, the miniport should act like it doesn't
        // support VELAN tag processing

        if (MuxRecognizedVlanId(pVElan,0))
        {
            break;
        }

        //
        //  Insert a tag only if we have a configured VLAN ID. Note that we do not
        //  support E-RIF
        //

        if (NdisPacket8021qInfo.TagHeader.CanonicalFormatId)
        {
            //
            // Skip the packet, return NDIS_STATUS_FAILURE
            //
            Status = NDIS_STATUS_INVALID_PACKET;
            break;
        }


        //
        // If the there is a tag header and it doesn't match the VLAN ID
        // then the packet is invalid .. ignore!
        //
        if ((NdisPacket8021qInfo.TagHeader.VlanId)
            && (! MuxRecognizedVlanId(pVElan,NdisPacket8021qInfo.TagHeader.VlanId)))
        {
            Status = NDIS_STATUS_INVALID_PACKET;
            break;
        }

        CurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
        LastNetBufferContext = NULL;

        while(CurrentNetBuffer)
        {
            //
            // Find the start address of the frame
            // 
            Storage = NULL;
            pEthFrame = NdisGetDataBuffer(CurrentNetBuffer,
                                          ETH_HEADER_SIZE,
                                          Storage,
                                          1,
                                          0);

            if (pEthFrame == NULL)
            {
                Status = NDIS_STATUS_INVALID_PACKET;
                break;
            }

            Mdl = NET_BUFFER_CURRENT_MDL(CurrentNetBuffer);
            PrevMdl = NULL;
            
            //
            // Retreat the net buffer list
            //
            Status = NdisRetreatNetBufferDataStart(CurrentNetBuffer,
                                                   VLAN_TAG_HEADER_SIZE,
                                                   0,
                                                   MuxAllocateMdl);

            if (Status == NDIS_STATUS_SUCCESS)
            {
                //
                // If there was a MDL ahead of the current MDL, this could result
                // in the retreat being successful, but the retreated bytes being in
                // a different MDL. But we need to make sure that the ethernet header
                // with the VLAN tag is in contiguous memory
                //
                if (Mdl != NET_BUFFER_CURRENT_MDL(CurrentNetBuffer))
                {
                    //
                    // Advance the NetBuffer so that we can allocate MDLs instead
                    //                    
                    NdisAdvanceNetBufferDataStart(CurrentNetBuffer,
                                                  VLAN_TAG_HEADER_SIZE,
                                                  FALSE,
                                                  NULL);

                    Status = NDIS_STATUS_RESOURCES;
                }
            }
            
            if (Status == NDIS_STATUS_RESOURCES)
            {
                do
                {                        
                    //
                    // There is no more unused data space in the NetBuffer, need to allocate
                    // a new MDL and memory
                    //
                    BytesToSkip = ETH_HEADER_SIZE;
                    Mdl = NET_BUFFER_CURRENT_MDL(CurrentNetBuffer);

                    //
                    // Assume the Ethernet Header is in the first buffer of the packet.
                    // The following loop is to find the start address of the data after
                    // the ethernet header. This may be either in the first MDL
                    // or in the second.
                    // 
                    while (TRUE)
                    {
                        pVa = NULL;
                        NdisQueryMdl(Mdl, &pVa, &BufferLength, NormalPagePriority);

                        if (pVa == NULL)
                        {
                            break;
                        }

                        //
                        // Have we gone far enough into the packet?
                        // 
                        if (BytesToSkip == 0)
                        {
                            break;
                        }

                        //
                        // Does the current buffer contain bytes past the Ethernet
                        // header? If so, stop.
                        // 
                        if (BufferLength > BytesToSkip)
                        {
                            pVa = (PVOID)((PUCHAR)pVa + BytesToSkip);
                            BufferLength -= BytesToSkip;
                            break;
                        }

                        //
                        // We haven't gone past the Ethernet header yet, so go
                        // to the next buffer.
                        //
                        BytesToSkip -= BufferLength;
                        Mdl = NDIS_MDL_LINKAGE(Mdl);                    
                    }

                    if (pVa == NULL)
                    {
                        Status = NDIS_STATUS_RESOURCES;
                        break;
                    }

                    //
                    // AllocateSpace for Ethernet + VLAN tag header + Netbuffer context
                    //
                    pNetBufferContext = (PIM_SEND_NB_ENTRY) NdisAllocateFromNPagedLookasideList(&pVElan->TagLookaside);

                    //
                    // Memory allocation failed
                    //
                    if (pNetBufferContext == NULL)
                    {
                        Status = NDIS_STATUS_RESOURCES;
                        break;
                    }

                    NdisZeroMemory((PVOID)pNetBufferContext, sizeof(IM_SEND_NB_ENTRY));
                    
                    pEthFrameNew = ((PUCHAR) pNetBufferContext) + sizeof(IM_SEND_NB_ENTRY);

                    //
                    // Allocate MDLs for the Ethernet + VLAN tag header and
                    // the data that follow these.
                    //
                    SecondMdl = NdisAllocateMdl(pVElan->MiniportAdapterHandle,
                                                pVa,    // byte following the Eth+tag headers
                                                BufferLength);
                    
                    FirstMdl = NdisAllocateMdl(pVElan->MiniportAdapterHandle,
                                               pEthFrameNew,
                                               ETH_HEADER_SIZE + VLAN_TAG_HEADER_SIZE);

                    if (!FirstMdl || !SecondMdl)
                    {
                        //
                        // One of the buffer allocations failed
                        //
                        if (FirstMdl)
                        {
                            NdisFreeMdl(FirstMdl);
                        }

                        if (SecondMdl)
                        {
                            NdisFreeMdl(SecondMdl);
                        }

                        NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, (PVOID) pNetBufferContext);

                        Status = NDIS_STATUS_RESOURCES;
                        break;
                    }
                    
                    //
                    // All allocations are successful. 
                    // Copy the Ethernet header to the newly allocated memory
                    // Leave space for the VLAN tag
                    //
                    NdisMoveMemory(pEthFrameNew, pEthFrame, 2 * ETH_LENGTH_OF_ADDRESS);
                    
                    NdisMoveMemory(pEthFrameNew + (2 * ETH_LENGTH_OF_ADDRESS) + VLAN_TAG_HEADER_SIZE, 
                                   pEthFrame + (2 * ETH_LENGTH_OF_ADDRESS), 
                                   2);

                    //
                    // Save the context for the NetBuffer
                    //
                    pNetBufferContext->CurrentMdl = NET_BUFFER_CURRENT_MDL(CurrentNetBuffer);

                    //
                    // If the current MDL is not the first on in the chain, we need to adjust the MDL chain
                    //
                    if (NET_BUFFER_FIRST_MDL(CurrentNetBuffer) != NET_BUFFER_CURRENT_MDL(CurrentNetBuffer))
                    {
                        PrevMdl = NET_BUFFER_FIRST_MDL(CurrentNetBuffer);
                        while (NDIS_MDL_LINKAGE(PrevMdl) != NET_BUFFER_CURRENT_MDL(CurrentNetBuffer))
                        {
                            PrevMdl = NDIS_MDL_LINKAGE(PrevMdl);
                        }

                        pNetBufferContext->PrevMdl = PrevMdl;                        
                    }
                    
                    pNetBufferContext->CurrentMdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer);

                    //
                    // Link this NB to the NBL context to be restored
                    // This is so that MPRestoreSendNBL can free the MDLs that were allocated
                    // for this NB
                    //
                    if (MdlAllocatedNetBuffers == NULL)
                    {
                        MdlAllocatedNetBuffers = CurrentNetBuffer;
                    }
                    else
                    {
                        ASSERT(LastNetBufferContext);
                        LastNetBufferContext->NextNetBuffer = CurrentNetBuffer;
                    }

                    LastNetBufferContext = pNetBufferContext;

                    //
                    // Adjust the NetBuffer to use the new Mdls
                    //
                    NDIS_MDL_LINKAGE(FirstMdl) = SecondMdl;
                    
                    NDIS_MDL_LINKAGE(SecondMdl) = NDIS_MDL_LINKAGE(Mdl);
                    
                    NET_BUFFER_DATA_OFFSET(CurrentNetBuffer) = NET_BUFFER_DATA_OFFSET(CurrentNetBuffer) - 
                                                               NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer);

                    NET_BUFFER_DATA_LENGTH(CurrentNetBuffer) += VLAN_TAG_HEADER_SIZE;

                    NET_BUFFER_CURRENT_MDL_OFFSET(CurrentNetBuffer) = 0;

                    NET_BUFFER_CURRENT_MDL(CurrentNetBuffer) = FirstMdl;

                    //
                    // If there are any MDLs in the MDL chain ahead of the current MDL,
                    // adjust the linkage
                    //
                    if (PrevMdl)
                    {
                        NDIS_MDL_LINKAGE(PrevMdl) = FirstMdl;
                    }
                    else
                    {
                        NET_BUFFER_FIRST_MDL(CurrentNetBuffer) = FirstMdl;
                    }

                    Status = NDIS_STATUS_SUCCESS;
                }
                while (FALSE);
            }
            else if (Status == NDIS_STATUS_SUCCESS)
            {
                //
                // There was enough unused space in the NetBuffer to 
                // accomodate the VLAN tag. 
                // Get new start address of frame
                // 
                Storage = NULL;
                pEthFrameNew = NdisGetDataBuffer(CurrentNetBuffer,
                                                 VLAN_TAG_HEADER_SIZE,
                                                 Storage,
                                                 1,
                                                 0);

                if (pEthFrameNew == NULL)
                {
                    NdisAdvanceNetBufferDataStart(CurrentNetBuffer,
                                                  VLAN_TAG_HEADER_SIZE,
                                                  FALSE,
                                                  NULL);
                    
                    Status = NDIS_STATUS_INVALID_PACKET;
                }
                else
                {
                    //
                    // Adjust the header to insert the VLAN tag in the packet frame
                    //
                    NdisMoveMemory(pEthFrameNew, pEthFrame, 2 * ETH_LENGTH_OF_ADDRESS);
                }
                
            }

            if (Status != NDIS_STATUS_SUCCESS)
            {
                break;
            }
            
            pTpid = (PUSHORT)((PUCHAR)pEthFrameNew + 2 * ETH_LENGTH_OF_ADDRESS);
            *pTpid = TPID;
            pTagHeader = (PVLAN_TAG_HEADER)(pTpid + 1);

            //
            // Write IEEE 802.1Q info to packet frame
            //

            INITIALIZE_TAG_HEADER_TO_ZERO(pTagHeader);

            if (NdisPacket8021qInfo.Value)
            {
                SET_USER_PRIORITY_TO_TAG(pTagHeader, NdisPacket8021qInfo.TagHeader.UserPriority);
            }
            else
            {
                SET_USER_PRIORITY_TO_TAG(pTagHeader, 0);
            }

            SET_CANONICAL_FORMAT_ID_TO_TAG(pTagHeader, 0);

            if (NdisPacket8021qInfo.TagHeader.VlanId)
            {
                SET_VLAN_ID_TO_TAG(pTagHeader, NdisPacket8021qInfo.TagHeader.VlanId);
            }
            else
            {
                SET_VLAN_ID_TO_TAG(pTagHeader, pVElan->VlanId);
            }
            
            CurrentNetBuffer = NET_BUFFER_NEXT_NB(CurrentNetBuffer);
        }

        if(Status == NDIS_STATUS_SUCCESS)
        {
            SendContext->Flags |= MUX_RETREAT_DATA;
            SendContext->MdlAllocatedNetBuffers = MdlAllocatedNetBuffers;
            NET_BUFFER_LIST_INFO(NetBufferList, Ieee8021QNetBufferListInfo) = 0;            
        }
        else
        {
            //
            // In case of failure, restore the NetBuffers to their original state
            // Only the NetBuffers in the NBL upto CurrentNetBuffer needs to be restored
            //
            MPRestoreSendNBL(pVElan, NetBufferList, CurrentNetBuffer, MdlAllocatedNetBuffers);
        }

    } while(FALSE);

    DBGPRINT(MUX_LOUD, ("<== MPHandleSendTaggingNB: VELAN %p, NetBufferList %p, Status %8x\n", pVElan, NetBufferList, Status));
    return Status;
}

VOID 
MPRestoreSendNBL(
    IN PVELAN               pVElan,
    IN PNET_BUFFER_LIST     NetBufferList,
    IN PNET_BUFFER          LastNetBuffer,
    IN PNET_BUFFER          MdlAllocatedNetBuffers
    )
/*++

Routine Description:
    Restore the NBL that was modified during Send

Arguments:
    pVElan                          Pointer to VELAN structure
    NetBufferList                   A pointer to a NET_BUFFER_LIST

Return Value:

--*/
    
{
    PNET_BUFFER         CurrentNetBuffer;
    PNET_BUFFER         CurrentMdlAllocatedNetBuffer, SavedMdlAllocatedNetBuffer;
    PIM_SEND_NB_ENTRY   NetBufferContext;
    PVOID               pVa = NULL;
    ULONG               BufferLength;
    PUCHAR              pFrame = NULL, pDst = NULL; 
    PMDL                FirstMdl, SecondMdl;
    PVOID               Storage;

    CurrentNetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);
    CurrentMdlAllocatedNetBuffer = MdlAllocatedNetBuffers;
    
    while (CurrentNetBuffer != LastNetBuffer)
    {
        SavedMdlAllocatedNetBuffer = CurrentMdlAllocatedNetBuffer;

        //
        // Free the MDLs and the memory allocated for the NET_BUFFER
        //
        if (CurrentMdlAllocatedNetBuffer)
        {
            NdisQueryMdl(NET_BUFFER_CURRENT_MDL(CurrentMdlAllocatedNetBuffer), 
                         &pVa, 
                         &BufferLength, 
                         NormalPagePriority);
            if( pVa == NULL ){
                //you may do something
            }
            NetBufferContext = (PIM_SEND_NB_ENTRY) ((PUCHAR) pVa - sizeof(IM_SEND_NB_ENTRY));
            ASSERT(NetBufferContext != NULL);

            //
            // Save the MDLs to be freed in temporary variables
            //
            FirstMdl = NET_BUFFER_CURRENT_MDL(CurrentMdlAllocatedNetBuffer);

            SecondMdl = NDIS_MDL_LINKAGE(FirstMdl);

            //
            // Adjust the offsets and length
            //
            if( NetBufferContext == NULL ){
                //check why NetBufferContext is NULL
            }
            else{
                NET_BUFFER_DATA_OFFSET(CurrentMdlAllocatedNetBuffer) = NET_BUFFER_DATA_OFFSET(CurrentMdlAllocatedNetBuffer) + 
                                                                       NetBufferContext->CurrentMdlOffset;
                
                NET_BUFFER_DATA_LENGTH(CurrentMdlAllocatedNetBuffer) -= VLAN_TAG_HEADER_SIZE;   

                NET_BUFFER_CURRENT_MDL_OFFSET(CurrentMdlAllocatedNetBuffer) = NetBufferContext->CurrentMdlOffset;

                NET_BUFFER_CURRENT_MDL(CurrentMdlAllocatedNetBuffer) = NetBufferContext->CurrentMdl;

                if (NetBufferContext->PrevMdl)
                {
                    NDIS_MDL_LINKAGE(NetBufferContext->PrevMdl) = NetBufferContext->CurrentMdl;
                }
                else
                {
                    NET_BUFFER_FIRST_MDL(CurrentMdlAllocatedNetBuffer) = NetBufferContext->CurrentMdl;
                }
                
                CurrentMdlAllocatedNetBuffer = NetBufferContext->NextNetBuffer;                   

                //
                // Free the MDLs and the memory allocated
                //
                NdisFreeMdl(SecondMdl);        

                NdisFreeMdl(FirstMdl);

                NdisFreeToNPagedLookasideList(&pVElan->TagLookaside, (PVOID) NetBufferContext);          
            }
        }

        //
        // Advance the NET_BUFFERs until the NET_BUFFER for which 
        // the MDLs were allocated
        //
        while ((CurrentNetBuffer != SavedMdlAllocatedNetBuffer) &&
               (CurrentNetBuffer != LastNetBuffer))
        {
            Storage = NULL;
            pFrame = NdisGetDataBuffer(CurrentNetBuffer,
                                       (2 * ETH_LENGTH_OF_ADDRESS) + VLAN_TAG_HEADER_SIZE,
                                       Storage,
                                       1,
                                       0);

            if (pFrame == NULL)
            {
                ASSERT(FALSE);
            }
            else
            {
                //
                // Restore the original header
                //
                pDst = pFrame + VLAN_TAG_HEADER_SIZE;

                RtlMoveMemory(pDst, pFrame, (2 * ETH_LENGTH_OF_ADDRESS));
            }
                    
            NdisAdvanceNetBufferDataStart(CurrentNetBuffer,
                                          VLAN_TAG_HEADER_SIZE,
                                          FALSE,
                                          NULL);    
                
            CurrentNetBuffer = NET_BUFFER_NEXT_NB(CurrentNetBuffer);                
        }

        if (SavedMdlAllocatedNetBuffer)
        {
            CurrentNetBuffer = NET_BUFFER_NEXT_NB(SavedMdlAllocatedNetBuffer);
        }
    }
}

#endif


Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS