[NDIS]
[reactos.git] / reactos / drivers / network / ndis / ndis / protocol.c
index 0515ed9..3817391 100644 (file)
 LIST_ENTRY ProtocolListHead;
 KSPIN_LOCK ProtocolListLock;
 
+#define WORKER_TEST 0
+
+typedef struct _DMA_CONTEXT {
+    PLOGICAL_ADAPTER Adapter;
+    PNDIS_PACKET Packet;
+} DMA_CONTEXT, *PDMA_CONTEXT;
+
+PNET_PNP_EVENT
+ProSetupPnPEvent(
+    NET_PNP_EVENT_CODE EventCode,
+    PVOID              EventBuffer,
+    ULONG              EventBufferLength)
+{
+    PNET_PNP_EVENT PnPEvent;
+
+    PnPEvent = ExAllocatePool(PagedPool, sizeof(NET_PNP_EVENT));
+    if (!PnPEvent) {
+        NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+        return NULL;
+    }
+
+    RtlZeroMemory(PnPEvent, sizeof(NET_PNP_EVENT));
+
+    PnPEvent->NetEvent = EventCode;
+
+    if (EventBuffer != NULL)
+    {
+        PnPEvent->Buffer = ExAllocatePool(PagedPool, EventBufferLength);
+        if (!PnPEvent->Buffer)
+        {
+            NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+            ExFreePool(PnPEvent);
+            return NULL;
+        }
+
+        PnPEvent->BufferLength = EventBufferLength;
+
+        RtlCopyMemory(PnPEvent->Buffer, EventBuffer, PnPEvent->BufferLength);
+    }
+
+    return PnPEvent;
+}
+
+NDIS_STATUS
+ProSendAndFreePnPEvent(
+   PLOGICAL_ADAPTER Adapter,
+   PNET_PNP_EVENT   PnPEvent,
+   PIRP             Irp)
+{
+  PLIST_ENTRY CurrentEntry;
+  NDIS_STATUS Status;
+  PADAPTER_BINDING AdapterBinding;
+
+  CurrentEntry = Adapter->ProtocolListHead.Flink;
+
+  while (CurrentEntry != &Adapter->ProtocolListHead)
+  {
+     AdapterBinding = CONTAINING_RECORD(CurrentEntry, ADAPTER_BINDING, AdapterListEntry);
+
+     Status = (*AdapterBinding->ProtocolBinding->Chars.PnPEventHandler)(
+      AdapterBinding->NdisOpenBlock.ProtocolBindingContext,
+      PnPEvent);
+
+     if (Status == NDIS_STATUS_PENDING)
+     {
+         IoMarkIrpPending(Irp);
+         /* Yes, I know this is stupid */
+         PnPEvent->NdisReserved[0] = (ULONG_PTR)Irp;
+         PnPEvent->NdisReserved[1] = (ULONG_PTR)CurrentEntry->Flink;
+         return NDIS_STATUS_PENDING;
+     }
+     else if (Status != NDIS_STATUS_SUCCESS)
+     {
+         if (PnPEvent->Buffer) ExFreePool(PnPEvent->Buffer);
+         ExFreePool(PnPEvent);
+         return Status;
+     }
+
+     CurrentEntry = CurrentEntry->Flink;
+  }
+
+  if (PnPEvent->Buffer) ExFreePool(PnPEvent->Buffer);
+  ExFreePool(PnPEvent);
+
+  return NDIS_STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+NdisIPwrSetPower(
+    IN PDEVICE_OBJECT DeviceObject,
+    PIRP Irp)
+{
+  PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)DeviceObject->DeviceExtension;
+  PNET_PNP_EVENT PnPEvent;
+  PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+
+  ASSERT(Stack->Parameters.Power.Type == DevicePowerState);
+
+  PnPEvent = ProSetupPnPEvent(NetEventSetPower, &Stack->Parameters.Power.State, sizeof(NDIS_DEVICE_POWER_STATE));
+  if (!PnPEvent) {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+      return NDIS_STATUS_RESOURCES;
+  }
+
+  return ProSendAndFreePnPEvent(Adapter, PnPEvent, Irp);
+}
+
+NTSTATUS
+NTAPI
+NdisIPwrQueryPower(
+    IN PDEVICE_OBJECT DeviceObject,
+    PIRP Irp)
+{
+  PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)DeviceObject->DeviceExtension;
+  PNET_PNP_EVENT PnPEvent;
+  PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
+
+  ASSERT(Stack->Parameters.Power.Type == DevicePowerState);
+
+  PnPEvent = ProSetupPnPEvent(NetEventQueryPower, &Stack->Parameters.Power.State, sizeof(NDIS_DEVICE_POWER_STATE));
+  if (!PnPEvent) {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+      return NDIS_STATUS_RESOURCES;
+  }
+
+  return ProSendAndFreePnPEvent(Adapter, PnPEvent, Irp);
+}
+
+
+NTSTATUS
+NTAPI
+NdisIPnPQueryStopDevice(
+    IN PDEVICE_OBJECT DeviceObject,
+    PIRP Irp)
+{
+  PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)DeviceObject->DeviceExtension;
+  PNET_PNP_EVENT PnPEvent;
+
+  PnPEvent = ProSetupPnPEvent(NetEventQueryRemoveDevice, NULL, 0);
+  if (!PnPEvent) {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+      return NDIS_STATUS_RESOURCES;
+  }
+
+  return ProSendAndFreePnPEvent(Adapter, PnPEvent, Irp);
+}
+
+NTSTATUS
+NTAPI
+NdisIPnPCancelStopDevice(
+    IN PDEVICE_OBJECT DeviceObject,
+    PIRP Irp)
+{
+  PLOGICAL_ADAPTER Adapter = (PLOGICAL_ADAPTER)DeviceObject->DeviceExtension;
+  PNET_PNP_EVENT PnPEvent;
+
+  PnPEvent = ProSetupPnPEvent(NetEventCancelRemoveDevice, NULL, 0);
+  if (!PnPEvent) {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+      return NDIS_STATUS_RESOURCES;
+  }
+
+  return ProSendAndFreePnPEvent(Adapter, PnPEvent, Irp);
+}
+
 \f
 /*
  * @implemented
@@ -43,10 +209,38 @@ NdisCompleteBindAdapter(
 {
   PROTOCOL_BINDING *Protocol = (PROTOCOL_BINDING *)BindAdapterContext;
 
+  if (!NT_SUCCESS(Status)) {
+      NDIS_DbgPrint(MIN_TRACE, ("Binding failed (%x)\n", Status));
+      return;
+  }
+
   /* Put protocol binding struct on global list */
   ExInterlockedInsertTailList(&ProtocolListHead, &Protocol->ListEntry, &ProtocolListLock);
 }
 
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisCompleteUnbindAdapter(
+    IN  NDIS_HANDLE UnbindAdapterContext,
+    IN  NDIS_STATUS Status)
+{
+  /* We probably need to do more here but for now we just do
+   * the opposite of what NdisCompleteBindAdapter does
+   */
+
+  PROTOCOL_BINDING *Protocol = (PROTOCOL_BINDING *)UnbindAdapterContext;
+
+  if (!NT_SUCCESS(Status)) {
+      NDIS_DbgPrint(MIN_TRACE, ("Unbinding failed (%x)\n", Status));
+      return;
+  }
+
+  ExInterlockedRemoveEntryList(&Protocol->ListEntry, &ProtocolListLock);
+}
+
 \f
 NDIS_STATUS
 ProIndicatePacket(
@@ -63,46 +257,55 @@ ProIndicatePacket(
  *     - XXX ATM, this only handles loopback packets - is that its designed function?
  */
 {
-  KIRQL OldIrql;
   UINT BufferedLength;
   UINT PacketLength;
+  KIRQL OldIrql;
+  PUCHAR LookaheadBuffer;
 
   NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
-#ifdef DBG
+#if DBG
   MiniDisplayPacket(Packet);
 #endif
 
+  LookaheadBuffer = ExAllocatePool(NonPagedPool, Adapter->NdisMiniportBlock.CurrentLookahead + Adapter->MediumHeaderSize);
+  if (!LookaheadBuffer) {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+      return NDIS_STATUS_RESOURCES;
+  }
+
   NdisQueryPacket(Packet, NULL, NULL, NULL, &PacketLength);
 
   NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
   KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
     {
+      BufferedLength = CopyPacketToBuffer(LookaheadBuffer, Packet, 0, Adapter->NdisMiniportBlock.CurrentLookahead +
+                                                                      Adapter->MediumHeaderSize);
       Adapter->NdisMiniportBlock.IndicatedPacket[KeGetCurrentProcessorNumber()] = Packet;
-      BufferedLength = CopyPacketToBuffer(Adapter->LookaheadBuffer, Packet, 0, Adapter->NdisMiniportBlock.CurrentLookahead);
     }
   KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 
   if (BufferedLength > Adapter->MediumHeaderSize)
     {
       /* XXX Change this to call SendPackets so we don't have to duplicate this wacky logic */
-      MiniIndicateData(Adapter, NULL, Adapter->LookaheadBuffer, Adapter->MediumHeaderSize,
-          &Adapter->LookaheadBuffer[Adapter->MediumHeaderSize], BufferedLength - Adapter->MediumHeaderSize,
+      MiniIndicateData(Adapter, NULL, LookaheadBuffer, Adapter->MediumHeaderSize,
+          &LookaheadBuffer[Adapter->MediumHeaderSize], BufferedLength - Adapter->MediumHeaderSize,
           PacketLength - Adapter->MediumHeaderSize);
     }
   else
     {
-      MiniIndicateData(Adapter, NULL, Adapter->LookaheadBuffer, Adapter->MediumHeaderSize, NULL, 0, 0);
+      MiniIndicateData(Adapter, NULL, LookaheadBuffer, Adapter->MediumHeaderSize, NULL, 0, 0);
     }
 
-  NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
+  ExFreePool(LookaheadBuffer);
+
   KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
     {
       Adapter->NdisMiniportBlock.IndicatedPacket[KeGetCurrentProcessorNumber()] = NULL;
     }
   KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
 
-  return STATUS_SUCCESS;
+  return NDIS_STATUS_SUCCESS;
 }
 
 \f
@@ -119,9 +322,6 @@ ProRequest(
  *     Status of operation
  */
 {
-  KIRQL OldIrql;
-  BOOLEAN QueueWorkItem = FALSE;
-  NDIS_STATUS NdisStatus;
   PADAPTER_BINDING AdapterBinding;
   PLOGICAL_ADAPTER Adapter;
   PNDIS_REQUEST_MAC_BLOCK MacBlock = (PNDIS_REQUEST_MAC_BLOCK)NdisRequest->MacReserved;
@@ -136,61 +336,126 @@ ProRequest(
 
   MacBlock->Binding = &AdapterBinding->NdisOpenBlock;
 
-  /*
-   * If the miniport is already busy, queue a workitem
-   */
-  NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
-  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &OldIrql);
-    {
-      if(Adapter->MiniportBusy)
-        QueueWorkItem = TRUE;
-      else
-        {
-          NDIS_DbgPrint(MAX_TRACE, ("Setting adapter 0x%x to busy\n"));
-          Adapter->MiniportBusy = TRUE;
-        }
-    }
-
-  /* MiniQueueWorkItem must be called at IRQL >= DISPATCH_LEVEL */
-  if (QueueWorkItem)
-    {
-      MiniQueueWorkItem(Adapter, NdisWorkItemRequest, (PVOID)NdisRequest);
-      KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+#if WORKER_TEST
+  MiniQueueWorkItem(Adapter, NdisWorkItemRequest, NdisRequest, FALSE);
+  return NDIS_STATUS_PENDING;
+#else
+  if (MiniIsBusy(Adapter, NdisWorkItemRequest)) {
+      MiniQueueWorkItem(Adapter, NdisWorkItemRequest, NdisRequest, FALSE);
       return NDIS_STATUS_PENDING;
-    }
+  }
 
-  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, OldIrql);
+  return MiniDoRequest(Adapter, NdisRequest);
+#endif
+}
 
-  /* MiniportQueryInformation (called by MiniDoRequest) runs at DISPATCH_LEVEL */
-  /* TODO (?): move the irql raise into MiniDoRequest */
-  KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
-    {
-      NdisStatus = MiniDoRequest(&Adapter->NdisMiniportBlock, NdisRequest);
 
-      NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
-      KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
-        {
-          NDIS_DbgPrint(MAX_TRACE, ("Setting adapter 0x%x to free\n"));
-          Adapter->MiniportBusy = FALSE;
+NDIS_STATUS NTAPI
+ProReset(
+    IN  NDIS_HANDLE MacBindingHandle)
+{
+    PADAPTER_BINDING AdapterBinding = MacBindingHandle;
 
-          if (Adapter->WorkQueueHead)
-            KeInsertQueueDpc(&Adapter->NdisMiniportBlock.DeferredDpc, NULL, NULL);
-        }
-      KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
-    }
-  KeLowerIrql(OldIrql);
+    /* FIXME: Wait for all packets to be sent */
 
-  return NdisStatus;
+    return MiniReset(AdapterBinding->Adapter);
 }
 
-\f
-NDIS_STATUS NTAPI
-ProReset(
-    IN  NDIS_HANDLE MacBindingHandle)
+VOID NTAPI
+ScatterGatherSendPacket(
+   IN PDEVICE_OBJECT DeviceObject,
+   IN PIRP Irp,
+   IN PSCATTER_GATHER_LIST ScatterGather,
+   IN PVOID Context)
 {
-    UNIMPLEMENTED
+   PDMA_CONTEXT DmaContext = Context;
+   PLOGICAL_ADAPTER Adapter = DmaContext->Adapter;
+   PNDIS_PACKET Packet = DmaContext->Packet;
+   NDIS_STATUS Status;
+
+   NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
-    return NDIS_STATUS_FAILURE;
+   NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
+                                    ScatterGatherListPacketInfo) = ScatterGather;
+
+   Status = proSendPacketToMiniport(Adapter, Packet);
+
+   if (Status != NDIS_STATUS_PENDING) {
+       NDIS_DbgPrint(MAX_TRACE, ("Completing packet.\n"));
+       MiniSendComplete(Adapter,
+                        Packet,
+                        Status);
+   }
+
+   ExFreePool(DmaContext);
+}
+
+NDIS_STATUS
+proSendPacketToMiniport(PLOGICAL_ADAPTER Adapter, PNDIS_PACKET Packet)
+{
+#if WORKER_TEST
+   MiniQueueWorkItem(Adapter, NdisWorkItemSend, Packet, FALSE);
+   return NDIS_STATUS_PENDING;
+#else
+   KIRQL RaiseOldIrql;
+   NDIS_STATUS NdisStatus;
+
+   if(MiniIsBusy(Adapter, NdisWorkItemSend)) {
+      MiniQueueWorkItem(Adapter, NdisWorkItemSend, Packet, FALSE);
+      return NDIS_STATUS_PENDING;
+   }
+
+   if(Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)
+   {
+        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, &Packet, 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, &Packet, 1);
+            }
+            KeLowerIrql(RaiseOldIrql);
+
+            NdisStatus = NDIS_GET_PACKET_STATUS(Packet);
+            if (NdisStatus == NDIS_STATUS_RESOURCES) {
+                MiniQueueWorkItem(Adapter, NdisWorkItemSend, Packet, TRUE);
+                NdisStatus = NDIS_STATUS_PENDING;
+            }
+        }
+
+        return NdisStatus;
+   } else {
+        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, Packet, Packet->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, Packet, Packet->Private.Flags);
+            NDIS_DbgPrint(MAX_TRACE, ("back from miniport's send handler\n"));
+            KeLowerIrql(RaiseOldIrql);
+
+            if (NdisStatus == NDIS_STATUS_RESOURCES) {
+                MiniQueueWorkItem(Adapter, NdisWorkItemSend, Packet, TRUE);
+                NdisStatus = NDIS_STATUS_PENDING;
+            }
+        }
+
+        return NdisStatus;
+   }
+#endif
 }
 
 \f
