[NDIS]
[reactos.git] / drivers / network / ndis / ndis / miniport.c
index 5927af0..2a0c1b6 100644 (file)
 #include "ndissys.h"
 #include "efilter.h"
 
-#ifdef DBG
 #include <buffer.h>
-#endif /* DBG */
-
-#undef NdisMSendComplete
-VOID
-EXPORT
-NdisMSendComplete(
-    IN  NDIS_HANDLE     MiniportAdapterHandle,
-    IN  PNDIS_PACKET    Packet,
-    IN  NDIS_STATUS     Status);
-
-/* Root of the scm database */
-#define SERVICES_ROOT L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
 
 /*
  * Define to 1 to get a debugger breakpoint at the end of NdisInitializeWrapper
@@ -75,7 +62,7 @@ VOID
 MiniDisplayPacket(
     PNDIS_PACKET Packet)
 {
-#ifdef DBG
+#if DBG
     ULONG i, Length;
     UCHAR Buffer[64];
     if ((DebugTraceLevel & DEBUG_PACKET) > 0) {
@@ -105,7 +92,7 @@ MiniDisplayPacket2(
     PVOID  LookaheadBuffer,
     UINT   LookaheadBufferSize)
 {
-#ifdef DBG
+#if DBG
     if ((DebugTraceLevel & DEBUG_PACKET) > 0) {
         ULONG i, Length;
         PUCHAR p;
@@ -174,7 +161,7 @@ MiniIndicateData(
 
       if (CurrentEntry == &Adapter->ProtocolListHead)
         {
-          NDIS_DbgPrint(DEBUG_MINIPORT, ("WARNING: No upper protocol layer.\n"));
+          NDIS_DbgPrint(MIN_TRACE, ("WARNING: No upper protocol layer.\n"));
         }
 
       while (CurrentEntry != &Adapter->ProtocolListHead)
@@ -182,28 +169,6 @@ MiniIndicateData(
           AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
          NDIS_DbgPrint(DEBUG_MINIPORT, ("AdapterBinding = %x\n", AdapterBinding));
 
-          KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
-
-#ifdef DBG
-          if(!AdapterBinding)
-            {
-              NDIS_DbgPrint(MIN_TRACE, ("AdapterBinding was null\n"));
-              break;
-            }
-
-          if(!AdapterBinding->ProtocolBinding)
-            {
-              NDIS_DbgPrint(MIN_TRACE, ("AdapterBinding->ProtocolBinding was null\n"));
-              break;
-            }
-
-          if(!AdapterBinding->ProtocolBinding->Chars.ReceiveHandler)
-            {
-              NDIS_DbgPrint(MIN_TRACE, ("AdapterBinding->ProtocolBinding->Chars.ReceiveHandler was null\n"));
-              break;
-            }
-#endif
-
          NDIS_DbgPrint
              (MID_TRACE,
               ("XXX (%x) %x %x %x %x %x %x %x XXX\n",
@@ -226,8 +191,6 @@ MiniIndicateData(
               LookaheadBufferSize,
               PacketSize);
 
-          KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
-
           CurrentEntry = CurrentEntry->Flink;
         }
     }
@@ -239,60 +202,99 @@ MiniIndicateData(
 \f
 VOID NTAPI
 MiniIndicateReceivePacket(
-    IN  NDIS_HANDLE    Miniport,
+    IN  NDIS_HANDLE    MiniportAdapterHandle,
     IN  PPNDIS_PACKET  PacketArray,
     IN  UINT           NumberOfPackets)
 /*
  * FUNCTION: receives miniport packet array indications
  * ARGUMENTS:
- *     Miniport: Miniport handle for the adapter
+ *     MiniportAdapterHandle: Miniport handle for the adapter
  *     PacketArray: pointer to a list of packet pointers to indicate
  *     NumberOfPackets: number of packets to indicate
- * NOTES:
- *     - This currently is a big temporary hack.  In the future this should
- *       call ProtocolReceivePacket() on each bound protocol if it exists.
- *       For now it just mimics NdisMEthIndicateReceive.
+ *
  */
 {
+  PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
+  PLIST_ENTRY CurrentEntry;
+  PADAPTER_BINDING AdapterBinding;
+  KIRQL OldIrql;
   UINT i;
 
-  for(i = 0; i < NumberOfPackets; i++)
-    {
-      PCHAR PacketBuffer = 0;
-      UINT PacketLength = 0;
-      PNDIS_BUFFER NdisBuffer = 0;
+  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
 
-#define PACKET_TAG (('k' << 24) + ('P' << 16) + ('D' << 8) + 'N')
+  CurrentEntry = Adapter->ProtocolListHead.Flink;
+
+  while (CurrentEntry != &Adapter->ProtocolListHead)
+  {
+      AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
+
+      for (i = 0; i < NumberOfPackets; i++)
+      {
+           if (AdapterBinding->ProtocolBinding->Chars.ReceivePacketHandler &&
+               NDIS_GET_PACKET_STATUS(PacketArray[i]) != NDIS_STATUS_RESOURCES)
+           {
+              (*AdapterBinding->ProtocolBinding->Chars.ReceivePacketHandler)(
+               AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+               PacketArray[i]);
+           }
+           else
+           {
+              UINT FirstBufferLength, TotalBufferLength, LookAheadSize, HeaderSize;
+              PNDIS_BUFFER NdisBuffer;
+              PVOID NdisBufferVA, LookAheadBuffer;
+              NDIS_STATUS NdisStatus;
+
+
+              NdisGetFirstBufferFromPacket(PacketArray[i],
+                                           &NdisBuffer,
+                                           &NdisBufferVA,
+                                           &FirstBufferLength,
+                                           &TotalBufferLength);
+
+              HeaderSize = NDIS_GET_PACKET_HEADER_SIZE(PacketArray[i]);
+
+              if (Adapter->NdisMiniportBlock.CurrentLookahead < (TotalBufferLength - HeaderSize))
+              {
+                  LookAheadSize = Adapter->NdisMiniportBlock.CurrentLookahead;
+              }
+              else
+              {
+                  LookAheadSize = TotalBufferLength - HeaderSize;
+              }
 
-      NdisAllocateMemoryWithTag((PVOID)&PacketBuffer, 1518, PACKET_TAG);
-      if(!PacketBuffer)
-        {
-          NDIS_DbgPrint(MIN_TRACE, ("insufficient resources\n"));
-          return;
-        }
 
-      NdisQueryPacket(PacketArray[i], NULL, NULL, &NdisBuffer, NULL);
+              LookAheadBuffer = ExAllocatePool(NonPagedPool, LookAheadSize);
+              if (!LookAheadBuffer)
+              {
+                  NDIS_DbgPrint(MIN_TRACE, ("Failed to allocate lookahead buffer!\n"));
+                  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+                  return;
+              }
 
-      while(NdisBuffer)
-        {
-          PNDIS_BUFFER CurrentBuffer;
-          PVOID BufferVa;
-          UINT BufferLen;
+              CopyBufferChainToBuffer(LookAheadBuffer,
+                                      NdisBuffer,
+                                      HeaderSize,
+                                      LookAheadSize);
 
-          NdisQueryBuffer(NdisBuffer, &BufferVa, &BufferLen);
-          memcpy(PacketBuffer + PacketLength, BufferVa, BufferLen);
-          PacketLength += BufferLen;
+              NdisStatus = (*AdapterBinding->ProtocolBinding->Chars.ReceiveHandler)(
+                            AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+                            AdapterBinding->NdisOpenBlock.MacHandle,
+                            NdisBufferVA,
+                            HeaderSize,
+                            LookAheadBuffer,
+                            LookAheadSize,
+                            TotalBufferLength - HeaderSize);
 
-          CurrentBuffer = NdisBuffer;
-          NdisGetNextBuffer(CurrentBuffer, &NdisBuffer);
-        }
+              NDIS_SET_PACKET_STATUS(PacketArray[i], NdisStatus);
 
-      NDIS_DbgPrint(MID_TRACE, ("indicating a %d-byte packet\n", PacketLength));
+              ExFreePool(LookAheadBuffer);
+          }
+      }
 
-      MiniIndicateData(Miniport, NULL, PacketBuffer, 14, PacketBuffer+14, PacketLength-14, PacketLength-14);
+      CurrentEntry = CurrentEntry->Flink;
+  }
 
-      NdisFreeMemory(PacketBuffer, 0, 0);
-    }
+  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 }
 
 \f
@@ -302,21 +304,57 @@ MiniResetComplete(
     IN  NDIS_STATUS Status,
     IN  BOOLEAN     AddressingReset)
 {
-    UNIMPLEMENTED
-}
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
+    PLIST_ENTRY CurrentEntry;
+    PADAPTER_BINDING AdapterBinding;
+    KIRQL OldIrql;
 
+    MiniEndRequest(Adapter, NdisWorkItemResetRequested);
+
+    if (AddressingReset)
+        MiniDoAddressingReset(Adapter);
+
+    NdisMIndicateStatus(Adapter, NDIS_STATUS_RESET_END, NULL, 0);
+    NdisMIndicateStatusComplete(Adapter);
+
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+
+    CurrentEntry = Adapter->ProtocolListHead.Flink;
+
+    while (CurrentEntry != &Adapter->ProtocolListHead)
+    {
+        AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
+
+        (*AdapterBinding->ProtocolBinding->Chars.ResetCompleteHandler)(
+               AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+               Status);
+
+        CurrentEntry = CurrentEntry->Flink;
+    }
+
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+}
 
