- Merge aicom-network-fixes up to r36740
[reactos.git] / reactos / drivers / network / ndis / ndis / miniport.c
index 9400eec..ae7231c 100644 (file)
@@ -156,7 +156,7 @@ MiniIndicateData(
  *     PacketSize          = Total size of received packet
  */
 {
-  /* KIRQL OldIrql; */
+  KIRQL OldIrql;
   PLIST_ENTRY CurrentEntry;
   PADAPTER_BINDING AdapterBinding;
 
@@ -166,27 +166,8 @@ MiniIndicateData(
 
   MiniDisplayPacket2(HeaderBuffer, HeaderBufferSize, LookaheadBuffer, LookaheadBufferSize);
 
-  /*
-   * XXX Think about this.  This is probably broken.  Spinlocks are
-   * taken out for now until i comprehend the Right Way to do this.
-   *
-   * This used to acquire the MiniportBlock spinlock and hold it until
-   * just before the call to ReceiveHandler.  It would then release and
-   * subsequently re-acquire the lock.
-   *
-   * I don't see how this does any good, as it would seem he's just
-   * trying to protect the packet list.  If somebody else dequeues
-   * a packet, we are in fact in bad shape, but we don't want to
-   * necessarily call the receive handler at elevated irql either.
-   *
-   * therefore: We *are* going to call the receive handler at high irql
-   * (due to holding the lock) for now, and eventually we have to
-   * figure out another way to protect this packet list.
-   *
-   * UPDATE: this is busted; this results in a recursive lock acquisition.
-   */
-  //NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
-  //KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+  NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
+  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
     {
       CurrentEntry = Adapter->ProtocolListHead.Flink;
       NDIS_DbgPrint(DEBUG_MINIPORT, ("CurrentEntry = %x\n", CurrentEntry));
@@ -201,29 +182,6 @@ MiniIndicateData(
           AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
          NDIS_DbgPrint(DEBUG_MINIPORT, ("AdapterBinding = %x\n", AdapterBinding));
 
-          /* see above */
-          /* 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",
@@ -246,13 +204,10 @@ MiniIndicateData(
               LookaheadBufferSize,
               PacketSize);
 
-          /* see above */
-          /* KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql); */
-
           CurrentEntry = CurrentEntry->Flink;
         }
     }
-  //KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 
   NDIS_DbgPrint(MAX_TRACE, ("Leaving.\n"));
 }
@@ -260,60 +215,46 @@ 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;
-
-#define PACKET_TAG (('k' << 24) + ('P' << 16) + ('D' << 8) + 'N')
-
-      NdisAllocateMemoryWithTag((PVOID)&PacketBuffer, 1518, PACKET_TAG);
-      if(!PacketBuffer)
-        {
-          NDIS_DbgPrint(MIN_TRACE, ("insufficient resources\n"));
-          return;
-        }
-
-      NdisQueryPacket(PacketArray[i], NULL, NULL, &NdisBuffer, NULL);
-
-      while(NdisBuffer)
-        {
-          PNDIS_BUFFER CurrentBuffer;
-          PVOID BufferVa;
-          UINT BufferLen;
+  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
 
-          NdisQueryBuffer(NdisBuffer, &BufferVa, &BufferLen);
-          memcpy(PacketBuffer + PacketLength, BufferVa, BufferLen);
-          PacketLength += BufferLen;
+  CurrentEntry = Adapter->ProtocolListHead.Flink;
 
-          CurrentBuffer = NdisBuffer;
-          NdisGetNextBuffer(CurrentBuffer, &NdisBuffer);
-        }
+  while (CurrentEntry != &Adapter->ProtocolListHead)
+  {
+      AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
 
-      NDIS_DbgPrint(MID_TRACE, ("indicating a %d-byte packet\n", PacketLength));
+      if (AdapterBinding->ProtocolBinding->Chars.ReceivePacketHandler)
+      {
+          for (i = 0; i < NumberOfPackets; i++)
+          {
+              (*AdapterBinding->ProtocolBinding->Chars.ReceivePacketHandler)(
+               AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+               PacketArray[i]);
+          }
+      }
 
-      MiniIndicateData(Miniport, NULL, PacketBuffer, 14, PacketBuffer+14, PacketLength-14, PacketLength-14);
+      CurrentEntry = CurrentEntry->Flink;
+  }
 
-      NdisFreeMemory(PacketBuffer, 0, 0);
-    }
+  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 }
 
 \f