@@ -204,21 +469,17 @@ ProSend(
  *     MacBindingHandle = Adapter binding handle
  *     Packet           = Pointer to NDIS packet descriptor
  * RETURNS:
- *     NDIS_STATUS_SUCCESS always
- * NOTES:
- * TODO:
- *     - Fix return values
- *     - Should queue packet if miniport returns NDIS_STATUS_RESOURCES
- *     - Queue packets directly on the adapters when possible (i.e.
- *       when miniports not busy)
- *     - Break this up
+ *     NDIS_STATUS_SUCCESS if the packet was successfully sent
+ *     NDIS_STATUS_PENDING if the miniport was busy or a serialized miniport returned NDIS_STATUS_RESOURCES
  */
 {
-  KIRQL RaiseOldIrql, SpinOldIrql;
-  BOOLEAN QueueWorkItem = FALSE;
-  NDIS_STATUS NdisStatus;
   PADAPTER_BINDING AdapterBinding;
   PLOGICAL_ADAPTER Adapter;
+  PNDIS_BUFFER NdisBuffer;
+  PDMA_CONTEXT Context;
+  NDIS_STATUS NdisStatus;
+  UINT PacketLength;
+  KIRQL OldIrql;
 
   NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
@@ -234,31 +495,7 @@ ProSend(
   ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
 
   /* XXX what is this crazy black magic? */
-  Packet->Reserved[0] = (ULONG_PTR)MacBindingHandle;
-
-  /*
-   * Acquire this lock in order to see if the miniport is busy.
-   * If it is not busy, we mark it as busy and release the lock.
-   * Else we don't do anything because we have to queue a workitem
-   * anyway.
-   */
-  NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
-  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &SpinOldIrql);
-    {
-      /*
-       * if the miniport is marked as busy, we queue the packet as a work item,
-       * else we send the packet directly to the miniport.  Sending to the miniport
-       * makes it busy.
-       */
-      if (Adapter->MiniportBusy)
-        QueueWorkItem = TRUE;
-      else
-        {
-          NDIS_DbgPrint(MAX_TRACE, ("Setting adapter 0x%x to busy\n"));
-          Adapter->MiniportBusy = TRUE;
-        }
-    }
-  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, SpinOldIrql);
+  Packet->Reserved[1] = (ULONG_PTR)MacBindingHandle;
 
   /*
    * Test the packet to see if it is a MAC loopback.
@@ -267,127 +504,63 @@ ProSend(
    * If dest MAC address of packet == MAC address of adapter,
    * this is a loopback frame.
    */
+
   if ((Adapter->NdisMiniportBlock.MacOptions & NDIS_MAC_OPTION_NO_LOOPBACK) &&
       MiniAdapterHasAddress(Adapter, Packet))
     {
-      NDIS_DbgPrint(MIN_TRACE, ("Looping packet.\n"));
-
-      if (QueueWorkItem)
+#if WORKER_TEST
+        MiniQueueWorkItem(Adapter, NdisWorkItemSendLoopback, Packet, FALSE);
+        return NDIS_STATUS_PENDING;
+#else
+        return ProIndicatePacket(Adapter, Packet);
+#endif
+    } else {
+        if (Adapter->NdisMiniportBlock.ScatterGatherListSize != 0)
         {
-          MiniQueueWorkItem(Adapter, NdisWorkItemSendLoopback, (PVOID)Packet);
-          return NDIS_STATUS_PENDING;
-        }
+            NDIS_DbgPrint(MIN_TRACE, ("Using Scatter/Gather DMA\n"));
+
+            NdisQueryPacket(Packet,
+                            NULL,
+                            NULL,
+                            &NdisBuffer,
+                            &PacketLength);
+
+            Context = ExAllocatePool(NonPagedPool, sizeof(DMA_CONTEXT));
+            if (!Context) {
+                NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources\n"));
+                return NDIS_STATUS_RESOURCES;
+            }
 
-      KeRaiseIrql(DISPATCH_LEVEL, &RaiseOldIrql);
-        {
-          /*
-           * atm this *only* handles loopback packets - it calls MiniIndicateData to
-           * send back to the protocol.  FIXME: this will need to be adjusted a bit.
-           * Also, I'm not sure you really have to be at dispatch level for this.  It
-           * might use a ReceivePackets handler, which can run <= DISPATCH_LEVEL.
-           */
-          NdisStatus = ProIndicatePacket(Adapter, Packet);
-
-          NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
-          KeAcquireSpinLockAtDpcLevel(&Adapter->NdisMiniportBlock.Lock);
-            {
-              NDIS_DbgPrint(MAX_TRACE, ("Setting adapter 0x%x to free\n"));
-              Adapter->MiniportBusy = FALSE;
+            Context->Adapter = Adapter;
+            Context->Packet = Packet;
 
-              if (Adapter->WorkQueueHead)
-                KeInsertQueueDpc(&Adapter->NdisMiniportBlock.DeferredDpc, NULL, NULL);
-              else
-                NDIS_DbgPrint(MID_TRACE,("Failed to insert packet into work queue\n"));
-            }
-          KeReleaseSpinLockFromDpcLevel(&Adapter->NdisMiniportBlock.Lock);
-        }
-      KeLowerIrql(RaiseOldIrql);
+            KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
 
-      return NdisStatus;
-    }
-  else
-    NDIS_DbgPrint(MID_TRACE,("Not a loopback packet\n"));
+            KeFlushIoBuffers(NdisBuffer, FALSE, TRUE);
 
-  /* This is a normal send packet, not a loopback packet. */
-  if (QueueWorkItem)
-    {
-      MiniQueueWorkItem(Adapter, NdisWorkItemSend, (PVOID)Packet);
-      NDIS_DbgPrint(MAX_TRACE, ("Queued a work item and returning\n"));
-      return NDIS_STATUS_PENDING;
-    }
+            NdisStatus = Adapter->NdisMiniportBlock.SystemAdapterObject->DmaOperations->GetScatterGatherList(
+                          Adapter->NdisMiniportBlock.SystemAdapterObject,
+                          Adapter->NdisMiniportBlock.PhysicalDeviceObject,
+                          NdisBuffer,
+                          MmGetMdlVirtualAddress(NdisBuffer),
+                          PacketLength,
+                          ScatterGatherSendPacket,
+                          Context,
+                          TRUE);
 
-  ASSERT(Adapter->NdisMiniportBlock.DriverHandle);
+            KeLowerIrql(OldIrql);
 
-  /*
-   * Call the appropriate send handler
-   *
-   * If a miniport provides a SendPackets handler, we always call it.  If not, we call the
-   * Send handler.
-   */
-  if(Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)
-    {
-      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, &Packet, 1);
-        }
-      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, &Packet, 1);
+            if (!NT_SUCCESS(NdisStatus)) {
+                NDIS_DbgPrint(MIN_TRACE, ("GetScatterGatherList failed! (%x)\n", NdisStatus));
+                return NdisStatus;
             }