-\f
 VOID NTAPI
 MiniRequestComplete(
-    IN PNDIS_MINIPORT_BLOCK Adapter,
-    IN PNDIS_REQUEST Request,
+    IN NDIS_HANDLE MiniportAdapterHandle,
     IN NDIS_STATUS Status)
 {
-    PNDIS_REQUEST_MAC_BLOCK MacBlock = (PNDIS_REQUEST_MAC_BLOCK)Request->MacReserved;
+    PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)MiniportAdapterHandle;
+    PNDIS_REQUEST Request;
+    PNDIS_REQUEST_MAC_BLOCK MacBlock;
+    KIRQL OldIrql;
 
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+    Request = Adapter->NdisMiniportBlock.PendingRequest;
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+
+    MiniEndRequest(Adapter, NdisWorkItemRequest);
+
+    MacBlock = (PNDIS_REQUEST_MAC_BLOCK)Request->MacReserved;
+
     if( MacBlock->Binding->RequestCompleteHandler ) {
         (*MacBlock->Binding->RequestCompleteHandler)(
             MacBlock->Binding->ProtocolBindingContext,
@@ -325,6 +363,22 @@ MiniRequestComplete(
     }
 }
 
+VOID NTAPI
+MiniIndicateComplete(
+    IN  NDIS_HANDLE     MiniportAdapterHandle,
+    IN  PNDIS_PACKET    Packet,
+    IN  NDIS_STATUS     Status)
+{
+    PADAPTER_BINDING AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[1];
+
+    MiniEndRequest(MiniportAdapterHandle, NdisWorkItemSendLoopback);
+
+    (*AdapterBinding->ProtocolBinding->Chars.SendCompleteHandler)(
+        AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+        Packet,
+        Status);
+}
+
 VOID NTAPI
 MiniSendComplete(
     IN  NDIS_HANDLE     MiniportAdapterHandle,
@@ -339,11 +393,37 @@ MiniSendComplete(
  *     Status            = Status of send operation
  */
 {
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
     PADAPTER_BINDING AdapterBinding;
+    KIRQL OldIrql;
+    PSCATTER_GATHER_LIST SGList;
 
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-    AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[0];
+    AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[1];
+
+    if (Adapter->NdisMiniportBlock.ScatterGatherListSize != 0)
+    {
+        NDIS_DbgPrint(MAX_TRACE, ("Freeing Scatter/Gather list\n"));
+
+        SGList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
+                                                  ScatterGatherListPacketInfo);
+
+        KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+
+        Adapter->NdisMiniportBlock.SystemAdapterObject->
+            DmaOperations->PutScatterGatherList(
+                           Adapter->NdisMiniportBlock.SystemAdapterObject,
+                           SGList,
+                           TRUE);
+
+        KeLowerIrql(OldIrql);
+
+        NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
+                                         ScatterGatherListPacketInfo) = NULL;
+    }
+
+    MiniEndRequest(Adapter, NdisWorkItemSend);
 
     (*AdapterBinding->ProtocolBinding->Chars.SendCompleteHandler)(
         AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
@@ -351,17 +431,29 @@ MiniSendComplete(
         Status);
 }
 
+NDIS_STATUS
+SignalQueue(PLOGICAL_ADAPTER Adapter)
+{
+    PIO_WORKITEM WorkItem = IoAllocateWorkItem(Adapter->NdisMiniportBlock.DeviceObject);
+
+    ASSERT(WorkItem);
+    if (!WorkItem) return NDIS_STATUS_RESOURCES;
+
+    IoQueueWorkItem(WorkItem,
+                    MiniportWorker,
+                    DelayedWorkQueue,
+                    WorkItem);
+
+    return NDIS_STATUS_SUCCESS;
+}
 
 VOID NTAPI
 MiniSendResourcesAvailable(
     IN  NDIS_HANDLE MiniportAdapterHandle)
 {
-/*
-    UNIMPLEMENTED
-*/
+    SignalQueue(MiniportAdapterHandle);
 }
 
-
 VOID NTAPI
 MiniTransferDataComplete(
     IN  NDIS_HANDLE     MiniportAdapterHandle,
@@ -373,12 +465,13 @@ MiniTransferDataComplete(
 
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-    AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[0];
+    AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[1];
 
-    (*AdapterBinding->ProtocolBinding->Chars.SendCompleteHandler)(
+    (*AdapterBinding->ProtocolBinding->Chars.TransferDataCompleteHandler)(
         AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
         Packet,
-        Status);
+        Status,
+        BytesTransferred);
 }
 
 \f
@@ -403,16 +496,16 @@ MiniAdapterHasAddress(
 
   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-#ifdef DBG
+#if DBG
   if(!Adapter)
     {
-      NDIS_DbgPrint(MID_TRACE, ("Adapter object was null\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Adapter object was null\n"));
       return FALSE;
     }
 
   if(!Packet)
     {
-      NDIS_DbgPrint(MID_TRACE, ("Packet was null\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Packet was null\n"));
       return FALSE;
     }
 #endif
@@ -421,7 +514,7 @@ MiniAdapterHasAddress(
 
   if (!NdisBuffer)
     {
-      NDIS_DbgPrint(MID_TRACE, ("Packet contains no buffers.\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Packet contains no buffers.\n"));
       return FALSE;
     }
 
@@ -443,7 +536,7 @@ MiniAdapterHasAddress(
 
   if (BufferLength < Length)
     {
-        NDIS_DbgPrint(MID_TRACE, ("Buffer is too small.\n"));
+        NDIS_DbgPrint(MIN_TRACE, ("Buffer is too small.\n"));
         return FALSE;
     }
 
@@ -478,12 +571,6 @@ MiniLocateDevice(
 
   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-  if(IsListEmpty(&AdapterListHead))
-    {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("No registered miniports for protocol to bind to\n"));
-      return NULL;
-    }
-
   KeAcquireSpinLock(&AdapterListLock, &OldIrql);
     {
       CurrentEntry = AdapterListHead.Flink;
@@ -515,13 +602,57 @@ MiniLocateDevice(
     }
   else
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Leaving (adapter not found).\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Leaving (adapter not found for %wZ).\n", AdapterName));
     }
 
   return Adapter;
 }
 
-\f
+NDIS_STATUS
+MiniSetInformation(
+    PLOGICAL_ADAPTER    Adapter,
+    NDIS_OID            Oid,
+    ULONG               Size,
+    PVOID               Buffer,
+    PULONG              BytesRead)
+{
+  NDIS_STATUS NdisStatus;
+  PNDIS_REQUEST NdisRequest;
+
+  NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+
+  NdisRequest = ExAllocatePool(NonPagedPool, sizeof(NDIS_REQUEST));
+  if (!NdisRequest) {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+      return NDIS_STATUS_RESOURCES;
+  }
+
+  RtlZeroMemory(NdisRequest, sizeof(NDIS_REQUEST));
+
+  NdisRequest->RequestType = NdisRequestSetInformation;
+  NdisRequest->DATA.SET_INFORMATION.Oid = Oid;
+  NdisRequest->DATA.SET_INFORMATION.InformationBuffer = Buffer;
+  NdisRequest->DATA.SET_INFORMATION.InformationBufferLength = Size;
+
+  NdisStatus = MiniBeginRequest(Adapter, NdisWorkItemRequest, NdisRequest);
+  if (!NT_SUCCESS(NdisStatus))
+      return NdisStatus;
+
+  NdisStatus = MiniDoRequest(Adapter, NdisRequest);
+
+  /* FIXME: Wait in pending case! */
+  if (NdisStatus != NDIS_STATUS_PENDING)
+      MiniEndRequest(Adapter, NdisWorkItemRequest);
+
+  ASSERT(NdisStatus != NDIS_STATUS_PENDING);
+
+  *BytesRead = NdisRequest->DATA.SET_INFORMATION.BytesRead;
+
+  ExFreePool(NdisRequest);
+
+  return NdisStatus;
+}
+
 NDIS_STATUS
 MiniQueryInformation(
     PLOGICAL_ADAPTER    Adapter,
@@ -537,86 +668,173 @@ MiniQueryInformation(
  *     Size         = Size of the passed buffer
  *     Buffer       = Buffer for the output
  *     BytesWritten = Address of buffer to place number of bytes written
- * NOTES:
- *     If the specified buffer is too small, a new buffer is allocated,
- *     and the query is attempted again
  * RETURNS:
  *     Status of operation
- * TODO:
- *     Is there any way to use the buffer provided by the protocol?
  */
 {
   NDIS_STATUS NdisStatus;
-  ULONG BytesNeeded;
+  PNDIS_REQUEST NdisRequest;
 
   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-  /* call the miniport's queryinfo handler */
-  NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.QueryInformationHandler)(
-      Adapter->NdisMiniportBlock.MiniportAdapterContext,
-      Oid,
-      Buffer,
-      Size,
-      BytesWritten,
-      &BytesNeeded);
+  NdisRequest = ExAllocatePool(NonPagedPool, sizeof(NDIS_REQUEST));
+  if (!NdisRequest) {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+      return NDIS_STATUS_RESOURCES;
+  }
+
+  RtlZeroMemory(NdisRequest, sizeof(NDIS_REQUEST));
+
+  NdisRequest->RequestType = NdisRequestQueryInformation;
+  NdisRequest->DATA.QUERY_INFORMATION.Oid = Oid;
+  NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer = Buffer;
+  NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = Size;
+
+  NdisStatus = MiniBeginRequest(Adapter, NdisWorkItemRequest, NdisRequest);
+  if (!NT_SUCCESS(NdisStatus))
+      return NdisStatus;
+
+  NdisStatus = MiniDoRequest(Adapter, NdisRequest);
 
   /* FIXME: Wait in pending case! */
+  if (NdisStatus != NDIS_STATUS_PENDING)
+      MiniEndRequest(Adapter, NdisWorkItemRequest);
+
+  ASSERT(NdisStatus != NDIS_STATUS_PENDING);
+
+  *BytesWritten = NdisRequest->DATA.QUERY_INFORMATION.BytesWritten;
+
+  ExFreePool(NdisRequest);
 
   return NdisStatus;
 }
 
-\f
+BOOLEAN
+MiniCheckForHang( PLOGICAL_ADAPTER Adapter )
+/*
+ * FUNCTION: Checks to see if the miniport is hung
+ * ARGUMENTS:
+ *     Adapter = Pointer to the logical adapter object
+ * RETURNS:
+ *     TRUE if the miniport is hung
+ *     FALSE if the miniport is not hung
+ */
+{
+   BOOLEAN Ret = FALSE;
+
+   if (Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CheckForHangHandler)
+       Ret = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CheckForHangHandler)(
+         Adapter->NdisMiniportBlock.MiniportAdapterContext);
+
+   return Ret;
+}
+
+VOID
+MiniDoAddressingReset(PLOGICAL_ADAPTER Adapter)
+{
+   ULONG BytesRead;
+
+   MiniSetInformation(Adapter,
+                      OID_GEN_CURRENT_LOOKAHEAD,
+                      sizeof(ULONG),
+                      &Adapter->NdisMiniportBlock.CurrentLookahead,
+                      &BytesRead);
+
+   /* FIXME: Set more stuff */
+}
+
 NDIS_STATUS
-FASTCALL
-MiniQueueWorkItem(
-    PLOGICAL_ADAPTER     Adapter,
-    NDIS_WORK_ITEM_TYPE  WorkItemType,
-    PVOID                WorkItemContext)
+MiniReset(
+    PLOGICAL_ADAPTER Adapter)
 /*
- * FUNCTION: Queues a work item for execution at a later time
+ * FUNCTION: Resets the miniport
  * ARGUMENTS:
- *     Adapter         = Pointer to the logical adapter object to queue work item on
- *     WorkItemType    = Type of work item to queue
- *     WorkItemContext = Pointer to context information for work item
- * NOTES:
- *     Adapter lock must be held when called
+ *     Adapter = Pointer to the logical adapter object
  * RETURNS:
- *     Status of operation
+ *     Status of the operation
  */
 {
-    PNDIS_MINIPORT_WORK_ITEM Item;
+   NDIS_STATUS Status;
+   BOOLEAN AddressingReset = TRUE;
 
-    NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+   Status = MiniBeginRequest(Adapter, NdisWorkItemResetRequested, NULL);
+   if (!NT_SUCCESS(Status))
+       return Status;
+
+   NdisMIndicateStatus(Adapter, NDIS_STATUS_RESET_START, NULL, 0);
+   NdisMIndicateStatusComplete(Adapter);
+
+   Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.ResetHandler)(
+            Adapter->NdisMiniportBlock.MiniportAdapterContext,
+            &AddressingReset);
 
-    ASSERT(Adapter);
-    ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
+   if (Status != NDIS_STATUS_PENDING) {
+       if (AddressingReset)
+           MiniDoAddressingReset(Adapter);
 
-    Item = ExAllocatePool(NonPagedPool, sizeof(NDIS_MINIPORT_WORK_ITEM));
-    if (Item == NULL)
+       MiniEndRequest(Adapter, NdisWorkItemResetRequested);
+
+       NdisMIndicateStatus(Adapter, NDIS_STATUS_RESET_END, NULL, 0);
+       NdisMIndicateStatusComplete(Adapter);
+   }
+
+   return Status;
+}
+
+VOID NTAPI
+MiniportHangDpc(
+        PKDPC Dpc,
+        PVOID DeferredContext,
+        PVOID SystemArgument1,
+        PVOID SystemArgument2)
+{
+  PLOGICAL_ADAPTER Adapter = DeferredContext;
+
+  if (MiniCheckForHang(Adapter)) {
+      NDIS_DbgPrint(MIN_TRACE, ("Miniport detected adapter hang\n"));
+      MiniReset(Adapter);
+  }
+}
+
+NDIS_STATUS
+MiniBeginRequest(
+    PLOGICAL_ADAPTER     Adapter,
+    NDIS_WORK_ITEM_TYPE  WorkItemType,
+    PVOID                WorkItemContext)
+{
+    KIRQL OldIrql;
+    BOOLEAN QueueBusy;
+    PNDIS_MINIPORT_WORK_ITEM MiniportWorkItem;
+    PSINGLE_LIST_ENTRY CurrentEntry;
+
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+
+    QueueBusy = (Adapter->NdisMiniportBlock.WorkQueue[WorkItemType].Next != NULL);
+
+    MiniportWorkItem = ExAllocatePool(NonPagedPool, sizeof(NDIS_MINIPORT_WORK_ITEM));
+    if (!MiniportWorkItem)
     {
         NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+        KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
         return NDIS_STATUS_RESOURCES;
     }
 
-    Item->WorkItemType    = WorkItemType;
-    Item->WorkItemContext = WorkItemContext;
+    MiniportWorkItem->WorkItemType    = WorkItemType;
+    MiniportWorkItem->WorkItemContext = WorkItemContext;
+    MiniportWorkItem->Link.Next = NULL;
 
-    /* safe due to adapter lock held */
-    Item->Link.Next = NULL;
-    if (!Adapter->WorkQueueHead)
-    {
-        Adapter->WorkQueueHead = Item;
-        Adapter->WorkQueueTail = Item;
-    }
-    else
-    {
-        Adapter->WorkQueueTail->Link.Next = (PSINGLE_LIST_ENTRY)Item;
-        Adapter->WorkQueueTail = Item;
-    }
+    CurrentEntry = &Adapter->NdisMiniportBlock.WorkQueue[WorkItemType];
+    while (CurrentEntry->Next)
+       CurrentEntry = CurrentEntry->Next;
 
-    KeInsertQueueDpc(&Adapter->NdisMiniportBlock.DeferredDpc, NULL, NULL);
+    CurrentEntry->Next = (PSINGLE_LIST_ENTRY)MiniportWorkItem;
 
-    return NDIS_STATUS_SUCCESS;
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+
+    if (QueueBusy)
+        NDIS_DbgPrint(MIN_TRACE, ("Queue %d busy!\n", WorkItemType));
+
+    return (QueueBusy ? NDIS_STATUS_PENDING : NDIS_STATUS_SUCCESS);
 }
 
 \f
@@ -624,7 +842,7 @@ NDIS_STATUS
 FASTCALL
 MiniDequeueWorkItem(
     PLOGICAL_ADAPTER    Adapter,
-    NDIS_WORK_ITEM_TYPE *WorkItemType,
+    NDIS_WORK_ITEM_TYPE WorkItemType,
     PVOID               *WorkItemContext)
 /*
  * FUNCTION: Dequeues a work item from the work queue of a logical adapter
@@ -639,35 +857,56 @@ MiniDequeueWorkItem(
  *     Status of operation
  */
 {
-    PNDIS_MINIPORT_WORK_ITEM Item;
+    PNDIS_MINIPORT_WORK_ITEM MiniportWorkItem;
+    KIRQL OldIrql;
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
-    Item = Adapter->WorkQueueHead;
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+    MiniportWorkItem = (PNDIS_MINIPORT_WORK_ITEM)Adapter->NdisMiniportBlock.WorkQueue[WorkItemType].Next;
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 
-    if (Item)
+    if (MiniportWorkItem)
     {
-        /* safe due to adapter lock held */
-        Adapter->WorkQueueHead = (PNDIS_MINIPORT_WORK_ITEM)Item->Link.Next;
-
-        if (Item == Adapter->WorkQueueTail)
-            Adapter->WorkQueueTail = NULL;
+        /* This is VERY IMPORTANT! We dequeue the work item AFTER completion */
 
-        *WorkItemType    = Item->WorkItemType;
-        *WorkItemContext = Item->WorkItemContext;
-
-        ExFreePool(Item);
+        *WorkItemContext = MiniportWorkItem->WorkItemContext;
 
         return NDIS_STATUS_SUCCESS;
     }
+    else
+    {
+        return NDIS_STATUS_FAILURE;
+    }
+}
 
-    return NDIS_STATUS_FAILURE;
+VOID
+MiniEndRequest(
+    PLOGICAL_ADAPTER     Adapter,
+    NDIS_WORK_ITEM_TYPE  WorkItemType)
+{
+    KIRQL OldIrql;
+    BOOLEAN QueueBusy;
+    PNDIS_MINIPORT_WORK_ITEM MiniportWorkItem;
+
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+
+    MiniportWorkItem = (PNDIS_MINIPORT_WORK_ITEM)Adapter->NdisMiniportBlock.WorkQueue[WorkItemType].Next;
+    ASSERT(MiniportWorkItem);
+    Adapter->NdisMiniportBlock.WorkQueue[WorkItemType].Next = MiniportWorkItem->Link.Next;
+    ExFreePool(MiniportWorkItem);
+
+    QueueBusy = (Adapter->NdisMiniportBlock.WorkQueue[WorkItemType].Next != NULL);
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+
+    if (QueueBusy)
+        SignalQueue(Adapter);
 }
 
 \f
 NDIS_STATUS
 MiniDoRequest(
-    PNDIS_MINIPORT_BLOCK Adapter,
+    PLOGICAL_ADAPTER Adapter,
     PNDIS_REQUEST NdisRequest)
 /*
  * FUNCTION: Sends a request to a miniport
@@ -678,120 +917,175 @@ MiniDoRequest(
  *     Status of operation
  */
 {
+    NDIS_STATUS Status;
+    KIRQL OldIrql;
+
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-    Adapter->MediaRequest = NdisRequest;
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+    Adapter->NdisMiniportBlock.PendingRequest = NdisRequest;
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 
-    switch (NdisRequest->RequestType)
+    if (!Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CoRequestHandler)
     {
-    case NdisRequestQueryInformation:
-        return (*Adapter->DriverHandle->MiniportCharacteristics.QueryInformationHandler)(
-            Adapter->MiniportAdapterContext,
-            NdisRequest->DATA.QUERY_INFORMATION.Oid,
-            NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
-            NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
-            (PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
-            (PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded);
-        break;
+        switch (NdisRequest->RequestType)
+        {
+        case NdisRequestQueryInformation:
+            Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.QueryInformationHandler)(
+                Adapter->NdisMiniportBlock.MiniportAdapterContext,
+                NdisRequest->DATA.QUERY_INFORMATION.Oid,
+                NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
+                NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
+                (PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesWritten,
+                (PULONG)&NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded);
+            break;
 
-    case NdisRequestSetInformation:
-        return (*Adapter->DriverHandle->MiniportCharacteristics.SetInformationHandler)(
-            Adapter->MiniportAdapterContext,
-            NdisRequest->DATA.SET_INFORMATION.Oid,
-            NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
-            NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
-            (PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
-            (PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded);
-        break;
+        case NdisRequestSetInformation:
+            Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SetInformationHandler)(
+                Adapter->NdisMiniportBlock.MiniportAdapterContext,
+                NdisRequest->DATA.SET_INFORMATION.Oid,
+                NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
+                NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
+                (PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesRead,
+                (PULONG)&NdisRequest->DATA.SET_INFORMATION.BytesNeeded);
+            break;
 
-    default:
-        return NDIS_STATUS_FAILURE;
+        default:
+            NDIS_DbgPrint(MIN_TRACE, ("Bad request type\n"));
+            Status = NDIS_STATUS_FAILURE;
+        }
+    }
+    else
+    {
+        Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CoRequestHandler)(
+            Adapter->NdisMiniportBlock.MiniportAdapterContext,
+            NULL, /* FIXME */
+            NdisRequest);
     }
+
+    return Status;
 }
 
 \f
 /*
  * @implemented
  */
-#undef NdisMQueryInformationComplete
+#undef NdisMSetInformationComplete
 VOID
 EXPORT
-NdisMQueryInformationComplete(
+NdisMSetInformationComplete(
     IN  NDIS_HANDLE MiniportAdapterHandle,
     IN  NDIS_STATUS Status)
 {
-    PNDIS_MINIPORT_BLOCK MiniportBlock =
-       (PNDIS_MINIPORT_BLOCK)MiniportAdapterHandle;
-    ASSERT(MiniportBlock);
-    if( MiniportBlock->QueryCompleteHandler )
-       (MiniportBlock->QueryCompleteHandler)(MiniportAdapterHandle, Status);
+  PLOGICAL_ADAPTER Adapter =
+       (PLOGICAL_ADAPTER)MiniportAdapterHandle;
+
+  (Adapter->NdisMiniportBlock.SetCompleteHandler)(MiniportAdapterHandle, Status);
+
+  MiniEndRequest(Adapter, NdisWorkItemRequest);
 }
 
 \f
-VOID NTAPI MiniportDpc(
-    IN PKDPC Dpc,
-    IN PVOID DeferredContext,
-    IN PVOID SystemArgument1,
-    IN PVOID SystemArgument2)
 /*
- * FUNCTION: Deferred routine to handle serialization
- * ARGUMENTS:
- *     Dpc             = Pointer to DPC object
- *     DeferredContext = Pointer to context information (LOGICAL_ADAPTER)
- *     SystemArgument1 = Unused
- *     SystemArgument2 = Unused
+ * @implemented
  */
+#undef NdisMQueryInformationComplete
+VOID
+EXPORT
+NdisMQueryInformationComplete(
+    IN  NDIS_HANDLE MiniportAdapterHandle,
+    IN  NDIS_STATUS Status)
+{
+    PLOGICAL_ADAPTER Adapter =
+       (PLOGICAL_ADAPTER)MiniportAdapterHandle;
+
+    (Adapter->NdisMiniportBlock.QueryCompleteHandler)(MiniportAdapterHandle, Status);
+
+    MiniEndRequest(Adapter, NdisWorkItemRequest);
+}
+
+VOID
+NTAPI
+MiniportWorker(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context)
 {
+  PLOGICAL_ADAPTER Adapter = DeviceObject->DeviceExtension;
+  KIRQL RaiseOldIrql;
   NDIS_STATUS NdisStatus;
   PVOID WorkItemContext;
   NDIS_WORK_ITEM_TYPE WorkItemType;
-  PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(DeferredContext);
-
-  NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+  BOOLEAN AddressingReset, NextQueue;
 
-  NdisStatus =
-      MiniDequeueWorkItem
-      (Adapter, &WorkItemType, &WorkItemContext);
+  IoFreeWorkItem((PIO_WORKITEM)Context);
 
-  if (NdisStatus == NDIS_STATUS_SUCCESS)
-    {
-      switch (WorkItemType)
+  for (WorkItemType = 0; WorkItemType < NUMBER_OF_WORK_ITEM_TYPES; WorkItemType++)
+  {
+     NextQueue = FALSE;
+     while (!NextQueue && MiniDequeueWorkItem(Adapter, WorkItemType, &WorkItemContext) == NDIS_STATUS_SUCCESS)
+     {
+        switch (WorkItemType)
         {
           case NdisWorkItemSend:
             /*
              * called by ProSend when protocols want to send packets to the miniport
              */
-#ifdef DBG
-            MiniDisplayPacket((PNDIS_PACKET)WorkItemContext);
-#endif
+
             if(Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)
               {
-                NDIS_DbgPrint(MAX_TRACE, ("Calling miniport's SendPackets handler\n"));
-
-                /*
-                 * XXX assumes single-packet - prolly OK since we'll call something
-                 * different on multi-packet sends
-                 */
-                (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)(
-                    Adapter->NdisMiniportBlock.MiniportAdapterContext, (PPNDIS_PACKET)&WorkItemContext, 1);
-               NdisStatus =
-                   NDIS_GET_PACKET_STATUS((PNDIS_PACKET)WorkItemContext);
-
-                NDIS_DbgPrint(MAX_TRACE, ("back from miniport's SendPackets handler\n"));
+                if(Adapter->NdisMiniportBlock.Flags & NDIS_ATTRIBUTE_DESERIALIZE)
+                {
+                    NDIS_DbgPrint(MAX_TRACE, ("Calling miniport's SendPackets handler\n"));
+                    (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)(
+                     Adapter->NdisMiniportBlock.MiniportAdapterContext, (PPNDIS_PACKET)&WorkItemContext, 1);
+                    NdisStatus = NDIS_STATUS_PENDING;
+                }
+                else
+                {
+                    /* SendPackets is called at DISPATCH_LEVEL for all serialized miniports */
+                    KeRaiseIrql(DISPATCH_LEVEL, &RaiseOldIrql);
+                    {
+                      NDIS_DbgPrint(MAX_TRACE, ("Calling miniport's SendPackets handler\n"));
+                      (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)(
+                       Adapter->NdisMiniportBlock.MiniportAdapterContext, (PPNDIS_PACKET)&WorkItemContext, 1);
+                    }
+                    KeLowerIrql(RaiseOldIrql);
+
+                    NdisStatus = NDIS_GET_PACKET_STATUS((PNDIS_PACKET)WorkItemContext);
+                    if( NdisStatus == NDIS_STATUS_RESOURCES ) {
+                        NextQueue = TRUE;
+                        break;
+                    }
+                }
               }
             else
               {
-                NDIS_DbgPrint(MAX_TRACE, ("Calling miniport's Send handler\n"));
-
-                NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendHandler)(
-                    Adapter->NdisMiniportBlock.MiniportAdapterContext, (PNDIS_PACKET)WorkItemContext, 0);
-
-                NDIS_DbgPrint(MAX_TRACE, ("back from miniport's Send handler\n"));
+                if(Adapter->NdisMiniportBlock.Flags & NDIS_ATTRIBUTE_DESERIALIZE)
+                {
+                  NDIS_DbgPrint(MAX_TRACE, ("Calling miniport's Send handler\n"));
+                  NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendHandler)(
+                                Adapter->NdisMiniportBlock.MiniportAdapterContext, (PNDIS_PACKET)WorkItemContext,
+                                ((PNDIS_PACKET)WorkItemContext)->Private.Flags);
+                  NDIS_DbgPrint(MAX_TRACE, ("back from miniport's send handler\n"));
+                }
+                else
+                {
+                  /* Send is called at DISPATCH_LEVEL for all serialized miniports */
+                  KeRaiseIrql(DISPATCH_LEVEL, &RaiseOldIrql);
+                  NDIS_DbgPrint(MAX_TRACE, ("Calling miniport's Send handler\n"));
+                  NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendHandler)(
+                                Adapter->NdisMiniportBlock.MiniportAdapterContext, (PNDIS_PACKET)WorkItemContext,
+                                ((PNDIS_PACKET)WorkItemContext)->Private.Flags);
+                  NDIS_DbgPrint(MAX_TRACE, ("back from miniport's send handler\n"));
+                  KeLowerIrql(RaiseOldIrql);
+                  if( NdisStatus == NDIS_STATUS_RESOURCES ) {
+                      NextQueue = TRUE;
+                      break;
+                  }
+                }
               }
+
            if( NdisStatus != NDIS_STATUS_PENDING ) {
-               NdisMSendComplete
+               MiniSendComplete
                    ( Adapter, (PNDIS_PACKET)WorkItemContext, NdisStatus );
-               Adapter->MiniportBusy = FALSE;
            }
             break;
 
@@ -801,13 +1095,21 @@ VOID NTAPI MiniportDpc(
              */
             /* XXX atm ProIndicatePacket sends a packet up via the loopback adapter only */
             NdisStatus = ProIndicatePacket(Adapter, (PNDIS_PACKET)WorkItemContext);
-            MiniSendComplete((NDIS_HANDLE)Adapter, (PNDIS_PACKET)WorkItemContext, NdisStatus);
+
+            if( NdisStatus != NDIS_STATUS_PENDING )
+                MiniIndicateComplete((NDIS_HANDLE)Adapter, (PNDIS_PACKET)WorkItemContext, NdisStatus);
             break;
 
           case NdisWorkItemReturnPackets:
             break;
 
           case NdisWorkItemResetRequested:
+            NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.ResetHandler)(
+                          Adapter->NdisMiniportBlock.MiniportAdapterContext,
+                          &AddressingReset);
+
+            if (NdisStatus != NDIS_STATUS_PENDING)
+               MiniResetComplete(Adapter, NdisStatus, AddressingReset);
             break;
 
           case NdisWorkItemResetInProgress:
@@ -817,7 +1119,7 @@ VOID NTAPI MiniportDpc(
             break;
 
           case NdisWorkItemRequest:
-            NdisStatus = MiniDoRequest(&Adapter->NdisMiniportBlock, (PNDIS_REQUEST)WorkItemContext);
+            NdisStatus = MiniDoRequest(Adapter, (PNDIS_REQUEST)WorkItemContext);
 
             if (NdisStatus == NDIS_STATUS_PENDING)
               break;
@@ -826,27 +1128,29 @@ VOID NTAPI MiniportDpc(
               {
                 case NdisRequestQueryInformation:
                  NdisMQueryInformationComplete((NDIS_HANDLE)Adapter, NdisStatus);
-                  MiniRequestComplete( &Adapter->NdisMiniportBlock, (PNDIS_REQUEST)WorkItemContext, NdisStatus );
                   break;
 
                 case NdisRequestSetInformation:
                   NdisMSetInformationComplete((NDIS_HANDLE)Adapter, NdisStatus);
-                  MiniRequestComplete( &Adapter->NdisMiniportBlock, (PNDIS_REQUEST)WorkItemContext, NdisStatus );
                   break;
 
                 default:
                   NDIS_DbgPrint(MIN_TRACE, ("Unknown NDIS request type.\n"));
+                  MiniEndRequest(Adapter, NdisWorkItemRequest);
                   break;
               }
             break;
 
           default:
+            MiniEndRequest(Adapter, WorkItemType);
             NDIS_DbgPrint(MIN_TRACE, ("Unknown NDIS work item type (%d).\n", WorkItemType));
             break;
         }
-    }
+     }
+  }
 }
 
+
 \f
 VOID
 NTAPI
@@ -856,7 +1160,29 @@ MiniStatus(
     IN PVOID  StatusBuffer,
     IN UINT  StatusBufferSize)
 {
-    UNIMPLEMENTED
+    PLOGICAL_ADAPTER Adapter = MiniportHandle;
+    PLIST_ENTRY CurrentEntry;
+    PADAPTER_BINDING AdapterBinding;
+    KIRQL OldIrql;
+
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+
+    CurrentEntry = Adapter->ProtocolListHead.Flink;
+
+    while (CurrentEntry != &Adapter->ProtocolListHead)
+    {
+       AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
+
+       (*AdapterBinding->ProtocolBinding->Chars.StatusHandler)(
+           AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+           GeneralStatus,
+           StatusBuffer,
+           StatusBufferSize);
+
+       CurrentEntry = CurrentEntry->Flink;
+    }
+
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 }
 
 \f
@@ -865,24 +1191,52 @@ NTAPI
 MiniStatusComplete(
     IN NDIS_HANDLE  MiniportAdapterHandle)
 {
-    UNIMPLEMENTED
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
+    PLIST_ENTRY CurrentEntry;
+    PADAPTER_BINDING AdapterBinding;
+    KIRQL OldIrql;
+
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+
+    CurrentEntry = Adapter->ProtocolListHead.Flink;
+
+    while (CurrentEntry != &Adapter->ProtocolListHead)
+    {
+       AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
+
+       (*AdapterBinding->ProtocolBinding->Chars.StatusCompleteHandler)(
+           AdapterBinding->NdisOpenBlock.ProtocolBindingContext);
+
+       CurrentEntry = CurrentEntry->Flink;
+    }
+
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 }
 
 \f
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 EXPORT
 NdisMCloseLog(
     IN  NDIS_HANDLE LogHandle)
 {
-    UNIMPLEMENTED
+    PNDIS_LOG Log = (PNDIS_LOG)LogHandle;
+    PNDIS_MINIPORT_BLOCK Miniport = Log->Miniport;
+    KIRQL OldIrql;
+
+    NDIS_DbgPrint(MAX_TRACE, ("called: LogHandle 0x%x\n", LogHandle));
+
+    KeAcquireSpinLock(&(Miniport)->Lock, &OldIrql);
+    Miniport->Log = NULL;
+    KeReleaseSpinLock(&(Miniport)->Lock, OldIrql);
+
+    ExFreePool(Log);
 }
 
-\f
 /*
- * @unimplemented
+ * @implemented
  */
 NDIS_STATUS
 EXPORT
@@ -891,12 +1245,47 @@ NdisMCreateLog(
     IN  UINT            Size,
     OUT PNDIS_HANDLE    LogHandle)
 {
-    UNIMPLEMENTED
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
+    PNDIS_LOG Log;
+    KIRQL OldIrql;
+
+    NDIS_DbgPrint(MAX_TRACE, ("called: MiniportAdapterHandle 0x%x, Size %ld\n", MiniportAdapterHandle, Size));
+
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
 
-  return NDIS_STATUS_FAILURE;
+    if (Adapter->NdisMiniportBlock.Log)
+    {
+        *LogHandle = NULL;
+        KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+        return NDIS_STATUS_FAILURE;
+    }
+
+    Log = ExAllocatePool(NonPagedPool, Size + sizeof(NDIS_LOG));
+    if (!Log)
+    {
+        *LogHandle = NULL;
+        KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    Adapter->NdisMiniportBlock.Log = Log;
+
+    KeInitializeSpinLock(&Log->LogLock);
+
+    Log->Miniport = &Adapter->NdisMiniportBlock;
+    Log->TotalSize = Size;
+    Log->CurrentSize = 0;
+    Log->OutPtr = 0;
+    Log->InPtr = 0;
+    Log->Irp = NULL;
+
+    *LogHandle = Log;
+
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+
+    return NDIS_STATUS_SUCCESS;
 }
 
-\f
 /*
  * @implemented
  */
@@ -913,24 +1302,39 @@ NdisMDeregisterAdapterShutdownHandler(
 
   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-  if(Adapter->BugcheckContext->ShutdownHandler)
+  if(Adapter->BugcheckContext->ShutdownHandler) {
     KeDeregisterBugCheckCallback(Adapter->BugcheckContext->CallbackRecord);
+    IoUnregisterShutdownNotification(Adapter->NdisMiniportBlock.DeviceObject);
+  }
 }
 
-\f
 /*
- * @unimplemented
+ * @implemented
  */
 VOID
 EXPORT
 NdisMFlushLog(
     IN  NDIS_HANDLE LogHandle)
 {
-    UNIMPLEMENTED
+    PNDIS_LOG Log = (PNDIS_LOG) LogHandle;
+    KIRQL OldIrql;
+
+    NDIS_DbgPrint(MAX_TRACE, ("called: LogHandle 0x%x\n", LogHandle));
+
+    /* Lock object */
+    KeAcquireSpinLock(&Log->LogLock, &OldIrql);
+
+    /* Set buffers size */
+    Log->CurrentSize = 0;
+    Log->OutPtr = 0;
+    Log->InPtr = 0;
+
+    /* Unlock object */
+    KeReleaseSpinLock(&Log->LogLock, OldIrql);
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 #undef NdisMIndicateStatus
 VOID
@@ -941,11 +1345,11 @@ NdisMIndicateStatus(
     IN  PVOID       StatusBuffer,
     IN  UINT        StatusBufferSize)
 {
-    UNIMPLEMENTED
+    MiniStatus(MiniportAdapterHandle, GeneralStatus, StatusBuffer, StatusBufferSize);
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 #undef NdisMIndicateStatusComplete
 VOID
@@ -953,7 +1357,7 @@ EXPORT
 NdisMIndicateStatusComplete(
     IN  NDIS_HANDLE MiniportAdapterHandle)
 {
-    UNIMPLEMENTED
+    MiniStatusComplete(MiniportAdapterHandle);
 }
 
 \f
@@ -989,7 +1393,7 @@ NdisInitializeWrapper(
   *NdisWrapperHandle = NULL;
 
 #if BREAK_ON_MINIPORT_INIT
-  __asm__ ("int $3\n");
+  DbgBreakPoint();
 #endif
 
   Miniport = ExAllocatePool(NonPagedPool, sizeof(NDIS_M_DRIVER_BLOCK));
@@ -1079,17 +1483,13 @@ NdisMRegisterAdapterShutdownHandler(
  *     ShutdownHandler:  Function to call to handle the bugcheck
  * NOTES:
  *     - I'm not sure about ShutdownContext
- *     - FIXME - memory leak below
  */
 {
   PLOGICAL_ADAPTER            Adapter = (PLOGICAL_ADAPTER)MiniportHandle;
-  PMINIPORT_BUGCHECK_CONTEXT  BugcheckContext = Adapter->BugcheckContext;
+  PMINIPORT_BUGCHECK_CONTEXT  BugcheckContext;
 
   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-  if(BugcheckContext)
-    return;
-
   BugcheckContext = ExAllocatePool(NonPagedPool, sizeof(MINIPORT_BUGCHECK_CONTEXT));
   if(!BugcheckContext)
     {
@@ -1100,11 +1500,20 @@ NdisMRegisterAdapterShutdownHandler(
   BugcheckContext->ShutdownHandler = ShutdownHandler;
   BugcheckContext->DriverContext = ShutdownContext;
 
-  /* not sure if this needs to be initialized or not... oh well, it's a leak. */
   BugcheckContext->CallbackRecord = ExAllocatePool(NonPagedPool, sizeof(KBUGCHECK_CALLBACK_RECORD));
+  if (!BugcheckContext->CallbackRecord) {
+      ExFreePool(BugcheckContext);
+      return;
+  }
+
+  Adapter->BugcheckContext = BugcheckContext;
+
+  KeInitializeCallbackRecord(BugcheckContext->CallbackRecord);
 
   KeRegisterBugCheckCallback(BugcheckContext->CallbackRecord, NdisIBugcheckCallback,
-      BugcheckContext, sizeof(BugcheckContext), (PUCHAR)"Ndis Miniport");
+      BugcheckContext, sizeof(*BugcheckContext), (PUCHAR)"Ndis Miniport");
+
+  IoRegisterShutdownNotification(Adapter->NdisMiniportBlock.DeviceObject);
 }
 
 \f
@@ -1149,7 +1558,7 @@ DoQueries(
       return NdisStatus;
     }
 
-#ifdef DBG
+#if DBG
     {
       /* 802.3 only */
 
@@ -1181,17 +1590,19 @@ DoQueries(
       return NdisStatus;
     }
 
-  NDIS_DbgPrint(DEBUG_MINIPORT, ("CurLookaheadLength (0x%X).\n", Adapter->NdisMiniportBlock.CurrentLookahead));
+  NdisStatus = MiniQueryInformation(Adapter, OID_GEN_MAXIMUM_SEND_PACKETS, sizeof(ULONG),
+                                    &Adapter->NdisMiniportBlock.MaxSendPackets, &BytesWritten);
 
-  if (Adapter->NdisMiniportBlock.MaximumLookahead != 0)
+  if (NdisStatus != NDIS_STATUS_SUCCESS)
     {
-      Adapter->LookaheadLength = Adapter->NdisMiniportBlock.MaximumLookahead + Adapter->MediumHeaderSize;
-      Adapter->LookaheadBuffer = ExAllocatePool(NonPagedPool, Adapter->LookaheadLength);
+      NDIS_DbgPrint(MIN_TRACE, ("OID_GEN_MAXIMUM_SEND_PACKETS failed. NdisStatus (0x%X).\n", NdisStatus));
 
-      if (!Adapter->LookaheadBuffer)
-        return NDIS_STATUS_RESOURCES;
+      /* Set it to 1 if it fails because some drivers don't support this (?)*/
+      Adapter->NdisMiniportBlock.MaxSendPackets = 1;
     }
 
+  NDIS_DbgPrint(DEBUG_MINIPORT, ("CurLookaheadLength (0x%X).\n", Adapter->NdisMiniportBlock.CurrentLookahead));
+
   return STATUS_SUCCESS;
 }
 
@@ -1262,7 +1673,9 @@ NdisIPnPStartDevice(
   PNDIS_CONFIGURATION_PARAMETER ConfigParam;
   NDIS_HANDLE ConfigHandle;
   ULONG Size;
-/* FIXME - KIRQL OldIrql; */
+  LARGE_INTEGER Timeout;
+  UINT MaxMulticastAddresses;
+  ULONG BytesWritten;
 
   /*
    * Prepare wrapper context used by HW and configuration routines.
@@ -1281,6 +1694,7 @@ NdisIPnPStartDevice(
   if (!NT_SUCCESS(Status))
     {
       NDIS_DbgPrint(MIN_TRACE,("failed to open adapter-specific reg key\n"));
+      ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
       return Status;
     }
 
@@ -1293,8 +1707,7 @@ NdisIPnPStartDevice(
    * NdisMQueryAdapterResources.
    */
 
-  if (Stack->Parameters.StartDevice.AllocatedResources != NULL &&
-      Stack->Parameters.StartDevice.AllocatedResourcesTranslated != NULL)
+  if (Stack->Parameters.StartDevice.AllocatedResources != NULL)
     {
       ResourceCount = Stack->Parameters.StartDevice.AllocatedResources->List[0].
                       PartialResourceList.Count;
@@ -1306,28 +1719,51 @@ NdisIPnPStartDevice(
         ExAllocatePool(PagedPool, ResourceListSize);
       if (Adapter->NdisMiniportBlock.AllocatedResources == NULL)
         {
+          NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
          ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
           return STATUS_INSUFFICIENT_RESOURCES;
         }
 
-      Adapter->NdisMiniportBlock.AllocatedResourcesTranslated =
+      Adapter->NdisMiniportBlock.Resources =
         ExAllocatePool(PagedPool, ResourceListSize);
-      if (Adapter->NdisMiniportBlock.AllocatedResourcesTranslated == NULL)
-        {
+      if (!Adapter->NdisMiniportBlock.Resources)
+      {
+          NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
           ExFreePool(Adapter->NdisMiniportBlock.AllocatedResources);
-          Adapter->NdisMiniportBlock.AllocatedResources = NULL;
-         ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
+          ExInterlockedRemoveEntryList(&Adapter->ListEntry, &AdapterListLock);
           return STATUS_INSUFFICIENT_RESOURCES;
-        }
+      }
+
+      RtlCopyMemory(Adapter->NdisMiniportBlock.Resources,
+                    Stack->Parameters.StartDevice.AllocatedResources,
+                    ResourceListSize);
 
       RtlCopyMemory(Adapter->NdisMiniportBlock.AllocatedResources,
                     Stack->Parameters.StartDevice.AllocatedResources,
                     ResourceListSize);
+    }
+
+  if (Stack->Parameters.StartDevice.AllocatedResourcesTranslated != NULL)
+    {
+      ResourceCount = Stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].
+                      PartialResourceList.Count;
+      ResourceListSize =
+        FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.
+                     PartialDescriptors[ResourceCount]);
+
+      Adapter->NdisMiniportBlock.AllocatedResourcesTranslated =
+        ExAllocatePool(PagedPool, ResourceListSize);
+      if (Adapter->NdisMiniportBlock.AllocatedResourcesTranslated == NULL)
+        {
+          NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+         ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
+          return STATUS_INSUFFICIENT_RESOURCES;
+        }
 
       RtlCopyMemory(Adapter->NdisMiniportBlock.AllocatedResourcesTranslated,
                     Stack->Parameters.StartDevice.AllocatedResourcesTranslated,
                     ResourceListSize);
-    }
+   }
 
   /*
    * Store the Bus Type, Bus Number and Slot information. It's used by
@@ -1335,6 +1771,12 @@ NdisIPnPStartDevice(
    */
 
   NdisOpenConfiguration(&NdisStatus, &ConfigHandle, (NDIS_HANDLE)&WrapperContext);
+  if (NdisStatus != NDIS_STATUS_SUCCESS)
+  {
+      NDIS_DbgPrint(MIN_TRACE, ("Failed to open configuration key\n"));
+      ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
+      return NdisStatus;
+  }
 
   Size = sizeof(ULONG);
   Status = IoGetDeviceProperty(Adapter->NdisMiniportBlock.PhysicalDeviceObject,
@@ -1391,6 +1833,8 @@ NdisIPnPStartDevice(
 
         Adapter->NdisMiniportBlock.SlotNumber = SlotNumber.u.AsULONG;
     }
+  WrapperContext.SlotNumber = Adapter->NdisMiniportBlock.SlotNumber;
+
   NdisCloseConfiguration(ConfigHandle);
 
   /* Set handlers (some NDIS macros require these) */
@@ -1403,6 +1847,9 @@ NdisIPnPStartDevice(
   Adapter->NdisMiniportBlock.PacketIndicateHandler= MiniIndicateReceivePacket;
   Adapter->NdisMiniportBlock.StatusHandler        = MiniStatus;
   Adapter->NdisMiniportBlock.StatusCompleteHandler= MiniStatusComplete;
+  Adapter->NdisMiniportBlock.SendPacketsHandler   = ProSendPackets;
+  Adapter->NdisMiniportBlock.QueryCompleteHandler = MiniRequestComplete;
+  Adapter->NdisMiniportBlock.SetCompleteHandler   = MiniRequestComplete;
 
   /*
    * Call MiniportInitialize.
@@ -1415,13 +1862,18 @@ NdisIPnPStartDevice(
 
   ZwClose(WrapperContext.RegistryHandle);
 
-  if (NdisStatus != NDIS_STATUS_SUCCESS ||
-      SelectedMediumIndex >= MEDIA_ARRAY_SIZE)
+  if (NdisStatus != NDIS_STATUS_SUCCESS)
     {
       NDIS_DbgPrint(MIN_TRACE, ("MiniportInitialize() failed for an adapter.\n"));
       ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
-      if (NdisStatus == NDIS_STATUS_SUCCESS) NdisStatus = NDIS_STATUS_FAILURE;
-      return (NTSTATUS)NdisStatus;
+      return NdisStatus;
+    }
+
+  if (SelectedMediumIndex >= MEDIA_ARRAY_SIZE)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("MiniportInitialize() selected a bad index\n"));
+      ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
+      return NDIS_STATUS_UNSUPPORTED_MEDIA;
     }
 
   Adapter->NdisMiniportBlock.MediaType = MediaArray[SelectedMediumIndex];
@@ -1435,7 +1887,17 @@ NdisIPnPStartDevice(
         NdisStatus = DoQueries(Adapter, AddressOID);
         if (NdisStatus == NDIS_STATUS_SUCCESS)
           {
-            Success = EthCreateFilter(32, /* FIXME: Query this from miniport. */
+            NdisStatus = MiniQueryInformation(Adapter, OID_802_3_MAXIMUM_LIST_SIZE, sizeof(UINT),
+                                    &MaxMulticastAddresses, &BytesWritten);
+
+            if (NdisStatus != NDIS_STATUS_SUCCESS)
+            {
+               ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
+               NDIS_DbgPrint(MIN_TRACE, ("MiniQueryInformation failed (%x)\n", NdisStatus));
+               return NdisStatus;
+            }
+
+            Success = EthCreateFilter(MaxMulticastAddresses,
                                       Adapter->Address.Type.Medium802_3,
                                       &Adapter->NdisMiniportBlock.EthDB);
             if (Success)
@@ -1449,27 +1911,30 @@ NdisIPnPStartDevice(
         /* FIXME: Support other types of media */
         NDIS_DbgPrint(MIN_TRACE, ("error: unsupported media\n"));
         ASSERT(FALSE);
-/* FIXME - KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql); */
        ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
         return STATUS_UNSUCCESSFUL;
     }
 
-  if (!Success || NdisStatus != NDIS_STATUS_SUCCESS)
+  if (NdisStatus != NDIS_STATUS_SUCCESS)
     {
-      NDIS_DbgPrint(MAX_TRACE, ("couldn't create filter (%x)\n", NdisStatus));
-      if (Adapter->LookaheadBuffer)
-        {
-          ExFreePool(Adapter->LookaheadBuffer);
-          Adapter->LookaheadBuffer = NULL;
-        }
-      ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
-      if (NdisStatus == NDIS_STATUS_SUCCESS) NdisStatus = NDIS_STATUS_FAILURE;
-      return (NTSTATUS)NdisStatus;
+      NDIS_DbgPrint(MIN_TRACE, ("couldn't create filter (%x)\n", NdisStatus));
+      return NdisStatus;
     }
 
+  /* Check for a hang every two seconds if it wasn't set in MiniportInitialize */
+  if (Adapter->NdisMiniportBlock.CheckForHangSeconds == 0)
+      Adapter->NdisMiniportBlock.CheckForHangSeconds = 2;
+
   Adapter->NdisMiniportBlock.OldPnPDeviceState = Adapter->NdisMiniportBlock.PnPDeviceState;
   Adapter->NdisMiniportBlock.PnPDeviceState = NdisPnPDeviceStarted;
 
+  IoSetDeviceInterfaceState(&Adapter->NdisMiniportBlock.SymbolicLinkName, TRUE);
+
+  Timeout.QuadPart = Int32x32To64(Adapter->NdisMiniportBlock.CheckForHangSeconds, -1000000);
+  KeSetTimerEx(&Adapter->NdisMiniportBlock.WakeUpDpcTimer.Timer, Timeout,
+               Adapter->NdisMiniportBlock.CheckForHangSeconds * 1000,
+               &Adapter->NdisMiniportBlock.WakeUpDpcTimer.Dpc);
+
   /* Put adapter in adapter list for this miniport */
   ExInterlockedInsertTailList(&Adapter->NdisMiniportBlock.DriverHandle->DeviceList, &Adapter->MiniportListEntry, &Adapter->NdisMiniportBlock.DriverHandle->Lock);
 
@@ -1492,25 +1957,19 @@ NdisIPnPStopDevice(
  */
 {
   PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)DeviceObject->DeviceExtension;
-  KIRQL OldIrql;
 
   /* Remove adapter from adapter list for this miniport */
-  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.DriverHandle->Lock, &OldIrql);
-  RemoveEntryList(&Adapter->MiniportListEntry);
-  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.DriverHandle->Lock, OldIrql);
+  ExInterlockedRemoveEntryList(&Adapter->MiniportListEntry, &Adapter->NdisMiniportBlock.DriverHandle->Lock);
 
   /* Remove adapter from global adapter list */
-  KeAcquireSpinLock(&AdapterListLock, &OldIrql);
-  RemoveEntryList(&Adapter->ListEntry);
-  KeReleaseSpinLock(&AdapterListLock, OldIrql);
+  ExInterlockedRemoveEntryList(&Adapter->ListEntry, &AdapterListLock);
+
+  KeCancelTimer(&Adapter->NdisMiniportBlock.WakeUpDpcTimer.Timer);
 
   (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.HaltHandler)(Adapter);
 
-  if (Adapter->LookaheadBuffer)
-    {
-      ExFreePool(Adapter->LookaheadBuffer);
-      Adapter->LookaheadBuffer = NULL;
-    }
+  IoSetDeviceInterfaceState(&Adapter->NdisMiniportBlock.SymbolicLinkName, FALSE);
+
   if (Adapter->NdisMiniportBlock.AllocatedResources)
     {
       ExFreePool(Adapter->NdisMiniportBlock.AllocatedResources);
@@ -1522,12 +1981,86 @@ NdisIPnPStopDevice(
       Adapter->NdisMiniportBlock.AllocatedResourcesTranslated = NULL;
     }
 
+  if (Adapter->NdisMiniportBlock.Resources)
+    {
+      ExFreePool(Adapter->NdisMiniportBlock.Resources);
+      Adapter->NdisMiniportBlock.Resources = NULL;
+    }
+
+  if (Adapter->NdisMiniportBlock.EthDB)
+    {
+      EthDeleteFilter(Adapter->NdisMiniportBlock.EthDB);
+      Adapter->NdisMiniportBlock.EthDB = NULL;
+    }
+
   Adapter->NdisMiniportBlock.OldPnPDeviceState = Adapter->NdisMiniportBlock.PnPDeviceState;
   Adapter->NdisMiniportBlock.PnPDeviceState = NdisPnPDeviceStopped;
 
   return STATUS_SUCCESS;
 }
 
+NTSTATUS
+NTAPI
+NdisIShutdown(
+    IN PDEVICE_OBJECT DeviceObject,
+    PIRP Irp)
+{
+  PLOGICAL_ADAPTER Adapter = DeviceObject->DeviceExtension;
+  PMINIPORT_BUGCHECK_CONTEXT Context = Adapter->BugcheckContext;
+  ADAPTER_SHUTDOWN_HANDLER ShutdownHandler = Context->ShutdownHandler;
+
+  ASSERT(ShutdownHandler);
+
+  ShutdownHandler(Context->DriverContext);
+
+  Irp->IoStatus.Status = STATUS_SUCCESS;
+  Irp->IoStatus.Information = 0;
+
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+  return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+NdisIDeviceIoControl(
+    IN PDEVICE_OBJECT DeviceObject,
+    PIRP Irp)
+{
+  PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)DeviceObject->DeviceExtension;
+  PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+  NDIS_STATUS Status = STATUS_NOT_SUPPORTED;
+
+  Irp->IoStatus.Information = 0;
+
+  ASSERT(Adapter);
+
+  switch (Stack->Parameters.DeviceIoControl.IoControlCode)
+  {
+    case IOCTL_NDIS_QUERY_GLOBAL_STATS:
+      Status = MiniQueryInformation(Adapter,
+                                    *(PNDIS_OID)Irp->AssociatedIrp.SystemBuffer,
+                                    Stack->Parameters.DeviceIoControl.OutputBufferLength,
+                                    MmGetSystemAddressForMdl(Irp->MdlAddress),
+                                    &Irp->IoStatus.Information);
+      break;
+
+    default:
+      ASSERT(FALSE);
+      break;
+  }
+
+  if (Status != NDIS_STATUS_PENDING)
+  {
+      Irp->IoStatus.Status = Status;
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+  }
+  else
+      IoMarkIrpPending(Irp);
+
+  return Status;
+}
+
 \f
 NTSTATUS
 NTAPI
@@ -1547,6 +2080,8 @@ NdisIDispatchPnp(
           {
              Status = NdisIPnPStartDevice(DeviceObject, Irp);
           }
+          else
+              NDIS_DbgPrint(MIN_TRACE, ("Lower driver failed device start\n"));
         Irp->IoStatus.Status = Status;
         IoCompleteRequest(Irp, IO_NO_INCREMENT);
         break;
@@ -1557,16 +2092,33 @@ NdisIDispatchPnp(
           {
             Status = NdisIPnPStopDevice(DeviceObject, Irp);
           }
+          else
+            NDIS_DbgPrint(MIN_TRACE, ("Lower driver failed device stop\n"));
         Irp->IoStatus.Status = Status;
         IoCompleteRequest(Irp, IO_NO_INCREMENT);
         break;
 
-      case IRP_MN_QUERY_DEVICE_RELATIONS:
-        Status = STATUS_NOT_SUPPORTED;
+      case IRP_MN_QUERY_REMOVE_DEVICE:
+      case IRP_MN_QUERY_STOP_DEVICE:
+        Status = NdisIPnPQueryStopDevice(DeviceObject, Irp);
         Irp->IoStatus.Status = Status;
         IoCompleteRequest(Irp, IO_NO_INCREMENT);
         break;
 
+      case IRP_MN_CANCEL_REMOVE_DEVICE:
+      case IRP_MN_CANCEL_STOP_DEVICE:
+        Status = NdisIPnPCancelStopDevice(DeviceObject, Irp);
+        Irp->IoStatus.Status = Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+
+      case IRP_MN_QUERY_PNP_DEVICE_STATE:
+        Status = NDIS_STATUS_SUCCESS;
+        Irp->IoStatus.Status = Status;
+        Irp->IoStatus.Information |= Adapter->NdisMiniportBlock.PnPFlags;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+
       default:
         IoSkipCurrentIrpStackLocation(Irp);
         Status = IoCallDriver(Adapter->NdisMiniportBlock.NextDeviceObject, Irp);
@@ -1600,16 +2152,17 @@ NdisIAddDevice(
   PDEVICE_OBJECT DeviceObject;
   PLOGICAL_ADAPTER Adapter;
   NTSTATUS Status;
+  UINT i;
 
   /*
    * Gain the access to the miniport data structure first.
    */
 
-  MiniportPtr = IoGetDriverObjectExtension(DriverObject, (PVOID)TAG('D','I','M','N'));
+  MiniportPtr = IoGetDriverObjectExtension(DriverObject, (PVOID)'NMID');
   if (MiniportPtr == NULL)
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Can't get driver object extension.\n"));
-      return STATUS_UNSUCCESSFUL;
+      NDIS_DbgPrint(MIN_TRACE, ("Can't get driver object extension.\n"));
+      return NDIS_STATUS_FAILURE;
     }
   Miniport = *MiniportPtr;
 
@@ -1623,9 +2176,9 @@ NdisIAddDevice(
 
   Status = IoGetDeviceProperty(PhysicalDeviceObject, DevicePropertyDriverKeyName,
                                0, NULL, &DriverKeyLength);
-  if (Status != STATUS_BUFFER_TOO_SMALL && Status != STATUS_BUFFER_OVERFLOW)
+  if (Status != STATUS_BUFFER_TOO_SMALL && Status != STATUS_BUFFER_OVERFLOW && Status != STATUS_SUCCESS)
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Can't get miniport driver key length.\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Can't get miniport driver key length.\n"));
       return Status;
     }
 
@@ -1633,7 +2186,7 @@ NdisIAddDevice(
                                     sizeof(ClassKeyName) + sizeof(LinkageKeyName));
   if (LinkageKeyBuffer == NULL)
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Can't allocate memory for driver key name.\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Can't allocate memory for driver key name.\n"));
       return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -1643,7 +2196,7 @@ NdisIAddDevice(
                                &DriverKeyLength);
   if (!NT_SUCCESS(Status))
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Can't get miniport driver key.\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Can't get miniport driver key.\n"));
       ExFreePool(LinkageKeyBuffer);
       return Status;
     }
@@ -1671,7 +2224,7 @@ NdisIAddDevice(
   ExFreePool(LinkageKeyBuffer);
   if (!NT_SUCCESS(Status))
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Can't get miniport device name. (%x)\n", Status));
+      NDIS_DbgPrint(MIN_TRACE, ("Can't get miniport device name. (%x)\n", Status));
       return Status;
     }
 
@@ -1698,10 +2251,22 @@ NdisIAddDevice(
   Adapter = (PLOGICAL_ADAPTER)DeviceObject->DeviceExtension;
   KeInitializeSpinLock(&Adapter->NdisMiniportBlock.Lock);
   InitializeListHead(&Adapter->ProtocolListHead);
-  Adapter->NdisMiniportBlock.DriverHandle = Miniport;
 
-  Adapter->NdisMiniportBlock.MiniportName = ExportName;
+  Status = IoRegisterDeviceInterface(PhysicalDeviceObject,
+                                     &GUID_DEVINTERFACE_NET,
+                                     NULL,
+                                     &Adapter->NdisMiniportBlock.SymbolicLinkName);
+
+  if (!NT_SUCCESS(Status))
+  {
+      NDIS_DbgPrint(MIN_TRACE, ("Could not create device interface.\n"));
+      IoDeleteDevice(DeviceObject);
+      RtlFreeUnicodeString(&ExportName);
+      return Status;
+  }
 
+  Adapter->NdisMiniportBlock.DriverHandle = Miniport;
+  Adapter->NdisMiniportBlock.MiniportName = ExportName;
   Adapter->NdisMiniportBlock.DeviceObject = DeviceObject;
   Adapter->NdisMiniportBlock.PhysicalDeviceObject = PhysicalDeviceObject;
   Adapter->NdisMiniportBlock.NextDeviceObject =
@@ -1711,7 +2276,11 @@ NdisIAddDevice(
   Adapter->NdisMiniportBlock.OldPnPDeviceState = 0;
   Adapter->NdisMiniportBlock.PnPDeviceState = NdisPnPDeviceAdded;
 
-  KeInitializeDpc(&Adapter->NdisMiniportBlock.DeferredDpc, MiniportDpc, (PVOID)Adapter);
+  for (i = 0; i < NUMBER_OF_WORK_ITEM_TYPES; i++)
+       Adapter->NdisMiniportBlock.WorkQueue[i].Next = NULL;
+
+  KeInitializeTimer(&Adapter->NdisMiniportBlock.WakeUpDpcTimer.Timer);
+  KeInitializeDpc(&Adapter->NdisMiniportBlock.WakeUpDpcTimer.Dpc, MiniportHangDpc, Adapter);
 
   DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
 
@@ -1760,42 +2329,76 @@ NdisMRegisterMiniport(
         break;
 
       default:
-        NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics version.\n"));
+        NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics version.\n"));
         return NDIS_STATUS_BAD_VERSION;
     }
 
+   NDIS_DbgPrint(MIN_TRACE, ("Initializing an NDIS %u.%u miniport\n", 
+                              MiniportCharacteristics->MajorNdisVersion,
+                              MiniportCharacteristics->MinorNdisVersion));
+
   if (CharacteristicsLength < MinSize)
     {
-        NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
+        NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics length.\n"));
         return NDIS_STATUS_BAD_CHARACTERISTICS;
     }
 
   /* Check if mandatory MiniportXxx functions are specified */
   if ((!MiniportCharacteristics->HaltHandler) ||
-      (!MiniportCharacteristics->InitializeHandler)||
-      (!MiniportCharacteristics->QueryInformationHandler) ||
-      (!MiniportCharacteristics->ResetHandler) ||
-      (!MiniportCharacteristics->SetInformationHandler))
+       (!MiniportCharacteristics->InitializeHandler)||
+       (!MiniportCharacteristics->ResetHandler))
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics.\n"));
       return NDIS_STATUS_BAD_CHARACTERISTICS;
     }
 
+  if (MiniportCharacteristics->MajorNdisVersion < 0x05)
+  {
+      if ((!MiniportCharacteristics->QueryInformationHandler) ||
+          (!MiniportCharacteristics->SetInformationHandler))
+      {
+           NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics. (Set/Query)\n"));
+           return NDIS_STATUS_BAD_CHARACTERISTICS;
+      }
+  }
+  else
+  {
+      if (((!MiniportCharacteristics->QueryInformationHandler) ||
+           (!MiniportCharacteristics->SetInformationHandler)) &&
+           (!MiniportCharacteristics->CoRequestHandler))
+      {
+           NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics. (Set/Query)\n"));
+           return NDIS_STATUS_BAD_CHARACTERISTICS;
+      }
+  }
+
   if (MiniportCharacteristics->MajorNdisVersion == 0x03)
     {
       if (!MiniportCharacteristics->SendHandler)
         {
-          NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
+          NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics. (NDIS 3.0)\n"));
           return NDIS_STATUS_BAD_CHARACTERISTICS;
         }
     }
-  else if (MiniportCharacteristics->MajorNdisVersion >= 0x04)
+  else if (MiniportCharacteristics->MajorNdisVersion == 0x04)
     {
-      /* NDIS 4.0+ */
+      /* NDIS 4.0 */
       if ((!MiniportCharacteristics->SendHandler) &&
           (!MiniportCharacteristics->SendPacketsHandler))
         {
-          NDIS_DbgPrint(DEBUG_MINIPORT, ("Bad miniport characteristics.\n"));
+          NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics. (NDIS 4.0)\n"));
+          return NDIS_STATUS_BAD_CHARACTERISTICS;
+        }
+    }
+  else if (MiniportCharacteristics->MajorNdisVersion == 0x05)
+    {
+      /* TODO: Add more checks here */
+
+      if ((!MiniportCharacteristics->SendHandler) &&
+          (!MiniportCharacteristics->SendPacketsHandler) &&
+          (!MiniportCharacteristics->CoSendPacketsHandler))
+        {
+          NDIS_DbgPrint(MIN_TRACE, ("Bad miniport characteristics. (NDIS 5.0)\n"));
           return NDIS_STATUS_BAD_CHARACTERISTICS;
         }
     }
@@ -1809,17 +2412,19 @@ NdisMRegisterMiniport(
    * structure in the driver extension or what?
    */
 
-  Status = IoAllocateDriverObjectExtension(Miniport->DriverObject, (PVOID)TAG('D','I','M','N'),
+  Status = IoAllocateDriverObjectExtension(Miniport->DriverObject, (PVOID)'NMID',
                                            sizeof(PNDIS_M_DRIVER_BLOCK), (PVOID*)&MiniportPtr);
   if (!NT_SUCCESS(Status))
     {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Can't allocate driver object extension.\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("Can't allocate driver object extension.\n"));
       return NDIS_STATUS_RESOURCES;
     }
 
   *MiniportPtr = Miniport;
 
   Miniport->DriverObject->MajorFunction[IRP_MJ_PNP] = NdisIDispatchPnp;
+  Miniport->DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = NdisIShutdown;
+  Miniport->DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NdisIDeviceIoControl;
   Miniport->DriverObject->DriverExtension->AddDevice = NdisIAddDevice;
 
   return NDIS_STATUS_SUCCESS;
@@ -1893,20 +2498,6 @@ NdisMTransferDataComplete(
 }
 
 \f
-/*
- * @implemented
- */
-#undef NdisMSetInformationComplete
-VOID
-EXPORT
-NdisMSetInformationComplete(
-    IN  NDIS_HANDLE MiniportAdapterHandle,
-    IN  NDIS_STATUS Status)
-{
-  (*((PNDIS_MINIPORT_BLOCK)(MiniportAdapterHandle))->SetCompleteHandler)(MiniportAdapterHandle, Status);
-}
-
-\f
 /*
  * @implemented
  */
@@ -1956,8 +2547,6 @@ NdisMSetAttributesEx(
  *     AdapterType               = Specifies the I/O bus interface of the caller's NIC
  */
 {
-  /* TODO: Take CheckForHandTimeInSeconds into account! */
-
   PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(MiniportAdapterHandle);
 
   NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
@@ -1965,8 +2554,19 @@ NdisMSetAttributesEx(
   Adapter->NdisMiniportBlock.MiniportAdapterContext = MiniportAdapterContext;
   Adapter->NdisMiniportBlock.Flags = AttributeFlags;
   Adapter->NdisMiniportBlock.AdapterType = AdapterType;
+  if (CheckForHangTimeInSeconds > 0)
+      Adapter->NdisMiniportBlock.CheckForHangSeconds = CheckForHangTimeInSeconds;
   if (AttributeFlags & NDIS_ATTRIBUTE_INTERMEDIATE_DRIVER)
     NDIS_DbgPrint(MAX_TRACE, ("Intermediate drivers not supported yet.\n"));
+
+
+  if (Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.AdapterShutdownHandler)
+  {
+      NDIS_DbgPrint(MAX_TRACE, ("Miniport set AdapterShutdownHandler in MiniportCharacteristics\n"));
+      NdisMRegisterAdapterShutdownHandler(Adapter,
+                      Adapter->NdisMiniportBlock.MiniportAdapterContext,
+                      Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.AdapterShutdownHandler);
+  }
 }
 
 \f
@@ -2078,8 +2678,396 @@ NdisTerminateWrapper(
 
   ExFreePool(Miniport->RegistryPath->Buffer);
   ExFreePool(Miniport->RegistryPath);
+  ExInterlockedRemoveEntryList(&Miniport->ListEntry, &MiniportListLock);
   ExFreePool(Miniport);
 }
 
+
+/*
+ * @implemented
+ */
+NDIS_STATUS
+EXPORT
+NdisMQueryAdapterInstanceName(
+    OUT PNDIS_STRING    AdapterInstanceName,
+    IN  NDIS_HANDLE     MiniportAdapterHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)MiniportAdapterHandle;
+    UNICODE_STRING AdapterName;
+
+    NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    AdapterName.Length = 0;
+    AdapterName.MaximumLength = Adapter->NdisMiniportBlock.MiniportName.MaximumLength;
+    AdapterName.Buffer = ExAllocatePool(PagedPool, AdapterName.MaximumLength);
+    if (!AdapterName.Buffer) {
+        NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    RtlCopyUnicodeString(&AdapterName, &Adapter->NdisMiniportBlock.MiniportName);
+
+    *AdapterInstanceName = AdapterName;
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisDeregisterAdapterShutdownHandler(
+    IN  NDIS_HANDLE NdisAdapterHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 4.0
+ */
+{
+    NdisMDeregisterAdapterShutdownHandler(NdisAdapterHandle);
+}
+
+
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisRegisterAdapterShutdownHandler(
+    IN  NDIS_HANDLE                 NdisAdapterHandle,
+    IN  PVOID                       ShutdownContext,
+    IN  ADAPTER_SHUTDOWN_HANDLER    ShutdownHandler)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 4.0
+ */
+{
+    NdisMRegisterAdapterShutdownHandler(NdisAdapterHandle,
+                                        ShutdownContext,
+                                        ShutdownHandler);
+}
+
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisMGetDeviceProperty(
+    IN      NDIS_HANDLE         MiniportAdapterHandle,
+    IN OUT  PDEVICE_OBJECT      *PhysicalDeviceObject           OPTIONAL,
+    IN OUT  PDEVICE_OBJECT      *FunctionalDeviceObject         OPTIONAL,
+    IN OUT  PDEVICE_OBJECT      *NextDeviceObject               OPTIONAL,
+    IN OUT  PCM_RESOURCE_LIST   *AllocatedResources             OPTIONAL,
+    IN OUT  PCM_RESOURCE_LIST   *AllocatedResourcesTranslated   OPTIONAL)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
+
+    NDIS_DbgPrint(MAX_TRACE, ("Called\n"));
+
+    if (PhysicalDeviceObject != NULL)
+        *PhysicalDeviceObject = Adapter->NdisMiniportBlock.PhysicalDeviceObject;
+
+    if (FunctionalDeviceObject != NULL)
+        *FunctionalDeviceObject = Adapter->NdisMiniportBlock.DeviceObject;
+
+    if (NextDeviceObject != NULL)
+        *NextDeviceObject = Adapter->NdisMiniportBlock.NextDeviceObject;
+
+    if (AllocatedResources != NULL)
+        *AllocatedResources = Adapter->NdisMiniportBlock.AllocatedResources;
+
+    if (AllocatedResourcesTranslated != NULL)
+        *AllocatedResourcesTranslated = Adapter->NdisMiniportBlock.AllocatedResourcesTranslated;
+}
+
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisMRegisterUnloadHandler(
+    IN  NDIS_HANDLE     NdisWrapperHandle,
+    IN  PDRIVER_UNLOAD  UnloadHandler)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PNDIS_M_DRIVER_BLOCK DriverBlock = NdisWrapperHandle;
+
+    NDIS_DbgPrint(MAX_TRACE, ("Miniport registered unload handler\n"));
+
+    DriverBlock->DriverObject->DriverUnload = UnloadHandler;
+}
+
+/*
+ * @implemented
+ */
+NDIS_STATUS
+EXPORT
+NdisMRegisterDevice(
+    IN  NDIS_HANDLE         NdisWrapperHandle,
+    IN  PNDIS_STRING        DeviceName,
+    IN  PNDIS_STRING        SymbolicName,
+    IN  PDRIVER_DISPATCH    MajorFunctions[],
+    OUT PDEVICE_OBJECT      *pDeviceObject,
+    OUT NDIS_HANDLE         *NdisDeviceHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PNDIS_M_DRIVER_BLOCK DriverBlock = NdisWrapperHandle;
+    PNDIS_M_DEVICE_BLOCK DeviceBlock;
+    PDEVICE_OBJECT DeviceObject;
+    NDIS_STATUS Status;
+    UINT i;
+
+    NDIS_DbgPrint(MAX_TRACE, ("Called\n"));
+
+    Status = IoCreateDevice(DriverBlock->DriverObject,
+                            0, /* This space is reserved for us. Should we use it? */
+                            DeviceName,
+                            FILE_DEVICE_NETWORK,
+                            0,
+                            FALSE,
+                            &DeviceObject);
+
+    if (!NT_SUCCESS(Status))
+    {
+        NDIS_DbgPrint(MIN_TRACE, ("IoCreateDevice failed (%x)\n", Status));
+        return Status;
+    }
+    
+    Status = IoCreateSymbolicLink(SymbolicName, DeviceName);
+
+    if (!NT_SUCCESS(Status))
+    {
+        NDIS_DbgPrint(MIN_TRACE, ("IoCreateSymbolicLink failed (%x)\n", Status));
+        IoDeleteDevice(DeviceObject);
+        return Status;
+    }
+
+    DeviceBlock = ExAllocatePool(NonPagedPool, sizeof(NDIS_M_DEVICE_BLOCK));
+
+    if (!DeviceBlock)
+    {
+        NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+        IoDeleteDevice(DeviceObject);
+        IoDeleteSymbolicLink(SymbolicName);
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
+         DriverBlock->DriverObject->MajorFunction[i] = MajorFunctions[i];
+
+    DriverBlock->DriverObject->MajorFunction[IRP_MJ_PNP] = NdisIDispatchPnp;
+
+    DeviceBlock->DeviceObject = DeviceObject;
+    DeviceBlock->SymbolicName = SymbolicName;
+
+    *pDeviceObject = DeviceObject;
+    *NdisDeviceHandle = DeviceBlock;
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NDIS_STATUS
+EXPORT
+NdisMDeregisterDevice(
+    IN  NDIS_HANDLE NdisDeviceHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PNDIS_M_DEVICE_BLOCK DeviceBlock = NdisDeviceHandle;
+
+    IoDeleteDevice(DeviceBlock->DeviceObject);
+
+    IoDeleteSymbolicLink(DeviceBlock->SymbolicName);
+
+    ExFreePool(DeviceBlock);
+
+    return NDIS_STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NDIS_STATUS
+EXPORT
+NdisQueryAdapterInstanceName(
+    OUT PNDIS_STRING    AdapterInstanceName,
+    IN  NDIS_HANDLE     NdisBindingHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PADAPTER_BINDING AdapterBinding = NdisBindingHandle;
+    PLOGICAL_ADAPTER Adapter = AdapterBinding->Adapter;
+
+    return NdisMQueryAdapterInstanceName(AdapterInstanceName,
+                                         Adapter);
+}
+
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisCompletePnPEvent(
+    IN  NDIS_STATUS     Status,
+    IN  NDIS_HANDLE     NdisBindingHandle,
+    IN  PNET_PNP_EVENT  NetPnPEvent)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+  PIRP Irp = (PIRP)NetPnPEvent->NdisReserved[0];
+  PLIST_ENTRY CurrentEntry = (PLIST_ENTRY)NetPnPEvent->NdisReserved[1];
+  PADAPTER_BINDING AdapterBinding = NdisBindingHandle;
+  PLOGICAL_ADAPTER Adapter = AdapterBinding->Adapter;
+  NDIS_STATUS NdisStatus;
+
+  if (Status != NDIS_STATUS_SUCCESS)
+  {
+      if (NetPnPEvent->Buffer) ExFreePool(NetPnPEvent->Buffer);
+      ExFreePool(NetPnPEvent);
+      Irp->IoStatus.Status = Status;
+      IoCompleteRequest(Irp, IO_NO_INCREMENT);
+      return;
+  }
+
+  while (CurrentEntry != &Adapter->ProtocolListHead)
+  {
+     AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
+
+     NdisStatus = (*AdapterBinding->ProtocolBinding->Chars.PnPEventHandler)(
+      AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+      NetPnPEvent);
+
+     if (NdisStatus == NDIS_STATUS_PENDING)
+     {
+         NetPnPEvent->NdisReserved[1] = (ULONG_PTR)CurrentEntry->Flink;
+         return;
+     }
+     else if (NdisStatus != NDIS_STATUS_SUCCESS)
+     {
+         if (NetPnPEvent->Buffer) ExFreePool(NetPnPEvent->Buffer);
+         ExFreePool(NetPnPEvent);
+         Irp->IoStatus.Status = NdisStatus;
+         IoCompleteRequest(Irp, IO_NO_INCREMENT);
+         return;
+     }
+
+     CurrentEntry = CurrentEntry->Flink;
+  }
+
+  if (NetPnPEvent->Buffer) ExFreePool(NetPnPEvent->Buffer);
+  ExFreePool(NetPnPEvent);
+
+  Irp->IoStatus.Status = NDIS_STATUS_SUCCESS;
+  IoCompleteRequest(Irp, IO_NO_INCREMENT);
+}
+
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisCancelSendPackets(
+    IN NDIS_HANDLE  NdisBindingHandle,
+    IN PVOID  CancelId)
+{
+    PADAPTER_BINDING AdapterBinding = NdisBindingHandle;
+    PLOGICAL_ADAPTER Adapter = AdapterBinding->Adapter;
+
+    NDIS_DbgPrint(MAX_TRACE, ("Called for ID %x.\n", CancelId));
+
+    if (Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CancelSendPacketsHandler)
+    {
+        (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CancelSendPacketsHandler)(
+          Adapter->NdisMiniportBlock.MiniportAdapterContext,
+          CancelId);
+    }
+}
+
+
+/*
+ * @implemented
+ */
+NDIS_HANDLE
+EXPORT
+NdisIMGetBindingContext(
+    IN  NDIS_HANDLE NdisBindingHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PADAPTER_BINDING AdapterBinding = NdisBindingHandle;
+    PLOGICAL_ADAPTER Adapter = AdapterBinding->Adapter;
+
+    NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    return Adapter->NdisMiniportBlock.DeviceContext;
+}
+
+
+/*
+ * @implemented
+ */
+NDIS_HANDLE
+EXPORT
+NdisIMGetDeviceContext(
+    IN  NDIS_HANDLE MiniportAdapterHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
+
+    NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+    return Adapter->NdisMiniportBlock.DeviceContext;
+}
+
 /* EOF */