@@ -323,27 +264,40 @@ MiniResetComplete(
     IN  NDIS_STATUS Status,
     IN  BOOLEAN     AddressingReset)
 {
-    UNIMPLEMENTED
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
+    KIRQL OldIrql;
+    NDIS_DbgPrint(MIN_TRACE, ("FIXME: MiniResetComplete is partially implemented\n"));
+    NdisMIndicateStatus(Adapter, NDIS_STATUS_RESET_END, NULL, 0);
+    KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+    Adapter->MiniportBusy = FALSE;
+    KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 }
 
 
 \f
 VOID NTAPI
 MiniRequestComplete(
-    IN PNDIS_MINIPORT_BLOCK Adapter,
+    IN PNDIS_HANDLE MiniportAdapterHandle,
     IN PNDIS_REQUEST Request,
     IN NDIS_STATUS Status)
 {
+    PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)MiniportAdapterHandle;
     PNDIS_REQUEST_MAC_BLOCK MacBlock = (PNDIS_REQUEST_MAC_BLOCK)Request->MacReserved;
+    KIRQL OldIrql;
 
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
+    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
     if( MacBlock->Binding->RequestCompleteHandler ) {
         (*MacBlock->Binding->RequestCompleteHandler)(
             MacBlock->Binding->ProtocolBindingContext,
             Request,
             Status);
     }
+    KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    Adapter->MiniportBusy = FALSE;
+    KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    KeLowerIrql(OldIrql);
 }
 
 VOID NTAPI
@@ -360,16 +314,23 @@ MiniSendComplete(
  *     Status            = Status of send operation
  */
 {
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
     PADAPTER_BINDING AdapterBinding;
+    KIRQL OldIrql;
 
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
     AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[0];
 
+    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
     (*AdapterBinding->ProtocolBinding->Chars.SendCompleteHandler)(
         AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
         Packet,
         Status);
+    KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    Adapter->MiniportBusy = FALSE;
+    KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    KeLowerIrql(OldIrql);
 }
 
 
@@ -390,16 +351,23 @@ MiniTransferDataComplete(
     IN  NDIS_STATUS     Status,
     IN  UINT            BytesTransferred)
 {
+    PLOGICAL_ADAPTER Adapter = MiniportAdapterHandle;
     PADAPTER_BINDING AdapterBinding;
+    KIRQL OldIrql;
 
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
     AdapterBinding = (PADAPTER_BINDING)Packet->Reserved[0];
 
+    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
     (*AdapterBinding->ProtocolBinding->Chars.SendCompleteHandler)(
         AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
         Packet,
         Status);
+    KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    Adapter->MiniportBusy = FALSE;
+    KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    KeLowerIrql(OldIrql);
 }
 
 \f
@@ -569,10 +537,12 @@ MiniQueryInformation(
 {
   NDIS_STATUS NdisStatus;
   ULONG BytesNeeded;
+  KIRQL OldIrql;
 
   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
   /* call the miniport's queryinfo handler */
+  KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
   NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.QueryInformationHandler)(
       Adapter->NdisMiniportBlock.MiniportAdapterContext,
       Oid,
@@ -580,19 +550,99 @@ MiniQueryInformation(
       Size,
       BytesWritten,
       &BytesNeeded);
+  KeLowerIrql(OldIrql);
 
   /* FIXME: Wait in pending case! */
 
-  /* XXX is status_pending part of success macro? */
-  if ((NT_SUCCESS(NdisStatus)) || (NdisStatus == NDIS_STATUS_PENDING))
-    {
-      NDIS_DbgPrint(DEBUG_MINIPORT, ("Miniport returned status (0x%X).\n", NdisStatus));
-      return NdisStatus;
-    }
-
   return NdisStatus;
 }
 
+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;
+   KIRQL OldIrql;
+
+   KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+   if (Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CheckForHangHandler)
+       Ret = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.CheckForHangHandler)(
+         Adapter->NdisMiniportBlock.MiniportAdapterContext);
+   KeLowerIrql(OldIrql);
+
+   return Ret;
+}
+
+NDIS_STATUS
+MiniReset(
+    PLOGICAL_ADAPTER Adapter,
+    PBOOLEAN AddressingReset)
+/*
+ * FUNCTION: Resets the miniport
+ * ARGUMENTS:
+ *     Adapter = Pointer to the logical adapter object
+ *     AddressingReset = Set to TRUE if we need to call MiniportSetInformation later
+ * RETURNS:
+ *     Status of the operation
+ */
+{
+   NDIS_STATUS Status;
+   KIRQL OldIrql;
+
+   if (Adapter->MiniportBusy) {
+       KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+       MiniQueueWorkItem(Adapter, NdisWorkItemResetRequested, NULL);
+       KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+       return NDIS_STATUS_PENDING;
+   }
+
+   NdisMIndicateStatus(Adapter, NDIS_STATUS_RESET_START, NULL, 0);
+   NdisMIndicateStatusComplete(Adapter);
+
+   KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+   Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.ResetHandler)(
+            Adapter->NdisMiniportBlock.MiniportAdapterContext,
+            AddressingReset);
+   KeLowerIrql(OldIrql);
+
+   if (Status != NDIS_STATUS_PENDING) {
+       NdisMIndicateStatus(Adapter, NDIS_STATUS_RESET_END, NULL, 0);
+       NdisMIndicateStatusComplete(Adapter);
+   } else {
+       KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+       Adapter->MiniportBusy = TRUE;
+       KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+   }
+
+   return Status;
+}
+
+VOID STDCALL
+MiniportHangDpc(
+        PKDPC Dpc,
+        PVOID DeferredContext,
+        PVOID SystemArgument1,
+        PVOID SystemArgument2)
+{
+  PLOGICAL_ADAPTER Adapter = DeferredContext;
+  BOOLEAN AddressingReset = FALSE;
+
+
+  if (MiniCheckForHang(Adapter)) {
+      NDIS_DbgPrint(MIN_TRACE, ("Miniport detected adapter hang\n"));
+      MiniReset(Adapter, &AddressingReset);
+  }
+
+  /* FIXME: We should call MiniportSetInformation if AddressingReset is TRUE */
+}
+
 \f
 NDIS_STATUS
 FASTCALL