-          KeLowerIrql(RaiseOldIrql);
-        }
 
-      /* SendPackets handlers return void - they always "succeed" */
-      NdisStatus = NDIS_STATUS_SUCCESS;
-    }
-  else
-    {
-      /* XXX FIXME THIS IS WRONG */
-      /* uh oh... forgot why i thought that... */
-      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, Packet, 0);
-          NDIS_DbgPrint(MAX_TRACE, ("back from miniport's send handler\n"));
+            return NDIS_STATUS_PENDING;
         }
-      else
-        {
-          /* Send handlers always run at DISPATCH_LEVEL so we raise here */
-          KeRaiseIrql(DISPATCH_LEVEL, &RaiseOldIrql);
 
-          NDIS_DbgPrint(MAX_TRACE, ("Calling miniport's Send handler\n"));
-          NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendHandler)(
-            Adapter->NdisMiniportBlock.MiniportAdapterContext, Packet, 0);
-          NDIS_DbgPrint(MAX_TRACE, ("back from miniport's send handler\n"));
-         if( NdisStatus != NDIS_STATUS_PENDING ) {
-             Adapter->MiniportBusy = FALSE;
-         }
-          KeLowerIrql(RaiseOldIrql);
-        }
-    }
 
-  /* XXX why the hell do we do this? */
-  NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
-  KeAcquireSpinLock(&Adapter->NdisMiniportBlock.Lock, &SpinOldIrql);
-    {
-      if (Adapter->WorkQueueHead)
-        {
-          KeInsertQueueDpc(&Adapter->NdisMiniportBlock.DeferredDpc, NULL, NULL);
-          NDIS_DbgPrint(MAX_TRACE, ("MiniportDpc queued; returning NDIS_STATUS_SUCCESS\n"));
-        }
+        return proSendPacketToMiniport(Adapter, Packet);
     }
