Sync with the new ipfw3 version.
[ipfw.git] / dummynet2 / miniport.c
diff --git a/dummynet2/miniport.c b/dummynet2/miniport.c
new file mode 100644 (file)
index 0000000..3baff88
--- /dev/null
@@ -0,0 +1,1481 @@
+/*++
+
+Copyright (c) 1992-2000  Microsoft Corporation
+
+Module Name:
+
+    miniport.c
+
+Abstract:
+
+    Ndis Intermediate Miniport driver sample. This is a passthru driver.
+
+Author:
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+#include "precomp.h"
+#pragma hdrstop
+
+
+
+NDIS_STATUS
+MPInitialize(
+    OUT PNDIS_STATUS             OpenErrorStatus,
+    OUT PUINT                    SelectedMediumIndex,
+    IN  PNDIS_MEDIUM             MediumArray,
+    IN  UINT                     MediumArraySize,
+    IN  NDIS_HANDLE              MiniportAdapterHandle,
+    IN  NDIS_HANDLE              WrapperConfigurationContext
+    )
+/*++
+
+Routine Description:
+
+    This is the initialize handler which gets called as a result of
+    the BindAdapter handler calling NdisIMInitializeDeviceInstanceEx.
+    The context parameter which we pass there is the adapter structure
+    which we retrieve here.
+
+    Arguments:
+
+    OpenErrorStatus            Not used by us.
+    SelectedMediumIndex        Place-holder for what media we are using
+    MediumArray                Array of ndis media passed down to us to pick from
+    MediumArraySize            Size of the array
+    MiniportAdapterHandle    The handle NDIS uses to refer to us
+    WrapperConfigurationContext    For use by NdisOpenConfiguration
+
+Return Value:
+
+    NDIS_STATUS_SUCCESS unless something goes wrong
+
+--*/
+{
+    UINT            i;
+    PADAPT          pAdapt;
+    NDIS_STATUS     Status = NDIS_STATUS_FAILURE;
+    NDIS_MEDIUM     Medium;
+
+    UNREFERENCED_PARAMETER(WrapperConfigurationContext);
+    
+    do
+    {
+        //
+        // Start off by retrieving our adapter context and storing
+        // the Miniport handle in it.
+        //
+        pAdapt = NdisIMGetDeviceContext(MiniportAdapterHandle);
+        pAdapt->MiniportIsHalted = FALSE;
+
+        DBGPRINT(("==> Miniport Initialize: Adapt %p\n", pAdapt));
+
+        //
+        // Usually we export the medium type of the adapter below as our
+        // virtual miniport's medium type. However if the adapter below us
+        // is a WAN device, then we claim to be of medium type 802.3.
+        //
+        Medium = pAdapt->Medium;
+
+        if (Medium == NdisMediumWan)
+        {
+            Medium = NdisMedium802_3;
+        }
+
+        for (i = 0; i < MediumArraySize; i++)
+        {
+            if (MediumArray[i] == Medium)
+            {
+                *SelectedMediumIndex = i;
+                break;
+            }
+        }
+
+        if (i == MediumArraySize)
+        {
+            Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
+            break;
+        }
+
+
+        //
+        // Set the attributes now. NDIS_ATTRIBUTE_DESERIALIZE enables us
+        // to make up-calls to NDIS without having to call NdisIMSwitchToMiniport
+        // or NdisIMQueueCallBack. This also forces us to protect our data using
+        // spinlocks where appropriate. Also in this case NDIS does not queue
+        // packets on our behalf. Since this is a very simple pass-thru
+        // miniport, we do not have a need to protect anything. However in
+        // a general case there will be a need to use per-adapter spin-locks
+        // for the packet queues at the very least.
+        //
+        NdisMSetAttributesEx(MiniportAdapterHandle,
+                             pAdapt,
+                             0,                                        // CheckForHangTimeInSeconds
+                             NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT    |
+                                NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT|
+                                NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER |
+                                NDIS_ATTRIBUTE_DESERIALIZE |
+                                NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND,
+                             0);
+
+        pAdapt->MiniportHandle = MiniportAdapterHandle;
+        //
+        // Initialize LastIndicatedStatus to be NDIS_STATUS_MEDIA_CONNECT
+        //
+        pAdapt->LastIndicatedStatus = NDIS_STATUS_MEDIA_CONNECT;
+        
+        //
+        // Initialize the power states for both the lower binding (PTDeviceState)
+        // and our miniport edge to Powered On.
+        //
+        pAdapt->MPDeviceState = NdisDeviceStateD0;
+        pAdapt->PTDeviceState = NdisDeviceStateD0;
+
+        //
+        // Add this adapter to the global pAdapt List
+        //
+        NdisAcquireSpinLock(&GlobalLock);
+
+        pAdapt->Next = pAdaptList;
+        pAdaptList = pAdapt;
+
+        NdisReleaseSpinLock(&GlobalLock);
+        
+        //
+        // Create an ioctl interface
+        //
+        (VOID)PtRegisterDevice();
+
+        Status = NDIS_STATUS_SUCCESS;
+    }
+    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.
+    //
+    ASSERT(pAdapt->MiniportInitPending == TRUE);
+    pAdapt->MiniportInitPending = FALSE;
+    NdisSetEvent(&pAdapt->MiniportInitEvent);
+
+    if (Status == NDIS_STATUS_SUCCESS)
+    {
+        PtReferenceAdapt(pAdapt);
+    }
+
+    DBGPRINT(("<== Miniport Initialize: Adapt %p, Status %x\n", pAdapt, Status));
+
+    *OpenErrorStatus = Status;
+
+    
+    return Status;
+}
+
+
+NDIS_STATUS
+MPSend(
+    IN NDIS_HANDLE             MiniportAdapterContext,
+    IN PNDIS_PACKET            Packet,
+    IN UINT                    Flags
+    )
+/*++
+
+Routine Description:
+
+    Send Packet handler. Either this or our SendPackets (array) handler is called
+    based on which one is enabled in our Miniport Characteristics.
+
+Arguments:
+
+    MiniportAdapterContext    Pointer to the adapter
+    Packet                    Packet to send
+    Flags                     Unused, passed down below
+
+Return Value:
+
+    Return code from NdisSend
+
+--*/
+{
+    PADAPT              pAdapt = (PADAPT)MiniportAdapterContext;
+    NDIS_STATUS         Status;
+    PNDIS_PACKET        MyPacket;
+    PVOID               MediaSpecificInfo = NULL;
+    ULONG               MediaSpecificInfoSize = 0;
+
+    //
+    // The driver should fail the send if the virtual miniport is in low 
+    // power state
+    //
+    if (pAdapt->MPDeviceState > NdisDeviceStateD0)
+    {
+         return NDIS_STATUS_FAILURE;
+    }
+
+#ifdef NDIS51
+    //
+    // Use NDIS 5.1 packet stacking:
+    //
+    if (0)     // XXX IPFW - make sure we don't go in here
+    {
+        PNDIS_PACKET_STACK        pStack;
+        BOOLEAN                   Remaining;
+
+        //
+        // Packet stacks: Check if we can use the same packet for sending down.
+        //
+
+        pStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
+        if (Remaining)
+        {
+            //
+            // We can reuse "Packet".
+            //
+            // NOTE: if we needed to keep per-packet information in packets
+            // sent down, we can use pStack->IMReserved[].
+            //
+            ASSERT(pStack);
+            //
+            // If the below miniport is going to low power state, stop sending down any packet.
+            //
+            NdisAcquireSpinLock(&pAdapt->Lock);
+            if (pAdapt->PTDeviceState > NdisDeviceStateD0)
+            {
+                NdisReleaseSpinLock(&pAdapt->Lock);
+                return NDIS_STATUS_FAILURE;
+            }
+            pAdapt->OutstandingSends++;
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            NdisSend(&Status,
+                     pAdapt->BindingHandle,
+                     Packet);
+
+            if (Status != NDIS_STATUS_PENDING)
+            {
+                ADAPT_DECR_PENDING_SENDS(pAdapt);
+            }
+
+            return(Status);
+        }
+    }
+#endif // NDIS51
+
+    //
+    // We are either not using packet stacks, or there isn't stack space
+    // in the original packet passed down to us. Allocate a new packet
+    // to wrap the data with.
+    //
+    //
+    // If the below miniport is going to low power state, stop sending down any packet.
+    //
+    NdisAcquireSpinLock(&pAdapt->Lock);
+    if (pAdapt->PTDeviceState > NdisDeviceStateD0)
+    {
+        NdisReleaseSpinLock(&pAdapt->Lock);
+        return NDIS_STATUS_FAILURE;
+    
+    }
+    pAdapt->OutstandingSends++;
+    NdisReleaseSpinLock(&pAdapt->Lock);
+    
+    NdisAllocatePacket(&Status,
+                       &MyPacket,
+                       pAdapt->SendPacketPoolHandle);
+
+    if (Status == NDIS_STATUS_SUCCESS)
+    {
+        PSEND_RSVD            SendRsvd;
+
+        //
+        // Save a pointer to the original packet in our reserved
+        // area in the new packet. This is needed so that we can
+        // get back to the original packet when the new packet's send
+        // is completed.
+        //
+        SendRsvd = (PSEND_RSVD)(MyPacket->ProtocolReserved);
+        SendRsvd->OriginalPkt = Packet;
+
+        NdisGetPacketFlags(MyPacket) = Flags;
+
+        //
+        // Set up the new packet so that it describes the same
+        // data as the original packet.
+        //
+        NDIS_PACKET_FIRST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(Packet);
+        NDIS_PACKET_LAST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(Packet);
+#ifdef WIN9X
+        //
+        // Work around the fact that NDIS does not initialize this
+        // to FALSE on Win9x.
+        //
+        NDIS_PACKET_VALID_COUNTS(MyPacket) = FALSE;
+#endif
+
+        //
+        // Copy the OOB Offset from the original packet to the new
+        // packet.
+        //
+        NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket),
+                       NDIS_OOB_DATA_FROM_PACKET(Packet),
+                       sizeof(NDIS_PACKET_OOB_DATA));
+
+#ifndef WIN9X
+        //
+        // Copy the right parts of per packet info into the new packet.
+        // This API is not available on Win9x since task offload is
+        // not supported on that platform.
+        //
+        NdisIMCopySendPerPacketInfo(MyPacket, Packet);
+#endif
+        
+        //
+        // Copy the Media specific information
+        //
+        NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet,
+                                            &MediaSpecificInfo,
+                                            &MediaSpecificInfoSize);
+
+        if (MediaSpecificInfo || MediaSpecificInfoSize)
+        {
+            NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket,
+                                                MediaSpecificInfo,
+                                                MediaSpecificInfoSize);
+               }
+#if 1  /* IPFW: query the firewall */
+       /* if dummynet keeps the packet, we mimic success.
+        * otherwise continue as usual.
+        */
+               {
+                       int ret = ipfw2_qhandler_w32(MyPacket, OUTGOING,
+                                       MiniportAdapterContext);
+                       if (ret != PASS) {
+                               if (ret == DROP)
+                                       return NDIS_STATUS_FAILURE;
+                               else {  //dummynet kept the packet
+#ifndef WIN9X
+                                       NdisIMCopySendCompletePerPacketInfo (Packet, MyPacket);
+#endif
+                                       return NDIS_STATUS_SUCCESS; //otherwise simply continue
+                               }
+                       }
+               }
+#endif /* end of IPFW code */
+
+        NdisSend(&Status,
+                 pAdapt->BindingHandle,
+                 MyPacket);
+
+
+        if (Status != NDIS_STATUS_PENDING)
+        {
+#ifndef WIN9X
+            NdisIMCopySendCompletePerPacketInfo (Packet, MyPacket);
+#endif
+            NdisFreePacket(MyPacket);
+            ADAPT_DECR_PENDING_SENDS(pAdapt);
+        }
+    }
+    else
+    {
+        ADAPT_DECR_PENDING_SENDS(pAdapt);
+        //
+        // We are out of packets. Silently drop it. Alternatively we can deal with it:
+        //    - By keeping separate send and receive pools
+        //    - Dynamically allocate more pools as needed and free them when not needed
+        //
+    }
+
+    return(Status);
+}
+
+
+VOID
+MPSendPackets(
+    IN NDIS_HANDLE             MiniportAdapterContext,
+    IN PPNDIS_PACKET           PacketArray,
+    IN UINT                    NumberOfPackets
+    )
+/*++
+
+Routine Description:
+
+    Send Packet Array handler. Either this or our SendPacket handler is called
+    based on which one is enabled in our Miniport Characteristics.
+
+Arguments:
+
+    MiniportAdapterContext     Pointer to our adapter
+    PacketArray                Set of packets to send
+    NumberOfPackets            Self-explanatory
+
+Return Value:
+
+    None
+
+--*/
+{
+    PADAPT              pAdapt = (PADAPT)MiniportAdapterContext;
+    NDIS_STATUS         Status;
+    UINT                i;
+    PVOID               MediaSpecificInfo = NULL;
+    UINT                MediaSpecificInfoSize = 0;
+    
+
+    for (i = 0; i < NumberOfPackets; i++)
+    {
+        PNDIS_PACKET    Packet, MyPacket;
+
+        Packet = PacketArray[i];
+        //
+        // The driver should fail the send if the virtual miniport is in low 
+        // power state
+        //
+        if (pAdapt->MPDeviceState > NdisDeviceStateD0)
+        {
+            NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt),
+                            Packet,
+                            NDIS_STATUS_FAILURE);
+            continue;
+        }
+
+#ifdef NDIS51
+
+        //
+        // Use NDIS 5.1 packet stacking:
+        //
+        {
+            PNDIS_PACKET_STACK        pStack;
+            BOOLEAN                   Remaining;
+
+            //
+            // Packet stacks: Check if we can use the same packet for sending down.
+            //
+            pStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
+            if (Remaining)
+            {
+                //
+                // We can reuse "Packet".
+                //
+                // NOTE: if we needed to keep per-packet information in packets
+                // sent down, we can use pStack->IMReserved[].
+                //
+                ASSERT(pStack);
+                //
+                // If the below miniport is going to low power state, stop sending down any packet.
+                //
+                NdisAcquireSpinLock(&pAdapt->Lock);
+                if (pAdapt->PTDeviceState > NdisDeviceStateD0)
+                {
+                    NdisReleaseSpinLock(&pAdapt->Lock);
+                    NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt),
+                                        Packet,
+                                        NDIS_STATUS_FAILURE);
+                }
+                else
+                {
+                    pAdapt->OutstandingSends++;
+                    NdisReleaseSpinLock(&pAdapt->Lock);
+                
+                    NdisSend(&Status,
+                              pAdapt->BindingHandle,
+                              Packet);
+        
+                    if (Status != NDIS_STATUS_PENDING)
+                    {
+                        NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt),
+                                            Packet,
+                                            Status);
+                   
+                        ADAPT_DECR_PENDING_SENDS(pAdapt);
+                    }
+                }
+                continue;
+            }
+        }
+#endif
+        do 
+        {
+            NdisAcquireSpinLock(&pAdapt->Lock);
+            //
+            // If the below miniport is going to low power state, stop sending down any packet.
+            //
+            if (pAdapt->PTDeviceState > NdisDeviceStateD0)
+            {
+                NdisReleaseSpinLock(&pAdapt->Lock);
+                Status = NDIS_STATUS_FAILURE;
+                break;
+            }
+            pAdapt->OutstandingSends++;
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            
+            NdisAllocatePacket(&Status,
+                               &MyPacket,
+                               pAdapt->SendPacketPoolHandle);
+
+            if (Status == NDIS_STATUS_SUCCESS)
+            {
+                PSEND_RSVD        SendRsvd;
+
+                SendRsvd = (PSEND_RSVD)(MyPacket->ProtocolReserved);
+                SendRsvd->OriginalPkt = Packet;
+
+                NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags(Packet);
+
+                NDIS_PACKET_FIRST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_FIRST_NDIS_BUFFER(Packet);
+                NDIS_PACKET_LAST_NDIS_BUFFER(MyPacket) = NDIS_PACKET_LAST_NDIS_BUFFER(Packet);
+#ifdef WIN9X
+                //
+                // Work around the fact that NDIS does not initialize this
+                // to FALSE on Win9x.
+                //
+                NDIS_PACKET_VALID_COUNTS(MyPacket) = FALSE;
+#endif // WIN9X
+
+                //
+                // Copy the OOB data from the original packet to the new
+                // packet.
+                //
+                NdisMoveMemory(NDIS_OOB_DATA_FROM_PACKET(MyPacket),
+                            NDIS_OOB_DATA_FROM_PACKET(Packet),
+                            sizeof(NDIS_PACKET_OOB_DATA));
+                //
+                // Copy relevant parts of the per packet info into the new packet
+                //
+#ifndef WIN9X
+                NdisIMCopySendPerPacketInfo(MyPacket, Packet);
+#endif
+
+                //
+                // Copy the Media specific information
+                //
+                NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet,
+                                                    &MediaSpecificInfo,
+                                                    &MediaSpecificInfoSize);
+
+                if (MediaSpecificInfo || MediaSpecificInfoSize)
+                {
+                    NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(MyPacket,
+                                                        MediaSpecificInfo,
+                                                        MediaSpecificInfoSize);
+                }
+
+                NdisSend(&Status,
+                         pAdapt->BindingHandle,
+                         MyPacket);
+
+                if (Status != NDIS_STATUS_PENDING)
+                {
+#ifndef WIN9X
+                    NdisIMCopySendCompletePerPacketInfo (Packet, MyPacket);
+#endif
+                    NdisFreePacket(MyPacket);
+                    ADAPT_DECR_PENDING_SENDS(pAdapt);
+                }
+            }
+            else
+            {
+                //
+                // The driver cannot allocate a packet.
+                // 
+                ADAPT_DECR_PENDING_SENDS(pAdapt);
+            }
+        }
+        while (FALSE);
+
+        if (Status != NDIS_STATUS_PENDING)
+        {
+            NdisMSendComplete(ADAPT_MINIPORT_HANDLE(pAdapt),
+                              Packet,
+                              Status);
+        }
+    }
+}
+
+
+NDIS_STATUS
+MPQueryInformation(
+    IN NDIS_HANDLE                MiniportAdapterContext,
+    IN NDIS_OID                   Oid,
+    IN PVOID                      InformationBuffer,
+    IN ULONG                      InformationBufferLength,
+    OUT PULONG                    BytesWritten,
+    OUT PULONG                    BytesNeeded
+    )
+/*++
+
+Routine Description:
+
+    Entry point called by NDIS to query for the value of the specified OID.
+    Typical processing is to forward the query down to the underlying miniport.
+
+    The following OIDs are filtered here:
+
+    OID_PNP_QUERY_POWER - return success right here
+
+    OID_GEN_SUPPORTED_GUIDS - do not forward, otherwise we will show up
+    multiple instances of private GUIDs supported by the underlying miniport.
+
+    OID_PNP_CAPABILITIES - we do send this down to the lower miniport, but
+    the values returned are postprocessed before we complete this request;
+    see PtRequestComplete.
+
+    NOTE on OID_TCP_TASK_OFFLOAD - if this IM driver modifies the contents
+    of data it passes through such that a lower miniport may not be able
+    to perform TCP task offload, then it should not forward this OID down,
+    but fail it here with the status NDIS_STATUS_NOT_SUPPORTED. This is to
+    avoid performing incorrect transformations on data.
+
+    If our miniport edge (upper edge) is at a low-power state, fail the request.
+
+    If our protocol edge (lower edge) has been notified of a low-power state,
+    we pend this request until the miniport below has been set to D0. Since
+    requests to miniports are serialized always, at most a single request will
+    be pended.
+
+Arguments:
+
+    MiniportAdapterContext    Pointer to the adapter structure
+    Oid                       Oid for this query
+    InformationBuffer         Buffer for information
+    InformationBufferLength   Size of this buffer
+    BytesWritten              Specifies how much info is written
+    BytesNeeded               In case the buffer is smaller than what we need, tell them how much is needed
+
+
+Return Value:
+
+    Return code from the NdisRequest below.
+
+--*/
+{
+    PADAPT        pAdapt = (PADAPT)MiniportAdapterContext;
+    NDIS_STATUS   Status = NDIS_STATUS_FAILURE;
+
+    do
+    {
+        if (Oid == OID_PNP_QUERY_POWER)
+        {
+            //
+            //  Do not forward this.
+            //
+            Status = NDIS_STATUS_SUCCESS;
+            break;
+        }
+
+        if (Oid == OID_GEN_SUPPORTED_GUIDS)
+        {
+            //
+            //  Do not forward this, otherwise we will end up with multiple
+            //  instances of private GUIDs that the underlying miniport
+            //  supports.
+            //
+            Status = NDIS_STATUS_NOT_SUPPORTED;
+            break;
+        }
+
+        if (Oid == OID_TCP_TASK_OFFLOAD)
+        {
+            //
+            // Fail this -if- this driver performs data transformations
+            // that can interfere with a lower driver's ability to offload
+            // TCP tasks.
+            //
+            // Status = NDIS_STATUS_NOT_SUPPORTED;
+            // break;
+            //
+        }
+        //
+        // If the miniport below is unbinding, just fail any request
+        //
+        NdisAcquireSpinLock(&pAdapt->Lock);
+        if (pAdapt->UnbindingInProcess == TRUE)
+        {
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+        NdisReleaseSpinLock(&pAdapt->Lock);
+        //
+        // All other queries are failed, if the miniport is not at D0,
+        //
+        if (pAdapt->MPDeviceState > NdisDeviceStateD0) 
+        {
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+        pAdapt->Request.RequestType = NdisRequestQueryInformation;
+        pAdapt->Request.DATA.QUERY_INFORMATION.Oid = Oid;
+        pAdapt->Request.DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer;
+        pAdapt->Request.DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength;
+        pAdapt->BytesNeeded = BytesNeeded;
+        pAdapt->BytesReadOrWritten = BytesWritten;
+
+        //
+        // If the miniport below is binding, fail the request
+        //
+        NdisAcquireSpinLock(&pAdapt->Lock);
+            
+        if (pAdapt->UnbindingInProcess == TRUE)
+        {
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+        //
+        // If the Protocol device state is OFF, mark this request as being 
+        // pended. We queue this until the device state is back to D0. 
+        //
+        if ((pAdapt->PTDeviceState > NdisDeviceStateD0) 
+                && (pAdapt->StandingBy == FALSE))
+        {
+            pAdapt->QueuedRequest = TRUE;
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_PENDING;
+            break;
+        }
+        //
+        // This is in the process of powering down the system, always fail the request
+        // 
+        if (pAdapt->StandingBy == TRUE)
+        {
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+        pAdapt->OutstandingRequests = TRUE;
+        
+        NdisReleaseSpinLock(&pAdapt->Lock);
+
+        //
+        // default case, most requests will be passed to the miniport below
+        //
+        NdisRequest(&Status,
+                    pAdapt->BindingHandle,
+                    &pAdapt->Request);
+
+
+        if (Status != NDIS_STATUS_PENDING)
+        {
+            PtRequestComplete(pAdapt, &pAdapt->Request, Status);
+            Status = NDIS_STATUS_PENDING;
+        }
+
+    } while (FALSE);
+
+    return(Status);
+
+}
+
+
+VOID
+MPQueryPNPCapabilities(
+    IN OUT PADAPT            pAdapt,
+    OUT PNDIS_STATUS         pStatus
+    )
+/*++
+
+Routine Description:
+
+    Postprocess a request for OID_PNP_CAPABILITIES that was forwarded
+    down to the underlying miniport, and has been completed by it.
+
+Arguments:
+
+    pAdapt - Pointer to the adapter structure
+    pStatus - Place to return final status
+
+Return Value:
+
+    None.
+
+--*/
+
+{
+    PNDIS_PNP_CAPABILITIES           pPNPCapabilities;
+    PNDIS_PM_WAKE_UP_CAPABILITIES    pPMstruct;
+
+    if (pAdapt->Request.DATA.QUERY_INFORMATION.InformationBufferLength >= sizeof(NDIS_PNP_CAPABILITIES))
+    {
+        pPNPCapabilities = (PNDIS_PNP_CAPABILITIES)(pAdapt->Request.DATA.QUERY_INFORMATION.InformationBuffer);
+
+        //
+        // The following fields must be overwritten by an IM driver.
+        //
+        pPMstruct= & pPNPCapabilities->WakeUpCapabilities;
+        pPMstruct->MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+        pPMstruct->MinPatternWakeUp = NdisDeviceStateUnspecified;
+        pPMstruct->MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
+        *pAdapt->BytesReadOrWritten = sizeof(NDIS_PNP_CAPABILITIES);
+        *pAdapt->BytesNeeded = 0;
+
+
+        //
+        // Setting our internal flags
+        // Default, device is ON
+        //
+        pAdapt->MPDeviceState = NdisDeviceStateD0;
+        pAdapt->PTDeviceState = NdisDeviceStateD0;
+
+        *pStatus = NDIS_STATUS_SUCCESS;
+    }
+    else
+    {
+        *pAdapt->BytesNeeded= sizeof(NDIS_PNP_CAPABILITIES);
+        *pStatus = NDIS_STATUS_RESOURCES;
+    }
+}
+
+
+NDIS_STATUS
+MPSetInformation(
+    IN NDIS_HANDLE                                  MiniportAdapterContext,
+    IN NDIS_OID                                     Oid,
+    __in_bcount(InformationBufferLength) IN PVOID   InformationBuffer,
+    IN ULONG                                        InformationBufferLength,
+    OUT PULONG                                      BytesRead,
+    OUT PULONG                                      BytesNeeded
+    )
+/*++
+
+Routine Description:
+
+    Miniport SetInfo handler.
+
+    In the case of OID_PNP_SET_POWER, record the power state and return the OID.    
+    Do not pass below
+    If the device is suspended, do not block the SET_POWER_OID 
+    as it is used to reactivate the Passthru miniport
+
+    
+    PM- If the MP is not ON (DeviceState > D0) return immediately  (except for 'query power' and 'set power')
+         If MP is ON, but the PT is not at D0, then queue the queue the request for later processing
+
+    Requests to miniports are always serialized
+
+
+Arguments:
+
+    MiniportAdapterContext    Pointer to the adapter structure
+    Oid                       Oid for this query
+    InformationBuffer         Buffer for information
+    InformationBufferLength   Size of this buffer
+    BytesRead                 Specifies how much info is read
+    BytesNeeded               In case the buffer is smaller than what we need, tell them how much is needed
+
+Return Value:
+
+    Return code from the NdisRequest below.
+
+--*/
+{
+    PADAPT        pAdapt = (PADAPT)MiniportAdapterContext;
+    NDIS_STATUS   Status;
+
+    Status = NDIS_STATUS_FAILURE;
+
+    do
+    {
+        //
+        // The Set Power should not be sent to the miniport below the Passthru, but is handled internally
+        //
+        if (Oid == OID_PNP_SET_POWER)
+        {
+            MPProcessSetPowerOid(&Status, 
+                                 pAdapt, 
+                                 InformationBuffer, 
+                                 InformationBufferLength, 
+                                 BytesRead, 
+                                 BytesNeeded);
+            break;
+
+        }
+
+        //
+        // If the miniport below is unbinding, fail the request
+        //
+        NdisAcquireSpinLock(&pAdapt->Lock);     
+        if (pAdapt->UnbindingInProcess == TRUE)
+        {
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+        NdisReleaseSpinLock(&pAdapt->Lock);
+        //
+        // All other Set Information requests are failed, if the miniport is
+        // not at D0 or is transitioning to a device state greater than D0.
+        //
+        if (pAdapt->MPDeviceState > NdisDeviceStateD0)
+        {
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+
+        // Set up the Request and return the result
+        pAdapt->Request.RequestType = NdisRequestSetInformation;
+        pAdapt->Request.DATA.SET_INFORMATION.Oid = Oid;
+        pAdapt->Request.DATA.SET_INFORMATION.InformationBuffer = InformationBuffer;
+        pAdapt->Request.DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength;
+        pAdapt->BytesNeeded = BytesNeeded;
+        pAdapt->BytesReadOrWritten = BytesRead;
+
+        //
+        // If the miniport below is unbinding, fail the request
+        //
+        NdisAcquireSpinLock(&pAdapt->Lock);     
+        if (pAdapt->UnbindingInProcess == TRUE)
+        {
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+            
+        //
+        // If the device below is at a low power state, we cannot send it the
+        // request now, and must pend it.
+        //
+        if ((pAdapt->PTDeviceState > NdisDeviceStateD0) 
+                && (pAdapt->StandingBy == FALSE))
+        {
+            pAdapt->QueuedRequest = TRUE;
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_PENDING;
+            break;
+        }
+        //
+        // This is in the process of powering down the system, always fail the request
+        // 
+        if (pAdapt->StandingBy == TRUE)
+        {
+            NdisReleaseSpinLock(&pAdapt->Lock);
+            Status = NDIS_STATUS_FAILURE;
+            break;
+        }
+        pAdapt->OutstandingRequests = TRUE;
+        
+        NdisReleaseSpinLock(&pAdapt->Lock);
+        //
+        // Forward the request to the device below.
+        //
+        NdisRequest(&Status,
+                    pAdapt->BindingHandle,
+                    &pAdapt->Request);
+
+        if (Status != NDIS_STATUS_PENDING)
+        {
+            *BytesRead = pAdapt->Request.DATA.SET_INFORMATION.BytesRead;
+            *BytesNeeded = pAdapt->Request.DATA.SET_INFORMATION.BytesNeeded;
+            pAdapt->OutstandingRequests = FALSE;
+        }
+
+    } while (FALSE);
+
+    return(Status);
+}
+
+
+VOID
+MPProcessSetPowerOid(
+    IN OUT PNDIS_STATUS                             pNdisStatus,
+    IN PADAPT                                       pAdapt,
+    __in_bcount(InformationBufferLength) IN PVOID   InformationBuffer,
+    IN ULONG                                        InformationBufferLength,
+    OUT PULONG                                      BytesRead,
+    OUT PULONG                                      BytesNeeded
+    )
+/*++
+
+Routine Description:
+    This routine does all the procssing for a request with a SetPower Oid
+    The miniport shoud accept  the Set Power and transition to the new state
+
+    The Set Power should not be passed to the miniport below
+
+    If the IM miniport is going into a low power state, then there is no guarantee if it will ever
+    be asked go back to D0, before getting halted. No requests should be pended or queued.
+
+    
+Arguments:
+    pNdisStatus           - Status of the operation
+    pAdapt                - The Adapter structure
+    InformationBuffer     - The New DeviceState
+    InformationBufferLength
+    BytesRead             - No of bytes read
+    BytesNeeded           -  No of bytes needed
+
+
+Return Value:
+    Status  - NDIS_STATUS_SUCCESS if all the wait events succeed.
+
+--*/
+{
+
+    
+    NDIS_DEVICE_POWER_STATE NewDeviceState;
+
+    DBGPRINT(("==>MPProcessSetPowerOid: Adapt %p\n", pAdapt)); 
+
+    ASSERT (InformationBuffer != NULL);
+
+    *pNdisStatus = NDIS_STATUS_FAILURE;
+
+    do 
+    {
+        //
+        // Check for invalid length
+        //
+        if (InformationBufferLength < sizeof(NDIS_DEVICE_POWER_STATE))
+        {
+            *pNdisStatus = NDIS_STATUS_INVALID_LENGTH;
+            break;
+        }
+
+        NewDeviceState = (*(PNDIS_DEVICE_POWER_STATE)InformationBuffer);
+
+        //
+        // Check for invalid device state
+        //
+        if ((pAdapt->MPDeviceState > NdisDeviceStateD0) && (NewDeviceState != NdisDeviceStateD0))
+        {
+            //
+            // If the miniport is in a non-D0 state, the miniport can only receive a Set Power to D0
+            //
+            ASSERT (!(pAdapt->MPDeviceState > NdisDeviceStateD0) && (NewDeviceState != NdisDeviceStateD0));
+
+            *pNdisStatus = NDIS_STATUS_FAILURE;
+            break;
+        }    
+
+        //
+        // Is the miniport transitioning from an On (D0) state to an Low Power State (>D0)
+        // If so, then set the StandingBy Flag - (Block all incoming requests)
+        //
+        if (pAdapt->MPDeviceState == NdisDeviceStateD0 && NewDeviceState > NdisDeviceStateD0)
+        {
+            pAdapt->StandingBy = TRUE;
+        }
+
+        //
+        // If the miniport is transitioning from a low power state to ON (D0), then clear the StandingBy flag
+        // All incoming requests will be pended until the physical miniport turns ON.
+        //
+        if (pAdapt->MPDeviceState > NdisDeviceStateD0 &&  NewDeviceState == NdisDeviceStateD0)
+        {
+            pAdapt->StandingBy = FALSE;
+        }
+        
+        //
+        // Now update the state in the pAdapt structure;
+        //
+        pAdapt->MPDeviceState = NewDeviceState;
+        
+        *pNdisStatus = NDIS_STATUS_SUCCESS;
+    
+
+    } while (FALSE);    
+        
+    if (*pNdisStatus == NDIS_STATUS_SUCCESS)
+    {
+        //
+        // The miniport resume from low power state
+        // 
+        if (pAdapt->StandingBy == FALSE)
+        {
+            //
+            // If we need to indicate the media connect state
+            // 
+            if (pAdapt->LastIndicatedStatus != pAdapt->LatestUnIndicateStatus)
+            {
+               if (pAdapt->MiniportHandle != NULL)
+               {
+                   NdisMIndicateStatus(pAdapt->MiniportHandle,
+                                            pAdapt->LatestUnIndicateStatus,
+                                            (PVOID)NULL,
+                                            0);
+                   NdisMIndicateStatusComplete(pAdapt->MiniportHandle);
+                   pAdapt->LastIndicatedStatus = pAdapt->LatestUnIndicateStatus;
+               }
+            }
+        }
+        else
+        {
+            //
+            // Initialize LatestUnIndicatedStatus
+            //
+            pAdapt->LatestUnIndicateStatus = pAdapt->LastIndicatedStatus;
+        }
+        *BytesRead = sizeof(NDIS_DEVICE_POWER_STATE);
+        *BytesNeeded = 0;
+    }
+    else
+    {
+        *BytesRead = 0;
+        *BytesNeeded = sizeof (NDIS_DEVICE_POWER_STATE);
+    }
+
+    DBGPRINT(("<==MPProcessSetPowerOid: Adapt %p\n", pAdapt)); 
+}
+
+
+VOID
+MPReturnPacket(
+    IN NDIS_HANDLE             MiniportAdapterContext,
+    IN PNDIS_PACKET            Packet
+    )
+/*++
+
+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 ADAPT structure
+    Packet    - packet being returned.
+
+Return Value:
+
+    None.
+
+--*/
+{
+    PADAPT            pAdapt = (PADAPT)MiniportAdapterContext;
+
+#ifdef NDIS51
+    //
+    // Packet stacking: Check if this packet belongs to us.
+    //
+    if (NdisGetPoolFromPacket(Packet) != pAdapt->RecvPacketPoolHandle)
+    {
+        //
+        // We reused the original packet in a receive indication.
+        // Simply return it to the miniport below us.
+        //
+        NdisReturnPackets(&Packet, 1);
+    }
+    else
+#endif // NDIS51
+    {
+        //
+        // This is a packet allocated from this IM's receive packet pool.
+        // Reclaim our packet, and return the original to the driver below.
+        //
+
+        PNDIS_PACKET    MyPacket;
+        PRECV_RSVD      RecvRsvd;
+    
+        RecvRsvd = (PRECV_RSVD)(Packet->MiniportReserved);
+        MyPacket = RecvRsvd->OriginalPkt;
+    
+        NdisFreePacket(Packet);
+        NdisReturnPackets(&MyPacket, 1);
+    }
+}
+
+
+NDIS_STATUS
+MPTransferData(
+    OUT PNDIS_PACKET            Packet,
+    OUT PUINT                   BytesTransferred,
+    IN NDIS_HANDLE              MiniportAdapterContext,
+    IN NDIS_HANDLE              MiniportReceiveContext,
+    IN UINT                     ByteOffset,
+    IN UINT                     BytesToTransfer
+    )
+/*++
+
+Routine Description:
+
+    Miniport's transfer data handler.
+
+Arguments:
+
+    Packet                    Destination packet
+    BytesTransferred          Place-holder for how much data was copied
+    MiniportAdapterContext    Pointer to the adapter structure
+    MiniportReceiveContext    Context
+    ByteOffset                Offset into the packet for copying data
+    BytesToTransfer           How much to copy.
+
+Return Value:
+
+    Status of transfer
+
+--*/
+{
+    PADAPT        pAdapt = (PADAPT)MiniportAdapterContext;
+    NDIS_STATUS   Status;
+
+    //
+    // Return, if the device is OFF
+    //
+
+    if (IsIMDeviceStateOn(pAdapt) == FALSE)
+    {
+        return NDIS_STATUS_FAILURE;
+    }
+
+    NdisTransferData(&Status,
+                     pAdapt->BindingHandle,
+                     MiniportReceiveContext,
+                     ByteOffset,
+                     BytesToTransfer,
+                     Packet,
+                     BytesTransferred);
+
+    return(Status);
+}
+
+VOID
+MPHalt(
+    IN NDIS_HANDLE                MiniportAdapterContext
+    )
+/*++
+
+Routine Description:
+
+    Halt handler. All the hard-work for clean-up is done here.
+
+Arguments:
+
+    MiniportAdapterContext    Pointer to the Adapter
+
+Return Value:
+
+    None.
+
+--*/
+{
+    PADAPT             pAdapt = (PADAPT)MiniportAdapterContext;
+    NDIS_STATUS        Status;
+    PADAPT            *ppCursor;
+
+    DBGPRINT(("==>MiniportHalt: Adapt %p\n", pAdapt));
+
+    pAdapt->MiniportHandle = NULL;
+    pAdapt->MiniportIsHalted = TRUE;
+
+    //
+    // Remove this adapter from the global list
+    //
+    NdisAcquireSpinLock(&GlobalLock);
+
+    for (ppCursor = &pAdaptList; *ppCursor != NULL; ppCursor = &(*ppCursor)->Next)
+    {
+        if (*ppCursor == pAdapt)
+        {
+            *ppCursor = pAdapt->Next;
+            break;
+        }
+    }
+
+    NdisReleaseSpinLock(&GlobalLock);
+
+    //
+    // Delete the ioctl interface that was created when the miniport
+    // was created.
+    //
+    (VOID)PtDeregisterDevice();
+
+    //
+    // If we have a valid bind, close the miniport below the protocol
+    //
+#pragma prefast(suppress: __WARNING_DEREF_NULL_PTR, "pAdapt cannot be NULL")
+    if (pAdapt->BindingHandle != NULL)
+    {
+        //
+        // Close the binding below. and wait for it to complete
+        //
+        NdisResetEvent(&pAdapt->Event);
+
+        NdisCloseAdapter(&Status, pAdapt->BindingHandle);
+
+        if (Status == NDIS_STATUS_PENDING)
+        {
+            NdisWaitEvent(&pAdapt->Event, 0);
+            Status = pAdapt->Status;
+        }
+
+        ASSERT (Status == NDIS_STATUS_SUCCESS);
+
+        pAdapt->BindingHandle = NULL;
+        
+        PtDereferenceAdapt(pAdapt);
+    }
+
+    if (PtDereferenceAdapt(pAdapt))
+    {
+        pAdapt = NULL;
+    }
+        
+    
+    DBGPRINT(("<== MiniportHalt: pAdapt %p\n", pAdapt));
+}
+
+
+#ifdef NDIS51_MINIPORT
+
+VOID
+MPCancelSendPackets(
+    IN NDIS_HANDLE            MiniportAdapterContext,
+    IN PVOID                  CancelId
+    )
+/*++
+
+Routine Description:
+
+    The miniport entry point to handle 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 NdisMSendComplete 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 ADAPT structure
+    CancelId    - ID of packets to be cancelled.
+
+Return Value:
+
+    None
+
+--*/
+{
+    PADAPT    pAdapt = (PADAPT)MiniportAdapterContext;
+
+    //
+    // If we queue packets on our adapter structure, this would be 
+    // the place to acquire a spinlock to it, unlink any packets whose
+    // Id matches CancelId, release the spinlock and call NdisMSendComplete
+    // with NDIS_STATUS_REQUEST_ABORTED for all unlinked packets.
+    //
+
+    //
+    // Next, pass this down so that we let the miniport(s) below cancel
+    // any packets that they might have queued.
+    //
+    NdisCancelSendPackets(pAdapt->BindingHandle, CancelId);
+
+    return;
+}
+
+VOID
+MPDevicePnPEvent(
+    IN NDIS_HANDLE              MiniportAdapterContext,
+    IN NDIS_DEVICE_PNP_EVENT    DevicePnPEvent,
+    IN PVOID                    InformationBuffer,
+    IN ULONG                    InformationBufferLength
+    )
+/*++
+
+Routine Description:
+
+    This handler is called to notify us of PnP events directed to
+    our miniport device object.
+
+Arguments:
+
+    MiniportAdapterContext    - pointer to ADAPT 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.
+
+    UNREFERENCED_PARAMETER(MiniportAdapterContext);
+    UNREFERENCED_PARAMETER(DevicePnPEvent);
+    UNREFERENCED_PARAMETER(InformationBuffer);
+    UNREFERENCED_PARAMETER(InformationBufferLength);
+    
+    return;
+}
+
+VOID
+MPAdapterShutdown(
+    IN NDIS_HANDLE                MiniportAdapterContext
+    )
+/*++
+
+Routine Description:
+
+    This handler is called to notify us of an impending system shutdown.
+
+Arguments:
+
+    MiniportAdapterContext    - pointer to ADAPT structure
+
+Return Value:
+
+    None
+--*/
+{
+    UNREFERENCED_PARAMETER(MiniportAdapterContext);
+    
+    return;
+}
+
+#endif
+
+
+VOID
+MPFreeAllPacketPools(
+    IN PADAPT                    pAdapt
+    )
+/*++
+
+Routine Description:
+
+    Free all packet pools on the specified adapter.
+    
+Arguments:
+
+    pAdapt    - pointer to ADAPT structure
+
+Return Value:
+
+    None
+
+--*/
+{
+    if (pAdapt->RecvPacketPoolHandle != NULL)
+    {
+        //
+        // Free the packet pool that is used to indicate receives
+        //
+        NdisFreePacketPool(pAdapt->RecvPacketPoolHandle);
+
+        pAdapt->RecvPacketPoolHandle = NULL;
+    }
+
+    if (pAdapt->SendPacketPoolHandle != NULL)
+    {
+
+        //
+        //  Free the packet pool that is used to send packets below
+        //
+
+        NdisFreePacketPool(pAdapt->SendPacketPoolHandle);
+
+        pAdapt->SendPacketPoolHandle = NULL;
+
+    }
+}
+