@@ -612,37 +662,52 @@ MiniQueueWorkItem(
  *     Status of operation
  */
 {
-    PNDIS_MINIPORT_WORK_ITEM Item;
+    PNDIS_MINIPORT_WORK_ITEM MiniportWorkItem;
+    PNDIS_WORK_ITEM NdisWorkItem;
+    PWORK_QUEUE_ITEM WorkQueueItem;
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
     ASSERT(Adapter);
     ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
 
-    Item = ExAllocatePool(NonPagedPool, sizeof(NDIS_MINIPORT_WORK_ITEM));
-    if (Item == NULL)
+    MiniportWorkItem = ExAllocatePool(NonPagedPool, sizeof(NDIS_MINIPORT_WORK_ITEM));
+    if (!MiniportWorkItem)
     {
         NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
         return NDIS_STATUS_RESOURCES;
     }
 
-    Item->WorkItemType    = WorkItemType;
-    Item->WorkItemContext = WorkItemContext;
+    NdisWorkItem = ExAllocatePool(NonPagedPool, sizeof(NDIS_WORK_ITEM));
+    if (!NdisWorkItem)
+    {
+        ExFreePool(MiniportWorkItem);
+        return NDIS_STATUS_RESOURCES;
+    }
+
+    MiniportWorkItem->WorkItemType    = WorkItemType;
+    MiniportWorkItem->WorkItemContext = WorkItemContext;
 
     /* safe due to adapter lock held */
-    Item->Link.Next = NULL;
+    MiniportWorkItem->Link.Next = NULL;
     if (!Adapter->WorkQueueHead)
     {
-        Adapter->WorkQueueHead = Item;
-        Adapter->WorkQueueTail = Item;
+        Adapter->WorkQueueHead = MiniportWorkItem;
+        Adapter->WorkQueueTail = MiniportWorkItem;
     }
     else
     {
-        Adapter->WorkQueueTail->Link.Next = (PSINGLE_LIST_ENTRY)Item;
-        Adapter->WorkQueueTail = Item;
+        Adapter->WorkQueueTail->Link.Next = (PSINGLE_LIST_ENTRY)MiniportWorkItem;
+        Adapter->WorkQueueTail = MiniportWorkItem;
     }
 
-    KeInsertQueueDpc(&Adapter->NdisMiniportBlock.DeferredDpc, NULL, NULL);
+    WorkQueueItem = (PWORK_QUEUE_ITEM)NdisWorkItem->WrapperReserved;
+
+    NdisWorkItem->Context = Adapter;
+
+    ExInitializeWorkItem(WorkQueueItem, MiniportWorker, NdisWorkItem);
+
+    ExQueueWorkItem(WorkQueueItem, CriticalWorkQueue);
 
     return NDIS_STATUS_SUCCESS;
 }
@@ -667,24 +732,40 @@ MiniDequeueWorkItem(
  *     Status of operation
  */
 {
-    PNDIS_MINIPORT_WORK_ITEM Item;
+    PNDIS_MINIPORT_WORK_ITEM MiniportWorkItem;
+    PNDIS_WORK_ITEM NdisWorkItem;
+    PWORK_QUEUE_ITEM WorkQueueItem;
+
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
-    Item = Adapter->WorkQueueHead;
+    if (Adapter->MiniportBusy) {
+        NDIS_DbgPrint(MID_TRACE, ("Waiting for miniport to become free.\n"));
+        NdisWorkItem = ExAllocatePool(NonPagedPool, sizeof(NDIS_WORK_ITEM));
+        if (!NdisWorkItem) return NDIS_STATUS_RESOURCES;
+        WorkQueueItem = (PWORK_QUEUE_ITEM)NdisWorkItem->WrapperReserved;
+        NdisWorkItem->Context = Adapter;
+        ExInitializeWorkItem(WorkQueueItem, MiniportWorker, NdisWorkItem);
+        ExQueueWorkItem(WorkQueueItem, CriticalWorkQueue);
+        return NDIS_STATUS_FAILURE;
+    }
+
+    MiniportWorkItem = Adapter->WorkQueueHead;
 
-    if (Item)
+    if (MiniportWorkItem)
     {
         /* safe due to adapter lock held */
-        Adapter->WorkQueueHead = (PNDIS_MINIPORT_WORK_ITEM)Item->Link.Next;
+        Adapter->WorkQueueHead = (PNDIS_MINIPORT_WORK_ITEM)MiniportWorkItem->Link.Next;
 
-        if (Item == Adapter->WorkQueueTail)
+        if (MiniportWorkItem == Adapter->WorkQueueTail)
             Adapter->WorkQueueTail = NULL;
 
-        *WorkItemType    = Item->WorkItemType;
-        *WorkItemContext = Item->WorkItemContext;
+        *WorkItemType    = MiniportWorkItem->WorkItemType;
+        *WorkItemContext = MiniportWorkItem->WorkItemContext;
+
+        ExFreePool(MiniportWorkItem);
 
-        ExFreePool(Item);
+        Adapter->MiniportBusy = TRUE;
 
         return NDIS_STATUS_SUCCESS;
     }
@@ -695,7 +776,7 @@ MiniDequeueWorkItem(
 \f
 NDIS_STATUS
 MiniDoRequest(
-    PNDIS_MINIPORT_BLOCK Adapter,
+    PLOGICAL_ADAPTER Adapter,
     PNDIS_REQUEST NdisRequest)
 /*
  * FUNCTION: Sends a request to a miniport
@@ -706,15 +787,18 @@ MiniDoRequest(
  *     Status of operation
  */
 {
+    NDIS_STATUS Status;
+    KIRQL OldIrql;
     NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-    Adapter->MediaRequest = NdisRequest;
+    Adapter->NdisMiniportBlock.MediaRequest = NdisRequest;
 
+    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
     switch (NdisRequest->RequestType)
     {
     case NdisRequestQueryInformation:
-        return (*Adapter->DriverHandle->MiniportCharacteristics.QueryInformationHandler)(
-            Adapter->MiniportAdapterContext,
+        Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.QueryInformationHandler)(
+            Adapter->NdisMiniportBlock.MiniportAdapterContext,
             NdisRequest->DATA.QUERY_INFORMATION.Oid,
             NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer,
             NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
@@ -723,8 +807,8 @@ MiniDoRequest(
         break;
 
     case NdisRequestSetInformation:
-        return (*Adapter->DriverHandle->MiniportCharacteristics.SetInformationHandler)(
-            Adapter->MiniportAdapterContext,
+        Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SetInformationHandler)(
+            Adapter->NdisMiniportBlock.MiniportAdapterContext,
             NdisRequest->DATA.SET_INFORMATION.Oid,
             NdisRequest->DATA.SET_INFORMATION.InformationBuffer,
             NdisRequest->DATA.SET_INFORMATION.InformationBufferLength,
@@ -733,8 +817,17 @@ MiniDoRequest(
         break;
 
     default:
-        return NDIS_STATUS_FAILURE;
+        Status = NDIS_STATUS_FAILURE;
     }
+
+    if (Status == NDIS_STATUS_PENDING) {
+        KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+        Adapter->MiniportBusy = TRUE;
+        KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    }
+
+    KeLowerIrql(OldIrql);
+    return Status;
 }
 
 \f
@@ -748,39 +841,37 @@ NdisMQueryInformationComplete(
     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;
+    KIRQL OldIrql;
+    ASSERT(Adapter);
+    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+    if( Adapter->NdisMiniportBlock.QueryCompleteHandler )
+       (Adapter->NdisMiniportBlock.QueryCompleteHandler)(MiniportAdapterHandle, Status);
+    KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    Adapter->MiniportBusy = FALSE;
+    KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+    KeLowerIrql(OldIrql);
 }
 
-\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
- */
+VOID NTAPI MiniportWorker(IN PVOID WorkItem)
 {
+  PNDIS_WORK_ITEM NdisWorkItem = WorkItem;
+  PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(NdisWorkItem->Context);
+  KIRQL OldIrql, RaiseOldIrql;
   NDIS_STATUS NdisStatus;
   PVOID WorkItemContext;
   NDIS_WORK_ITEM_TYPE WorkItemType;
-  PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(DeferredContext);
+  BOOLEAN AddressingReset;
 
-  NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
 
   NdisStatus =
       MiniDequeueWorkItem
       (Adapter, &WorkItemType, &WorkItemContext);
 
+  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+
   if (NdisStatus == NDIS_STATUS_SUCCESS)
     {
       switch (WorkItemType)
@@ -794,32 +885,63 @@ VOID NTAPI MiniportDpc(
 #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_GET_PACKET_STATUS((PNDIS_PACKET)WorkItemContext);
+                }
+                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 ) {
+                        KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+                        MiniQueueWorkItem(Adapter, WorkItemType, WorkItemContext);
+                        KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+                        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, 0);
+                  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, 0);
+                  NDIS_DbgPrint(MAX_TRACE, ("back from miniport's send handler\n"));
+                  KeLowerIrql(RaiseOldIrql);
+                  if( NdisStatus == NDIS_STATUS_RESOURCES ) {
+                      KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+                      MiniQueueWorkItem(Adapter, WorkItemType, WorkItemContext);
+                      KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+                      break;
+                  }
+                }
               }
+
            if( NdisStatus != NDIS_STATUS_PENDING ) {
                NdisMSendComplete
                    ( Adapter, (PNDIS_PACKET)WorkItemContext, NdisStatus );
-               Adapter->MiniportBusy = FALSE;
            }
             break;
 
@@ -829,13 +951,28 @@ 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 )
+                MiniSendComplete((NDIS_HANDLE)Adapter, (PNDIS_PACKET)WorkItemContext, NdisStatus);
             break;
 
           case NdisWorkItemReturnPackets:
             break;
 
           case NdisWorkItemResetRequested:
+            NdisMIndicateStatus(Adapter, NDIS_STATUS_RESET_START, NULL, 0);
+            NdisMIndicateStatusComplete(Adapter);
+
+            KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+            NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.ResetHandler)(
+                          Adapter->NdisMiniportBlock.MiniportAdapterContext,
+                          &AddressingReset);
+            KeLowerIrql(OldIrql);
+
+            if (NdisStatus == NDIS_STATUS_PENDING)
+                break;
+
+            MiniResetComplete(Adapter, NdisStatus, AddressingReset);
             break;
 
           case NdisWorkItemResetInProgress:
@@ -845,7 +982,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;
@@ -854,12 +991,12 @@ VOID NTAPI MiniportDpc(
               {
                 case NdisRequestQueryInformation:
                  NdisMQueryInformationComplete((NDIS_HANDLE)Adapter, NdisStatus);
-                  MiniRequestComplete( &Adapter->NdisMiniportBlock, (PNDIS_REQUEST)WorkItemContext, NdisStatus );
+                  MiniRequestComplete( (NDIS_HANDLE)Adapter, (PNDIS_REQUEST)WorkItemContext, NdisStatus );
                   break;
 
                 case NdisRequestSetInformation:
                   NdisMSetInformationComplete((NDIS_HANDLE)Adapter, NdisStatus);
-                  MiniRequestComplete( &Adapter->NdisMiniportBlock, (PNDIS_REQUEST)WorkItemContext, NdisStatus );
+                  MiniRequestComplete( (NDIS_HANDLE)Adapter, (PNDIS_REQUEST)WorkItemContext, NdisStatus );
                   break;
 
                 default:
@@ -873,8 +1010,17 @@ VOID NTAPI MiniportDpc(
             break;
         }
     }
+
+  if( NdisStatus != NDIS_STATUS_PENDING ) {
+      KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
+      Adapter->MiniportBusy = FALSE;
+      KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+  }
+
+  ExFreePool(WorkItem);
 }
 
+
 \f
 VOID
 NTAPI
@@ -884,7 +1030,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
@@ -893,7 +1061,26 @@ 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
@@ -941,8 +1128,10 @@ NdisMDeregisterAdapterShutdownHandler(
 
   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
 
-  if(Adapter->BugcheckContext->ShutdownHandler)
+  if(Adapter->BugcheckContext->ShutdownHandler) {
     KeDeregisterBugCheckCallback(Adapter->BugcheckContext->CallbackRecord);
+    IoUnregisterShutdownNotification(Adapter->NdisMiniportBlock.DeviceObject);
+  }
 }
 
 \f
@@ -958,7 +1147,7 @@ NdisMFlushLog(
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 #undef NdisMIndicateStatus
 VOID
@@ -969,11 +1158,11 @@ NdisMIndicateStatus(
     IN  PVOID       StatusBuffer,
     IN  UINT        StatusBufferSize)
 {
-    UNIMPLEMENTED
+    MiniStatus(MiniportAdapterHandle, GeneralStatus, StatusBuffer, StatusBufferSize);
 }
 
 /*
- * @unimplemented
+ * @implemented
  */
 #undef NdisMIndicateStatusComplete
 VOID
@@ -981,7 +1170,7 @@ EXPORT
 NdisMIndicateStatusComplete(
     IN  NDIS_HANDLE MiniportAdapterHandle)
 {
-    UNIMPLEMENTED
+    MiniStatusComplete(MiniportAdapterHandle);
 }
 
 \f
@@ -1038,6 +1227,7 @@ NdisInitializeWrapper(
   RegistryPath = ExAllocatePool(PagedPool, sizeof(UNICODE_STRING));
   if(!RegistryPath)
     {
+      ExFreePool(Miniport);
       NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
       return;
     }
@@ -1049,6 +1239,8 @@ NdisInitializeWrapper(
   if(!RegistryBuffer)
     {
       NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      ExFreePool(Miniport);
+      ExFreePool(RegistryPath);
       return;
     }
 
@@ -1104,17 +1296,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)
     {
@@ -1125,11 +1313,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");
+
+  IoRegisterShutdownNotification(Adapter->NdisMiniportBlock.DeviceObject);
 }
 
 \f
@@ -1206,6 +1403,17 @@ DoQueries(
       return NdisStatus;
     }
 
+  NdisStatus = MiniQueryInformation(Adapter, OID_GEN_MAXIMUM_SEND_PACKETS, sizeof(ULONG),
+                                    &Adapter->NdisMiniportBlock.MaxSendPackets, &BytesWritten);
+
+  if (NdisStatus != NDIS_STATUS_SUCCESS)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("OID_GEN_MAXIMUM_SEND_PACKETS failed. NdisStatus (0x%X).\n", NdisStatus));
+
+      /* 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));
 
   if (Adapter->NdisMiniportBlock.MaximumLookahead != 0)
@@ -1287,7 +1495,8 @@ NdisIPnPStartDevice(
   PNDIS_CONFIGURATION_PARAMETER ConfigParam;
   NDIS_HANDLE ConfigHandle;
   ULONG Size;
-/* FIXME - KIRQL OldIrql; */
+  LARGE_INTEGER Timeout;
+  /* FIXME - KIRQL OldIrql; */
 
   /*
    * Prepare wrapper context used by HW and configuration routines.
@@ -1306,6 +1515,7 @@ NdisIPnPStartDevice(
   if (!NT_SUCCESS(Status))
     {
       NDIS_DbgPrint(MIN_TRACE,("failed to open adapter-specific reg key\n"));
+      ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
       return Status;
     }
 
@@ -1318,8 +1528,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;
@@ -1335,24 +1544,31 @@ NdisIPnPStartDevice(
           return STATUS_INSUFFICIENT_RESOURCES;
         }
 
+      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)
         {
-          ExFreePool(Adapter->NdisMiniportBlock.AllocatedResources);
-          Adapter->NdisMiniportBlock.AllocatedResources = NULL;
          ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
           return STATUS_INSUFFICIENT_RESOURCES;
         }
 
-      RtlCopyMemory(Adapter->NdisMiniportBlock.AllocatedResources,
-                    Stack->Parameters.StartDevice.AllocatedResources,
-                    ResourceListSize);
-
       RtlCopyMemory(Adapter->NdisMiniportBlock.AllocatedResourcesTranslated,
                     Stack->Parameters.StartDevice.AllocatedResourcesTranslated,
                     ResourceListSize);
-    }
+   }
 
   /*
    * Store the Bus Type, Bus Number and Slot information. It's used by
@@ -1418,6 +1634,17 @@ NdisIPnPStartDevice(
     }
   NdisCloseConfiguration(ConfigHandle);
 
+  /* Set handlers (some NDIS macros require these) */
+  Adapter->NdisMiniportBlock.EthRxCompleteHandler = EthFilterDprIndicateReceiveComplete;
+  Adapter->NdisMiniportBlock.EthRxIndicateHandler = EthFilterDprIndicateReceive;
+  Adapter->NdisMiniportBlock.SendCompleteHandler  = MiniSendComplete;
+  Adapter->NdisMiniportBlock.SendResourcesHandler = MiniSendResourcesAvailable;
+  Adapter->NdisMiniportBlock.ResetCompleteHandler = MiniResetComplete;
+  Adapter->NdisMiniportBlock.TDCompleteHandler    = MiniTransferDataComplete;
+  Adapter->NdisMiniportBlock.PacketIndicateHandler= MiniIndicateReceivePacket;
+  Adapter->NdisMiniportBlock.StatusHandler        = MiniStatus;
+  Adapter->NdisMiniportBlock.StatusCompleteHandler= MiniStatusComplete;
+
   /*
    * Call MiniportInitialize.
    */
@@ -1429,25 +1656,19 @@ 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 );
-      return (NTSTATUS)NdisStatus;
+      return NdisStatus;
     }
 
-  /* Set handlers (some NDIS macros require these) */
-
-  Adapter->NdisMiniportBlock.EthRxCompleteHandler = EthFilterDprIndicateReceiveComplete;
-  Adapter->NdisMiniportBlock.EthRxIndicateHandler = EthFilterDprIndicateReceive;
-  Adapter->NdisMiniportBlock.SendCompleteHandler  = MiniSendComplete;
-  Adapter->NdisMiniportBlock.SendResourcesHandler = MiniSendResourcesAvailable;
-  Adapter->NdisMiniportBlock.ResetCompleteHandler = MiniResetComplete;
-  Adapter->NdisMiniportBlock.TDCompleteHandler    = MiniTransferDataComplete;
-  Adapter->NdisMiniportBlock.PacketIndicateHandler= MiniIndicateReceivePacket;
-  Adapter->NdisMiniportBlock.StatusHandler        = MiniStatus;
-  Adapter->NdisMiniportBlock.StatusCompleteHandler= MiniStatusComplete;
+  if (SelectedMediumIndex >= MEDIA_ARRAY_SIZE)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("MiniportInitialize() failed for an adapter\n"));
+      ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
+      return NDIS_STATUS_UNSUPPORTED_MEDIA;
+    }
 
   Adapter->NdisMiniportBlock.MediaType = MediaArray[SelectedMediumIndex];
 
@@ -1479,7 +1700,7 @@ NdisIPnPStartDevice(
         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)
@@ -1488,12 +1709,21 @@ NdisIPnPStartDevice(
           Adapter->LookaheadBuffer = NULL;
         }
       ExInterlockedRemoveEntryList( &Adapter->ListEntry, &AdapterListLock );
-      return (NTSTATUS)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;
 
+  Timeout.QuadPart = (LONGLONG)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);
 