-  KeReleaseSpinLock(&Adapter->NdisMiniportBlock.Lock, SpinOldIrql);
-
-  NDIS_DbgPrint(MAX_TRACE, ("returning 0x%x\n", NdisStatus));
-  return NdisStatus;
 }
 
 \f
@@ -397,7 +570,60 @@ ProSendPackets(
     IN  PPNDIS_PACKET   PacketArray,
     IN  UINT            NumberOfPackets)
 {
-    UNIMPLEMENTED
+    PADAPTER_BINDING AdapterBinding = NdisBindingHandle;
+    PLOGICAL_ADAPTER Adapter = AdapterBinding->Adapter;
+    KIRQL RaiseOldIrql;
+    NDIS_STATUS NdisStatus;
+    UINT i;
+
+    if(Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)
+    {
+       if(Adapter->NdisMiniportBlock.Flags & NDIS_ATTRIBUTE_DESERIALIZE)
+       {
+          (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)(
+           Adapter->NdisMiniportBlock.MiniportAdapterContext, PacketArray, NumberOfPackets);
+       }
+       else
+       {
+          /* SendPackets is called at DISPATCH_LEVEL for all serialized miniports */
+          KeRaiseIrql(DISPATCH_LEVEL, &RaiseOldIrql);
+          (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendPacketsHandler)(
+           Adapter->NdisMiniportBlock.MiniportAdapterContext, PacketArray, NumberOfPackets);
+          KeLowerIrql(RaiseOldIrql);
+          for (i = 0; i < NumberOfPackets; i++)
+          {
+             NdisStatus = NDIS_GET_PACKET_STATUS(PacketArray[i]);
+             if (NdisStatus != NDIS_STATUS_PENDING)
+                 MiniSendComplete(Adapter, PacketArray[i], NdisStatus);
+          }
+       }
+     }
+     else
+     {
+       if(Adapter->NdisMiniportBlock.Flags & NDIS_ATTRIBUTE_DESERIALIZE)
+       {  
+          for (i = 0; i < NumberOfPackets; i++)
+          {
+             NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendHandler)(
+                           Adapter->NdisMiniportBlock.MiniportAdapterContext, PacketArray[i], PacketArray[i]->Private.Flags);
+             if (NdisStatus != NDIS_STATUS_PENDING)
+                 MiniSendComplete(Adapter, PacketArray[i], NdisStatus);
+          }
+       }
+       else
+       {
+         /* Send is called at DISPATCH_LEVEL for all serialized miniports */
+         KeRaiseIrql(DISPATCH_LEVEL, &RaiseOldIrql);
+         for (i = 0; i < NumberOfPackets; i++)
+         {
+            NdisStatus = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.SendHandler)(
+                           Adapter->NdisMiniportBlock.MiniportAdapterContext, PacketArray[i], PacketArray[i]->Private.Flags);
+            if (NdisStatus != NDIS_STATUS_PENDING)
+                MiniSendComplete(Adapter, PacketArray[i], NdisStatus);
+         }
+         KeLowerIrql(RaiseOldIrql);
+       }
+     }
 }
 
 \f
@@ -422,31 +648,41 @@ ProTransferData(
 {
     PADAPTER_BINDING AdapterBinding = GET_ADAPTER_BINDING(MacBindingHandle);
     PLOGICAL_ADAPTER Adapter        = AdapterBinding->Adapter;
+    NDIS_STATUS Status;
+    KIRQL OldIrql;
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
     /* FIXME: Interrupts must be disabled for adapter */
     /* XXX sd - why is that true? */
 
-    if (Packet == Adapter->NdisMiniportBlock.IndicatedPacket[KeGetCurrentProcessorNumber()]) {
+    if (Adapter->NdisMiniportBlock.IndicatedPacket[KeGetCurrentProcessorNumber()]) {
        NDIS_DbgPrint(MAX_TRACE, ("LoopPacket\n"));
         /* NDIS is responsible for looping this packet */
         NdisCopyFromPacketToPacket(Packet,
-                                   ByteOffset,
-                                   BytesToTransfer,
+                                   ByteOffset + Adapter->MediumHeaderSize,
+                                   BytesToTransfer + Adapter->MediumHeaderSize,
                                    Adapter->NdisMiniportBlock.IndicatedPacket[KeGetCurrentProcessorNumber()],
                                    0,
                                    BytesTransferred);
         return NDIS_STATUS_SUCCESS;
     }
 
-    return (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.TransferDataHandler)(
+    ASSERT(Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.TransferDataHandler);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
+
+    Status = (*Adapter->NdisMiniportBlock.DriverHandle->MiniportCharacteristics.TransferDataHandler)(
         Packet,
         BytesTransferred,
         Adapter->NdisMiniportBlock.MiniportAdapterContext,
         MacReceiveContext,
         ByteOffset,
         BytesToTransfer);
+
+    KeLowerIrql(OldIrql);
+
+    return Status;
 }
 
 
@@ -466,21 +702,15 @@ NdisCloseAdapter(
  *     NdisBindingHandle = Handle returned by NdisOpenAdapter
  */
 {
-    KIRQL OldIrql;
     PADAPTER_BINDING AdapterBinding = GET_ADAPTER_BINDING(NdisBindingHandle);
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
     /* Remove from protocol's bound adapters list */
-    KeAcquireSpinLock(&AdapterBinding->ProtocolBinding->Lock, &OldIrql);
-    RemoveEntryList(&AdapterBinding->ProtocolListEntry);
-    KeReleaseSpinLock(&AdapterBinding->ProtocolBinding->Lock, OldIrql);
+    ExInterlockedRemoveEntryList(&AdapterBinding->ProtocolListEntry, &AdapterBinding->ProtocolBinding->Lock);
 
     /* Remove protocol from adapter's bound protocols list */
-    NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
-    KeAcquireSpinLock(&AdapterBinding->Adapter->NdisMiniportBlock.Lock, &OldIrql);
-    RemoveEntryList(&AdapterBinding->AdapterListEntry);
-    KeReleaseSpinLock(&AdapterBinding->Adapter->NdisMiniportBlock.Lock, OldIrql);
+    ExInterlockedRemoveEntryList(&AdapterBinding->AdapterListEntry, &AdapterBinding->Adapter->NdisMiniportBlock.Lock);
 
     ExFreePool(AdapterBinding);
 
@@ -503,7 +733,6 @@ NdisDeregisterProtocol(
  *     NdisProtocolHandle = Handle returned by NdisRegisterProtocol
  */
 {
-    KIRQL OldIrql;
     PPROTOCOL_BINDING Protocol = GET_PROTOCOL_BINDING(NdisProtocolHandle);
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
@@ -511,9 +740,7 @@ NdisDeregisterProtocol(
     /* FIXME: Make sure no adapter bindings exist */
 
     /* Remove protocol from global list */
-    KeAcquireSpinLock(&ProtocolListLock, &OldIrql);
-    RemoveEntryList(&Protocol->ListEntry);
-    KeReleaseSpinLock(&ProtocolListLock, OldIrql);
+    ExInterlockedRemoveEntryList(&Protocol->ListEntry, &ProtocolListLock);
 
     ExFreePool(Protocol);
 
@@ -564,7 +791,7 @@ NdisOpenAdapter(
 
   if(!NdisProtocolHandle)
     {
-      NDIS_DbgPrint(MAX_TRACE, ("NdisProtocolHandle is NULL\n"));
+      NDIS_DbgPrint(MIN_TRACE, ("NdisProtocolHandle is NULL\n"));
       *OpenErrorStatus = *Status = NDIS_STATUS_FAILURE;
       return;
     }
@@ -623,17 +850,12 @@ NdisOpenAdapter(
   AdapterBinding->NdisOpenBlock.SendPacketsHandler  = ProSendPackets;
   AdapterBinding->NdisOpenBlock.TransferDataHandler = ProTransferData;
 
-  AdapterBinding->NdisOpenBlock.RequestCompleteHandler = 
-    Protocol->Chars.RequestCompleteHandler; 
+  AdapterBinding->NdisOpenBlock.RequestCompleteHandler =
+    Protocol->Chars.RequestCompleteHandler;
 
-#if 0
-  /* XXX this looks fishy */
-  /* OK, this really *is* fishy - it bugchecks */
   /* Put on protocol's bound adapters list */
   ExInterlockedInsertTailList(&Protocol->AdapterListHead, &AdapterBinding->ProtocolListEntry, &Protocol->Lock);
-#endif
 
-  /* XXX so does this */
   /* Put protocol on adapter's bound protocols list */
   NDIS_DbgPrint(MAX_TRACE, ("acquiring miniport block lock\n"));
   ExInterlockedInsertTailList(&Adapter->ProtocolListHead, &AdapterBinding->AdapterListEntry, &Adapter->NdisMiniportBlock.Lock);
@@ -643,98 +865,10 @@ NdisOpenAdapter(
   *Status = NDIS_STATUS_SUCCESS;
 }
 
-
-/*
- * @implemented
- */
 VOID
-EXPORT
-NdisRegisterProtocol(
-    OUT PNDIS_STATUS                    Status,
-    OUT PNDIS_HANDLE                    NdisProtocolHandle,
-    IN  PNDIS_PROTOCOL_CHARACTERISTICS  ProtocolCharacteristics,
-    IN  UINT                            CharacteristicsLength)
-/*
- * FUNCTION: Registers an NDIS driver's ProtocolXxx entry points
- * ARGUMENTS:
- *     Status                  = Address of buffer for status information
- *     NdisProtocolHandle      = Address of buffer for handle used to identify the driver
- *     ProtocolCharacteristics = Pointer to NDIS_PROTOCOL_CHARACTERISTICS structure
- *     CharacteristicsLength   = Size of structure which ProtocolCharacteristics targets
- * NOTES:
- *     - you *must* set NdisProtocolHandle before doing anything that could wind up
- *       getting BindAdapterHandler, as it will probably call OpenAdapter with this handle
- *     - the above implies that the initialization of the protocol block must be complete
- *       by then
- * TODO:
- *     - break this function up - probably do a 'ndisRefreshProtocolBindings' function
- *     - make this thing able to handle >1 protocol
- */
+NTAPI
+ndisBindMiniportsToProtocol(OUT PNDIS_STATUS Status, IN PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics)
 {
-  PPROTOCOL_BINDING Protocol;
-  NTSTATUS NtStatus;
-  UINT MinSize;
-  HANDLE DriverKeyHandle = NULL;
-  PKEY_VALUE_PARTIAL_INFORMATION KeyInformation = NULL;
-  WCHAR *DataPtr;
-
-  NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
-
-  /* first validate the PROTOCOL_CHARACTERISTICS */
-  switch (ProtocolCharacteristics->MajorNdisVersion)
-    {
-    case 0x03:
-      /* we don't really want to support ndis3 drivers - so we complain for now */
-      NDIS_DbgPrint(MID_TRACE, ("NDIS 3 protocol attempting to register\n"));
-      MinSize = sizeof(NDIS30_PROTOCOL_CHARACTERISTICS);
-      break;
-
-    case 0x04:
-      MinSize = sizeof(NDIS40_PROTOCOL_CHARACTERISTICS);
-      break;
-
-    case 0x05:
-      MinSize = sizeof(NDIS50_PROTOCOL_CHARACTERISTICS);
-      break;
-
-    default:
-      *Status = NDIS_STATUS_BAD_VERSION;
-      NDIS_DbgPrint(MIN_TRACE, ("Incorrect characteristics size\n"));
-      return;
-    }
-
-  if (CharacteristicsLength < MinSize)
-    {
-      NDIS_DbgPrint(DEBUG_PROTOCOL, ("Bad protocol characteristics.\n"));
-      *Status = NDIS_STATUS_BAD_CHARACTERISTICS;
-      return;
-    }
-
-  /* set up the protocol block */
-  Protocol = ExAllocatePool(NonPagedPool, sizeof(PROTOCOL_BINDING));
-  if (!Protocol)
-    {
-      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
-      *Status = NDIS_STATUS_RESOURCES;
-      return;
-    }
-
-  RtlZeroMemory(Protocol, sizeof(PROTOCOL_BINDING));
-  RtlCopyMemory(&Protocol->Chars, ProtocolCharacteristics, MinSize);
-
-  NtStatus = RtlUpcaseUnicodeString(&Protocol->Chars.Name, &ProtocolCharacteristics->Name, TRUE);
-  if (!NT_SUCCESS(NtStatus))
-    {
-      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
-      ExFreePool(Protocol);
-      *Status = NDIS_STATUS_RESOURCES;
-      return;
-    }
-
-  KeInitializeSpinLock(&Protocol->Lock);
-
-  InitializeListHead(&Protocol->AdapterListHead);
-
   /*
    * bind the protocol to all of its miniports
    *
@@ -742,16 +876,18 @@ NdisRegisterProtocol(
    * get list of devices from Bind key
    * call BindAdapterHandler for each
    */
-  {
     OBJECT_ATTRIBUTES ObjectAttributes;
     UNICODE_STRING RegistryPath;
     WCHAR *RegistryPathStr;
+    NTSTATUS NtStatus;
+    WCHAR *DataPtr;
+    HANDLE DriverKeyHandle = NULL;
+    PKEY_VALUE_PARTIAL_INFORMATION KeyInformation = NULL;
 
     RegistryPathStr = ExAllocatePoolWithTag(PagedPool, sizeof(SERVICES_KEY) + ProtocolCharacteristics->Name.Length + sizeof(LINKAGE_KEY), NDIS_TAG + __LINE__);
     if(!RegistryPathStr)
       {
         NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
-        ExFreePool(Protocol);
         *Status = NDIS_STATUS_RESOURCES;
         return;
       }
@@ -771,12 +907,10 @@ NdisRegisterProtocol(
 
     if(!NT_SUCCESS(NtStatus))
       {
-        NDIS_DbgPrint(MID_TRACE, ("Unable to open protocol configuration\n"));
-        ExFreePool(Protocol);
+        NDIS_DbgPrint(MIN_TRACE, ("Unable to open protocol configuration\n"));
         *Status = NDIS_STATUS_FAILURE;
         return;
       }
-  }
 
   NDIS_DbgPrint(MAX_TRACE, ("Successfully opened the registry configuration\n"));
 
@@ -787,11 +921,10 @@ NdisRegisterProtocol(
     RtlInitUnicodeString(&ValueName, L"Bind");
 
     NtStatus = ZwQueryValueKey(DriverKeyHandle, &ValueName, KeyValuePartialInformation, NULL, 0, &ResultLength);
-    if(NtStatus != STATUS_BUFFER_OVERFLOW && NtStatus != STATUS_BUFFER_TOO_SMALL)
+    if(NtStatus != STATUS_BUFFER_OVERFLOW && NtStatus != STATUS_BUFFER_TOO_SMALL && NtStatus != STATUS_SUCCESS)
       {
-        NDIS_DbgPrint(MID_TRACE, ("Unable to query the Bind value for size\n"));
+        NDIS_DbgPrint(MIN_TRACE, ("Unable to query the Bind value for size\n"));
         ZwClose(DriverKeyHandle);
-        ExFreePool(Protocol);
         *Status = NDIS_STATUS_FAILURE;
         return;
       }
@@ -801,7 +934,6 @@ NdisRegisterProtocol(
       {
         NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
         ZwClose(DriverKeyHandle);
-        ExFreePool(Protocol);
         *Status = NDIS_STATUS_FAILURE;
         return;
       }
@@ -809,12 +941,12 @@ NdisRegisterProtocol(
     NtStatus = ZwQueryValueKey(DriverKeyHandle, &ValueName, KeyValuePartialInformation, KeyInformation,
         sizeof(KEY_VALUE_PARTIAL_INFORMATION) + ResultLength, &ResultLength);
 
+    ZwClose(DriverKeyHandle);
+
     if(!NT_SUCCESS(NtStatus))
       {
         NDIS_DbgPrint(MIN_TRACE, ("Unable to query the Bind value\n"));
-        ZwClose(DriverKeyHandle);
         ExFreePool(KeyInformation);
-        ExFreePool(Protocol);
         *Status = NDIS_STATUS_FAILURE;
         return;
       }
@@ -851,7 +983,6 @@ NdisRegisterProtocol(
         {
           NDIS_DbgPrint(MIN_TRACE, ("insufficient resources.\n"));
           ExFreePool(KeyInformation);
-          ExFreePool(Protocol);
           *Status = NDIS_STATUS_RESOURCES;
           return;
         }
@@ -868,36 +999,144 @@ NdisRegisterProtocol(
       NDIS_DbgPrint(MAX_TRACE, ("Calling protocol's BindAdapter handler with DeviceName %wZ and RegistryPath %wZ\n",
           &DeviceName, &RegistryPath));
 
-      /* XXX SD must do something with bind context */
-      *NdisProtocolHandle = Protocol;
-
         {
           BIND_HANDLER BindHandler = ProtocolCharacteristics->BindAdapterHandler;
           if(BindHandler)
+          {
             BindHandler(Status, BindContext, &DeviceName, &RegistryPath, 0);
+            NDIS_DbgPrint(MIN_TRACE, ("%wZ's BindAdapter handler returned 0x%x for %wZ\n", &ProtocolCharacteristics->Name, *Status, &DeviceName));
+          }
           else
-            NDIS_DbgPrint(MID_TRACE, ("No protocol bind handler specified\n"));
+            NDIS_DbgPrint(MIN_TRACE, ("No protocol bind handler specified\n"));
         }
+    }
 
-      /*
-      (*(Protocol->Chars.BindAdapterHandler))(Status, BindContext, &DeviceName, &RegistryPath, 0);
-      */
+   *Status = NDIS_STATUS_SUCCESS;
+   ExFreePool(KeyInformation);
+}
 
-      if(*Status == NDIS_STATUS_SUCCESS)
-        {
-          /* Put protocol binding struct on global list */
-          ExInterlockedInsertTailList(&ProtocolListHead, &Protocol->ListEntry, &ProtocolListLock);
-        }
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisRegisterProtocol(
+    OUT PNDIS_STATUS                    Status,
+    OUT PNDIS_HANDLE                    NdisProtocolHandle,
+    IN  PNDIS_PROTOCOL_CHARACTERISTICS  ProtocolCharacteristics,
+    IN  UINT                            CharacteristicsLength)
+/*
+ * FUNCTION: Registers an NDIS driver's ProtocolXxx entry points
+ * ARGUMENTS:
+ *     Status                  = Address of buffer for status information
+ *     NdisProtocolHandle      = Address of buffer for handle used to identify the driver
+ *     ProtocolCharacteristics = Pointer to NDIS_PROTOCOL_CHARACTERISTICS structure
+ *     CharacteristicsLength   = Size of structure which ProtocolCharacteristics targets
+ * NOTES:
+ *     - you *must* set NdisProtocolHandle before doing anything that could wind up
+ *       getting BindAdapterHandler, as it will probably call OpenAdapter with this handle
+ *     - the above implies that the initialization of the protocol block must be complete
+ *       by then
+ * TODO:
+ *     - break this function up - probably do a 'ndisRefreshProtocolBindings' function
+ *     - make this thing able to handle >1 protocol
+ */
+{
+  PPROTOCOL_BINDING Protocol;
+  NTSTATUS NtStatus;
+  UINT MinSize;
+  PNET_PNP_EVENT PnPEvent;
 
-      /*
-      else if(*Status != NDIS_STATUS_PENDING)
-        {
-          // what to do here?
-        }
-       */
+  NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  *NdisProtocolHandle = NULL;
+
+  /* first validate the PROTOCOL_CHARACTERISTICS */
+  switch (ProtocolCharacteristics->MajorNdisVersion)
+    {
+    case 0x03:
+      /* we don't really want to support ndis3 drivers - so we complain for now */
+      NDIS_DbgPrint(MID_TRACE, ("NDIS 3 protocol attempting to register\n"));
+      MinSize = sizeof(NDIS30_PROTOCOL_CHARACTERISTICS);
+      break;
+
+    case 0x04:
+      MinSize = sizeof(NDIS40_PROTOCOL_CHARACTERISTICS);
+      break;
+
+    case 0x05:
+      MinSize = sizeof(NDIS50_PROTOCOL_CHARACTERISTICS);
+      break;
+
+    default:
+      *Status = NDIS_STATUS_BAD_VERSION;
+      NDIS_DbgPrint(MIN_TRACE, ("Incorrect characteristics size\n"));
+      return;
+    }
+
+  if (CharacteristicsLength < MinSize)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Bad protocol characteristics.\n"));
+      *Status = NDIS_STATUS_BAD_CHARACTERISTICS;
+      return;
+    }
+
+  /* set up the protocol block */
+  Protocol = ExAllocatePool(NonPagedPool, sizeof(PROTOCOL_BINDING));
+  if (!Protocol)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      *Status = NDIS_STATUS_RESOURCES;
+      return;
+    }
+
+  RtlZeroMemory(Protocol, sizeof(PROTOCOL_BINDING));
+  RtlCopyMemory(&Protocol->Chars, ProtocolCharacteristics, MinSize);
+
+  NtStatus = RtlUpcaseUnicodeString(&Protocol->Chars.Name, &ProtocolCharacteristics->Name, TRUE);
+  if (!NT_SUCCESS(NtStatus))
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      ExFreePool(Protocol);
+      *Status = NDIS_STATUS_RESOURCES;
+      return;
     }
 
-  *Status             = NDIS_STATUS_SUCCESS;
+  KeInitializeSpinLock(&Protocol->Lock);
+
+  InitializeListHead(&Protocol->AdapterListHead);
+
+  /* We must set this before the call to ndisBindMiniportsToProtocol because the protocol's 
+   * BindAdapter handler might need it */
+
+  *NdisProtocolHandle = Protocol;
+
+  ndisBindMiniportsToProtocol(Status, &Protocol->Chars);
+
+  /* Should we only send this if ndisBindMiniportsToProtocol succeeds? */
+  PnPEvent = ProSetupPnPEvent(NetEventBindsComplete, NULL, 0);
+  if (PnPEvent)
+  {
+      if (Protocol->Chars.PnPEventHandler)
+      {
+          /* We call this with a NULL binding context because it affects all bindings */
+          NtStatus = (*Protocol->Chars.PnPEventHandler)(NULL,
+                                                        PnPEvent);
+
+          /* FIXME: We don't support this yet */
+          ASSERT(NtStatus != NDIS_STATUS_PENDING);
+      }
+
+      ExFreePool(PnPEvent);
+  }
+
+  if (*Status == NDIS_STATUS_SUCCESS) {
+      ExInterlockedInsertTailList(&ProtocolListHead, &Protocol->ListEntry, &ProtocolListLock);
+  } else {
+      NDIS_DbgPrint(MIN_TRACE, ("Binding failed (%x)\n", *Status));
+      ExFreePool(Protocol);
+      *NdisProtocolHandle = NULL;
+  }
 }
 
 \f
@@ -1006,4 +1245,45 @@ NdisTransferData(
                               BytesTransferred);
 }
 
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+NdisReEnumerateProtocolBindings(IN NDIS_HANDLE NdisProtocolHandle)
+{
+    PPROTOCOL_BINDING Protocol = NdisProtocolHandle;
+    NDIS_STATUS NdisStatus;
+
+    ndisBindMiniportsToProtocol(&NdisStatus, &Protocol->Chars);
+}
+
+
+/*
+ * @implemented
+ */
+VOID
+EXPORT
+NdisGetDriverHandle(
+    IN  PNDIS_HANDLE    NdisBindingHandle,
+    OUT PNDIS_HANDLE    NdisDriverHandle)
+/*
+ * FUNCTION:
+ * ARGUMENTS:
+ * NOTES:
+ *    NDIS 5.0
+ */
+{
+    PADAPTER_BINDING Binding = (PADAPTER_BINDING)NdisBindingHandle;
+
+    if (!Binding)
+    {
+        NDIS_DbgPrint(MIN_TRACE, ("Bad binding handle\n"));
+        *NdisDriverHandle = NULL;
+        return;
+    }
+
+    *NdisDriverHandle = Binding->Adapter->NdisMiniportBlock.DriverHandle;
+}
+
 /* EOF */