@@ -1528,6 +1758,8 @@ NdisIPnPStopDevice(
   RemoveEntryList(&Adapter->ListEntry);
   KeReleaseSpinLock(&AdapterListLock, OldIrql);
 
+  KeCancelTimer(&Adapter->NdisMiniportBlock.WakeUpDpcTimer.Timer);
+
   (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.HaltHandler)(Adapter);
 
   if (Adapter->LookaheadBuffer)
@@ -1552,6 +1784,28 @@ NdisIPnPStopDevice(
   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;
+}
+
 \f
 NTSTATUS
 NTAPI
@@ -1647,7 +1901,7 @@ NdisIAddDevice(
 
   Status = IoGetDeviceProperty(PhysicalDeviceObject, DevicePropertyDriverKeyName,
                                0, NULL, &DriverKeyLength);
-  if (Status != STATUS_BUFFER_TOO_SMALL)
+  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"));
       return Status;
@@ -1735,7 +1989,8 @@ NdisIAddDevice(
   Adapter->NdisMiniportBlock.OldPnPDeviceState = 0;
   Adapter->NdisMiniportBlock.PnPDeviceState = NdisPnPDeviceAdded;
 
-  KeInitializeDpc(&Adapter->NdisMiniportBlock.DeferredDpc, MiniportDpc, (PVOID)Adapter);
+  KeInitializeTimer(&Adapter->NdisMiniportBlock.WakeUpDpcTimer.Timer);
+  KeInitializeDpc(&Adapter->NdisMiniportBlock.WakeUpDpcTimer.Dpc, MiniportHangDpc, Adapter);
 
   DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
 
@@ -1844,6 +2099,7 @@ NdisMRegisterMiniport(
   *MiniportPtr = Miniport;
 
   Miniport->DriverObject->MajorFunction[IRP_MJ_PNP] = NdisIDispatchPnp;
+  Miniport->DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = NdisIShutdown;
   Miniport->DriverObject->DriverExtension->AddDevice = NdisIAddDevice;
 
   return NDIS_STATUS_SUCCESS;
@@ -1927,7 +2183,16 @@ NdisMSetInformationComplete(
     IN  NDIS_HANDLE MiniportAdapterHandle,
     IN  NDIS_STATUS Status)
 {
-  (*((PNDIS_MINIPORT_BLOCK)(MiniportAdapterHandle))->SetCompleteHandler)(MiniportAdapterHandle, Status);
+  PLOGICAL_ADAPTER Adapter =
+       (PLOGICAL_ADAPTER)MiniportAdapterHandle;
+  KIRQL OldIrql;
+  ASSERT(Adapter);
+  KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+  (Adapter->NdisMiniportBlock.SetCompleteHandler)(MiniportAdapterHandle, Status);
+  KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+  Adapter->MiniportBusy = FALSE;
+  KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
+  KeLowerIrql(OldIrql);
 }
 
 \f
@@ -1980,8 +2245,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"));
@@ -1989,6 +2252,8 @@ 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"));
 }