update for HEAD-2003091401
[reactos.git] / drivers / net / ndis / ndis / miniport.c
index cd1d3e7..02b740a 100644 (file)
@@ -4,22 +4,34 @@
  * FILE:        ndis/miniport.c
  * PURPOSE:     Routines used by NDIS miniport drivers
  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *              Vizzini (vizzini@plasmic.com)
  * REVISIONS:
  *   CSH 01/08-2000 Created
+ *   8/20/2003 vizzini - DMA support
  */
-#define DBG
 #include <miniport.h>
 #include <protocol.h>
 #ifdef DBG
 #include <buffer.h>
 #endif /* DBG */
 
-#ifdef DBG
-/* See debug.h for debug/trace constants */
-ULONG DebugTraceLevel = MIN_TRACE;
-//ULONG DebugTraceLevel = (MAX_TRACE + DEBUG_MINIPORT);
-#endif /* DBG */
+/* Root of the scm database */
+#define SERVICES_ROOT L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
+
+/* prefix for device object registration */
+#define DEVICE_ROOT L"\\Device\\"
+
+/*
+ * Define to 1 to get a debugger breakpoint at the end of NdisInitializeWrapper
+ * for each new miniport starting up
+ */
+#define BREAK_ON_MINIPORT_INIT 0
 
+/* 
+ * This has to be big enough to hold the results of querying the Route value
+ * from the Linkage key.  Please re-code me to determine this dynamically.
+ */
+#define ROUTE_DATA_SIZE 256
 
 /* Number of media we know */
 #define MEDIA_ARRAY_SIZE    15
@@ -42,21 +54,27 @@ static NDIS_MEDIUM MediaArray[MEDIA_ARRAY_SIZE] = {
     NdisMediumMax
 };
 
-
+/* global list and lock of Miniports NDIS has registered */
 LIST_ENTRY MiniportListHead;
 KSPIN_LOCK MiniportListLock;
+
+/* global list and lock of adapters NDIS has registered */
 LIST_ENTRY AdapterListHead;
 KSPIN_LOCK AdapterListLock;
 
+/* global list and lock of orphan adapters waiting to be claimed by a miniport */
+LIST_ENTRY OrphanAdapterListHead;
+KSPIN_LOCK OrphanAdapterListLock;
+
 
 #ifdef DBG
 VOID
 MiniDisplayPacket(
     PNDIS_PACKET Packet)
 {
+#if 0
     ULONG i, Length;
     UCHAR Buffer[64];
-#if 0
     if ((DebugTraceLevel | DEBUG_PACKET) > 0) {
         Length = CopyPacketToBuffer(
             (PUCHAR)&Buffer,
@@ -171,6 +189,23 @@ MiniIndicateData(
 }
 
 
+VOID STDCALL
+MiniIndicateReceivePacket(
+    IN  NDIS_HANDLE    Miniport,
+    IN  PPNDIS_PACKET  PacketArray,
+    IN  UINT           NumberOfPackets)
+/*
+ * FUNCTION: receives miniport packet array indications
+ * ARGUMENTS:
+ *     Miniport: Miniport handle for the adapter
+ *     PacketArray: pointer to a list of packet pointers to indicate
+ *     NumberOfPackets: number of packets to indicate
+ */
+{
+  NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+}
+
+
 VOID
 MiniEthReceiveComplete(
     IN  PETH_FILTER Filter)
@@ -326,6 +361,8 @@ MiniAdapterHasAddress(
     PNDIS_BUFFER NdisBuffer;
     UINT BufferLength;
 
+    NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+
     Start1 = (PUCHAR)&Adapter->Address;
     NdisQueryPacket(Packet, NULL, NULL, &NdisBuffer, NULL);
     if (!NdisBuffer) {
@@ -424,6 +461,8 @@ MiniQueryInformation(
     NDIS_STATUS NdisStatus;
     ULONG BytesNeeded;
 
+    NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+
     if (Adapter->QueryBufferLength == 0) {
         Adapter->QueryBuffer = ExAllocatePool(NonPagedPool, (Size == 0)? 32 : Size);
 
@@ -596,6 +635,8 @@ MiniDoRequest(
  *     Status of operation
  */
 {
+    NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+
     Adapter->NdisMiniportBlock.MediaRequest = NdisRequest;
 
     switch (NdisRequest->RequestType) {
@@ -721,6 +762,9 @@ VOID STDCALL MiniportDpc(
 }
 
 
+/*
+ * @unimplemented
+ */
 VOID
 EXPORT
 NdisMCloseLog(
@@ -730,6 +774,9 @@ NdisMCloseLog(
 }
 
 
+/*
+ * @unimplemented
+ */
 NDIS_STATUS
 EXPORT
 NdisMCreateLog(
@@ -743,15 +790,29 @@ NdisMCreateLog(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMDeregisterAdapterShutdownHandler(
     IN  NDIS_HANDLE MiniportHandle)
+/*
+ * FUNCTION: de-registers a shutdown handler
+ * ARGUMENTS:  MiniportHandle:  Handle passed into MiniportInitialize
+ */
 {
-    UNIMPLEMENTED
+  NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+  PLOGICAL_ADAPTER  Adapter = (PLOGICAL_ADAPTER)MiniportHandle;
+
+  if(Adapter->BugcheckContext->ShutdownHandler)
+    KeDeregisterBugCheckCallback(Adapter->BugcheckContext->CallbackRecord);
 }
 
 
+/*
+ * @unimplemented
+ */
 VOID
 EXPORT
 NdisMFlushLog(
@@ -761,6 +822,9 @@ NdisMFlushLog(
 }
 
 
+/*
+ * @unimplemented
+ */
 VOID
 EXPORT
 NdisMIndicateStatus(
@@ -773,6 +837,9 @@ NdisMIndicateStatus(
 }
 
 
+/*
+ * @unimplemented
+ */
 VOID
 EXPORT
 NdisMIndicateStatusComplete(
@@ -782,6 +849,9 @@ NdisMIndicateStatusComplete(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisInitializeWrapper(
@@ -796,12 +866,20 @@ NdisInitializeWrapper(
  *     SystemSpecific1   = Pointer to the driver's driver object
  *     SystemSpecific2   = Pointer to the driver's registry path
  *     SystemSpecific3   = Always NULL
+ * NOTES:
+ *     - SystemSpecific2 goes invalid so we copy it
  */
 {
     PMINIPORT_DRIVER Miniport;
+    PUNICODE_STRING RegistryPath;
+    WCHAR *RegistryBuffer;
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
+#if BREAK_ON_MINIPORT_INIT
+  __asm__ ("int $3\n");
+#endif
+
     Miniport = ExAllocatePool(NonPagedPool, sizeof(MINIPORT_DRIVER));
     if (!Miniport) {
         NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
@@ -817,6 +895,30 @@ NdisInitializeWrapper(
 
     Miniport->DriverObject = (PDRIVER_OBJECT)SystemSpecific1;
 
+    /* set the miniport's driver registry path */
+    RegistryPath = ExAllocatePool(PagedPool, sizeof(UNICODE_STRING));
+    if(!RegistryPath)
+    {
+       NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+       return;
+    }
+
+    RegistryPath->Length = ((PUNICODE_STRING)SystemSpecific2)->Length;
+    RegistryPath->MaximumLength = RegistryPath->Length + sizeof(WCHAR);        /* room for 0-term */
+
+    RegistryBuffer = ExAllocatePool(PagedPool, RegistryPath->Length + sizeof(WCHAR));
+    if(!RegistryBuffer)
+    {
+       NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+       return;
+    }
+
+    RtlCopyMemory(RegistryBuffer, ((PUNICODE_STRING)SystemSpecific2)->Buffer, RegistryPath->Length);
+    RegistryBuffer[RegistryPath->Length/sizeof(WCHAR)] = 0;
+
+    RegistryPath->Buffer = RegistryBuffer;
+    Miniport->RegistryPath = RegistryPath;
+
     InitializeListHead(&Miniport->AdapterListHead);
 
     /* Put miniport in global miniport list */
@@ -825,9 +927,13 @@ NdisInitializeWrapper(
                                 &MiniportListLock);
 
     *NdisWrapperHandle = Miniport;
+
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMQueryInformationComplete(
@@ -846,14 +952,69 @@ NdisMQueryInformationComplete(
 }
 
 
+VOID NdisIBugcheckCallback(
+    IN PVOID   Buffer,
+    IN ULONG   Length)
+/*
+ * FUNCTION:  Internal callback for handling bugchecks - calls adapter's shutdown handler
+ * ARGUMENTS:
+ *     Buffer:  Pointer to a bugcheck callback context
+ *     Length:  Unused
+ */
+{
+  PMINIPORT_BUGCHECK_CONTEXT Context = (PMINIPORT_BUGCHECK_CONTEXT)Buffer;
+  ADAPTER_SHUTDOWN_HANDLER sh = (ADAPTER_SHUTDOWN_HANDLER)Context->ShutdownHandler;
+
+   NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+
+  if(sh)
+    sh(Context->DriverContext);
+} 
+
+
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMRegisterAdapterShutdownHandler(
     IN  NDIS_HANDLE                 MiniportHandle,
     IN  PVOID                       ShutdownContext,
     IN  ADAPTER_SHUTDOWN_HANDLER    ShutdownHandler)
+/*
+ * FUNCTION:  Register a shutdown handler for an adapter
+ * ARGUMENTS:
+ *     MiniportHandle:  Handle originally passed into MiniportInitialize
+ *     ShutdownContext:  Pre-initialized bugcheck context
+ *     ShutdownHandler:  Function to call to handle the bugcheck
+ * NOTES:
+ *     - I'm not sure about ShutdownContext
+ *     - FIXME - memory leak below
+ */
 {
-    UNIMPLEMENTED
+  PLOGICAL_ADAPTER            Adapter = (PLOGICAL_ADAPTER)MiniportHandle;
+  PMINIPORT_BUGCHECK_CONTEXT  BugcheckContext = Adapter->BugcheckContext;
+
+  NDIS_DbgPrint(DEBUG_MINIPORT, ("Called.\n"));
+
+  if(BugcheckContext) 
+    return;
+
+  BugcheckContext = ExAllocatePool(NonPagedPool, sizeof(MINIPORT_BUGCHECK_CONTEXT));
+  if(!BugcheckContext)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      return;
+    }
+
+  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));
+
+  KeRegisterBugCheckCallback(BugcheckContext->CallbackRecord, NdisIBugcheckCallback, 
+      BugcheckContext, sizeof(BugcheckContext), "Ndis Miniport");
 }
 
 
@@ -953,6 +1114,274 @@ DoQueries(
 }
 
 
+VOID
+NdisIStartAdapter(
+    WCHAR *DeviceNameStr,
+    UINT DeviceNameStrLength,
+    PMINIPORT_DRIVER Miniport
+)
+/*
+ * FUNCTION: Start an adapter
+ * ARGUMENTS:
+ *     DeviceNameStr: 0-terminated wide char string of name of device to start
+ *     DeviceNameStrLength: length of DeviceNameStr *IN WCHARs*
+ * NOTES:
+ * TODO:
+ *     - verify that all resources are properly freed on success & failure
+ */
+{
+  WCHAR *DeviceName;
+  HANDLE RegKeyHandle;
+  WCHAR *RegKeyPath;
+  UNICODE_STRING RegKeyPathU;
+  OBJECT_ATTRIBUTES RegKeyAttributes;
+  NDIS_STATUS NdisStatus;
+  NDIS_STATUS OpenErrorStatus;
+  NTSTATUS Status;
+  UINT SelectedMediumIndex = 0;
+  PLOGICAL_ADAPTER Adapter = 0;
+  NDIS_OID AddressOID;
+  BOOLEAN MemError = FALSE;
+  KIRQL OldIrql;
+  PORPHAN_ADAPTER OrphanAdapter = 0;
+
+  NDIS_DbgPrint(MAX_TRACE, ("Called with %ws\n", DeviceNameStr));
+  Adapter = ExAllocatePool(NonPagedPool, sizeof(LOGICAL_ADAPTER));
+  if (!Adapter) 
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      return; 
+    }
+
+  /* This is very important */
+  RtlZeroMemory(Adapter, sizeof(LOGICAL_ADAPTER));
+
+  DeviceName = ExAllocatePool(NonPagedPool, sizeof(DEVICE_ROOT) + DeviceNameStrLength * sizeof(WCHAR));
+  if(!DeviceName)
+    {
+      NDIS_DbgPrint(MIN_TRACE,("Insufficient memory\n"));
+      ExFreePool(Adapter);
+      return;
+    }
+
+  /* DEVICE_ROOT is a constant string defined above, incl. 0-term */
+  wcscpy(DeviceName, DEVICE_ROOT);
+
+  /* reg_sz is 0-term by def */
+  wcsncat(DeviceName, DeviceNameStr, DeviceNameStrLength); 
+  RtlInitUnicodeString(&Adapter->DeviceName, DeviceName);
+
+  NDIS_DbgPrint(MAX_TRACE, ("creating device %ws\n", DeviceName));
+
+  Status = IoCreateDevice(Miniport->DriverObject, 0, &Adapter->DeviceName, FILE_DEVICE_PHYSICAL_NETCARD,
+      0, FALSE, &Adapter->NdisMiniportBlock.DeviceObject);
+  if (!NT_SUCCESS(Status)) 
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Could not create device object.\n"));
+      ExFreePool(Adapter);
+      return;
+    }
+
+  /* find out if there are any adapters in the orphans list and reserve resources */
+  KeAcquireSpinLock(&OrphanAdapterListLock, &OldIrql);
+  OrphanAdapter = (PORPHAN_ADAPTER)OrphanAdapterListHead.Flink;
+  while(&OrphanAdapter->ListEntry != &OrphanAdapterListHead)
+    {
+      PORPHAN_ADAPTER TempAdapter;
+      PCM_RESOURCE_LIST ResourceList;
+      UINT i;
+
+      if(!RtlCompareUnicodeString(&OrphanAdapter->RegistryPath, Miniport->RegistryPath, TRUE))
+        {
+          OrphanAdapter = (PORPHAN_ADAPTER)OrphanAdapter->ListEntry.Flink;
+          continue;
+        }
+
+      NDIS_DbgPrint(MAX_TRACE, ("Found an orphan adapter for RegistryPath %wZ\n", Miniport->RegistryPath));
+
+      /* there is an orphan adapter for us */
+      Adapter->SlotNumber = OrphanAdapter->SlotNumber;
+      Adapter->BusNumber  = OrphanAdapter->BusNumber;
+      Adapter->BusType    = OrphanAdapter->BusType;
+
+      Status = HalAssignSlotResources(Miniport->RegistryPath, 0, Miniport->DriverObject,
+          Adapter->NdisMiniportBlock.DeviceObject, Adapter->BusType, Adapter->BusNumber, 
+          Adapter->SlotNumber, &ResourceList);
+
+      if(!NT_SUCCESS(Status))
+        {
+          NDIS_DbgPrint(MIN_TRACE, ("HalAssignSlotResources broke: 0x%x\n", Status));
+#ifdef DBG
+          __asm__ ("int $3\n");
+#endif
+          /* i guess we should just give up on this adapter */
+          break;
+        }
+
+      /* go through the returned resource list and populate the Adapter */
+      for(i = 0; i<ResourceList->Count; i++)
+        {
+          int j;
+
+          PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor = &ResourceList->List[i];
+
+          for(j=0; j<ResourceDescriptor->PartialResourceList.Count; j++)
+            {
+              PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResourceDescriptor = 
+                  &ResourceDescriptor->PartialResourceList.PartialDescriptors[i];
+
+              switch(PartialResourceDescriptor->Type)
+                {
+                case CmResourceTypeInterrupt:
+                  Adapter->Irql = PartialResourceDescriptor->u.Interrupt.Level;
+                  Adapter->Vector = PartialResourceDescriptor->u.Interrupt.Vector;
+                  Adapter->Affinity = PartialResourceDescriptor->u.Interrupt.Affinity;
+                  break;
+
+                case CmResourceTypePort:
+                  Adapter->BaseIoAddress = PartialResourceDescriptor->u.Port.Start;
+                  break;
+
+                case CmResourceTypeMemory:
+                  Adapter->BaseMemoryAddress = PartialResourceDescriptor->u.Memory.Start;
+                  break;
+
+                case CmResourceTypeDma:
+                  Adapter->DmaPort = PartialResourceDescriptor->u.Dma.Port;
+                  Adapter->DmaChannel = PartialResourceDescriptor->u.Dma.Channel;
+                  break;
+
+                case CmResourceTypeDeviceSpecific:
+                default:
+                  break;
+                }
+            }
+        }
+
+      /* remove the adapter from the list */
+      TempAdapter = (PORPHAN_ADAPTER)OrphanAdapter->ListEntry.Flink;
+      RemoveEntryList(&OrphanAdapter->ListEntry);
+      OrphanAdapter = TempAdapter;
+    }
+  KeReleaseSpinLock(&OrphanAdapterListLock, OldIrql);
+
+  /* includes room for a 0-term */
+  RegKeyPath = ExAllocatePool(PagedPool, (wcslen(SERVICES_ROOT) + wcslen(DeviceNameStr) + 1) * sizeof(WCHAR));
+  if(!RegKeyPath)
+    {
+      NDIS_DbgPrint(MIN_TRACE,("Insufficient resources\n"));
+      ExFreePool(Adapter);
+      return; 
+    }
+
+  wcscpy(RegKeyPath, SERVICES_ROOT);
+  wcscat(RegKeyPath, DeviceNameStr);
+  RegKeyPath[wcslen(SERVICES_ROOT) + wcslen(DeviceNameStr)] = 0;
+
+  RtlInitUnicodeString(&RegKeyPathU, RegKeyPath);
+  InitializeObjectAttributes(&RegKeyAttributes, &RegKeyPathU, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+  Status = ZwOpenKey(&RegKeyHandle, KEY_ALL_ACCESS, &RegKeyAttributes);
+  if(Status != STATUS_SUCCESS)
+    {
+      NDIS_DbgPrint(MIN_TRACE,("failed to open adapter-specific reg key %ws\n", RegKeyPath));
+      ExFreePool(Adapter);
+      return;
+    }
+
+  NDIS_DbgPrint(MAX_TRACE, ("opened device reg key: %wZ\n", &RegKeyPathU));
+
+  KeInitializeSpinLock(&Adapter->NdisMiniportBlock.Lock);
+  InitializeListHead(&Adapter->ProtocolListHead);
+  Adapter->RefCount = 1;
+  Adapter->Miniport = Miniport;
+
+  /* Set handlers (some NDIS macros require these) */
+
+  Adapter->NdisMiniportBlock.EthRxCompleteHandler = MiniEthReceiveComplete;
+  Adapter->NdisMiniportBlock.EthRxIndicateHandler = MiniEthReceiveIndication;
+  Adapter->NdisMiniportBlock.SendCompleteHandler  = MiniSendComplete;
+  Adapter->NdisMiniportBlock.SendResourcesHandler = MiniSendResourcesAvailable;
+  Adapter->NdisMiniportBlock.ResetCompleteHandler = MiniResetComplete;
+  Adapter->NdisMiniportBlock.TDCompleteHandler    = MiniTransferDataComplete;
+  Adapter->NdisMiniportBlock.PacketIndicateHandler= MiniIndicateReceivePacket;
+
+  KeInitializeDpc(&Adapter->MiniportDpc, MiniportDpc, (PVOID)Adapter);
+
+  /* Put adapter in adapter list for this miniport */
+  ExInterlockedInsertTailList(&Miniport->AdapterListHead, &Adapter->MiniportListEntry, &Miniport->Lock);
+
+  /* Put adapter in global adapter list */
+  ExInterlockedInsertTailList(&AdapterListHead, &Adapter->ListEntry, &AdapterListLock);
+
+  /* Call MiniportInitialize */
+  NDIS_DbgPrint(MID_TRACE, ("calling MiniportInitialize\n"));
+  NdisStatus = (*Miniport->Chars.InitializeHandler)( &OpenErrorStatus, &SelectedMediumIndex, &MediaArray[0],
+      MEDIA_ARRAY_SIZE, Adapter, RegKeyHandle);
+
+  ZwClose(RegKeyHandle);
+
+  if ((NdisStatus == NDIS_STATUS_SUCCESS) && (SelectedMediumIndex < MEDIA_ARRAY_SIZE)) 
+    {
+      NDIS_DbgPrint(MID_TRACE,("successful return from MiniportInitialize\n"));
+
+      Adapter->NdisMiniportBlock.MediaType = MediaArray[SelectedMediumIndex];
+
+      switch (Adapter->NdisMiniportBlock.MediaType) 
+        {
+        case NdisMedium802_3:
+          Adapter->MediumHeaderSize = 14;       /* XXX figure out what to do about LLC */
+          AddressOID = OID_802_3_CURRENT_ADDRESS;
+          Adapter->AddressLength = ETH_LENGTH_OF_ADDRESS;
+
+          Adapter->NdisMiniportBlock.FilterDbs.u.EthDB = ExAllocatePool(NonPagedPool, sizeof(ETH_FILTER));
+          if (Adapter->NdisMiniportBlock.FilterDbs.u.EthDB) 
+            {
+              RtlZeroMemory(Adapter->NdisMiniportBlock.FilterDbs.u.EthDB, sizeof(ETH_FILTER));
+              Adapter->NdisMiniportBlock.FilterDbs.u.EthDB->Miniport = (PNDIS_MINIPORT_BLOCK)Adapter;
+            } 
+          else
+            MemError = TRUE;
+
+          break;
+
+        default:
+          /* FIXME: Support other types of media */
+          ExFreePool(Adapter);
+          ASSERT(FALSE);
+          return;
+        }
+
+      NdisStatus = DoQueries(Adapter, AddressOID);
+    }
+
+  if ((MemError) || (NdisStatus != NDIS_STATUS_SUCCESS) || (SelectedMediumIndex >= MEDIA_ARRAY_SIZE)) 
+    {
+      NDIS_DbgPrint(MAX_TRACE, ("return from MiniportInitialize: NdisStatus 0x%x, SelectedMediumIndex 0x%x\n",
+          NdisStatus, SelectedMediumIndex));
+
+      /* Remove adapter from adapter list for this miniport */
+      KeAcquireSpinLock(&Miniport->Lock, &OldIrql);
+      RemoveEntryList(&Adapter->MiniportListEntry);
+      KeReleaseSpinLock(&Miniport->Lock, OldIrql);
+
+      /* Remove adapter from global adapter list */
+      KeAcquireSpinLock(&AdapterListLock, &OldIrql);
+      RemoveEntryList(&Adapter->ListEntry);
+      KeReleaseSpinLock(&AdapterListLock, OldIrql);
+
+      if (Adapter->LookaheadBuffer)
+        ExFreePool(Adapter->LookaheadBuffer);
+
+      IoDeleteDevice(Adapter->NdisMiniportBlock.DeviceObject);
+      ExFreePool(Adapter);
+      NDIS_DbgPrint(MIN_TRACE, ("MiniportInitialize() failed for an adapter.\n"));
+    }
+}
+
+/*
+ * @implemented
+ */
 NDIS_STATUS
 EXPORT
 NdisMRegisterMiniport(
@@ -966,19 +1395,26 @@ NdisMRegisterMiniport(
  *     MiniportCharacteristics = Pointer to a buffer with miniport characteristics
  *     CharacteristicsLength   = Number of bytes in characteristics buffer
  * RETURNS:
- *     Status of operation
+ *     Status of operation      
+ * NOTES:
+ *     - To create device objects for the miniport, the Route value under Linkage is
+ *       parsed.  I don't know if this is the way Microsoft does it or not.
+ * TODO: 
+ *     verify this algorithm by playing with nt
  */
 {
     UINT MinSize;
-    KIRQL OldIrql;
     NTSTATUS Status;
-    NDIS_STATUS NdisStatus;
-    NDIS_STATUS OpenErrorStatus;
-    UINT SelectedMediumIndex;
-    PLOGICAL_ADAPTER Adapter;
-    NDIS_OID AddressOID;
-    BOOLEAN MemError          = FALSE;
     PMINIPORT_DRIVER Miniport = GET_MINIPORT_DRIVER(NdisWrapperHandle);
+    OBJECT_ATTRIBUTES DeviceKeyAttributes;
+    OBJECT_ATTRIBUTES LinkageKeyAttributes;
+    HANDLE DeviceKeyHandle;
+    HANDLE LinkageKeyHandle;
+    UNICODE_STRING RouteVal;
+    UNICODE_STRING LinkageKeyName;
+    KEY_VALUE_PARTIAL_INFORMATION *RouteData;
+    ULONG RouteDataLength;
+    UINT NextRouteOffset = 0;
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
@@ -1029,130 +1465,81 @@ NdisMRegisterMiniport(
         }
     }
 
+    /* TODO: verify NDIS5 and NDIS5.1 */
+
     RtlCopyMemory(&Miniport->Chars, MiniportCharacteristics, MinSize);
 
-    Adapter = ExAllocatePool(NonPagedPool, sizeof(LOGICAL_ADAPTER));
-    if (!Adapter) {
-        NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
-        return NDIS_STATUS_RESOURCES;
-    }
+    /*
+     * extract the list of bound adapters from the registry's Route value
+     * for this adapter.  It seems under WinNT that the Route value in the
+     * Linkage subkey holds an entry for each miniport instance we know about.
+     * This surely isn't how Windows does it, but it's better than nothing.
+     */
+
+    /* Read the miniport config from the registry */
+    InitializeObjectAttributes(&DeviceKeyAttributes, Miniport->RegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
 
-    /* This is very important */
-    RtlZeroMemory(Adapter, sizeof(LOGICAL_ADAPTER));
-
-    /* Create the device object for this adapter */
-    /* FIXME: Use GUIDs */
-    RtlInitUnicodeStringFromLiteral(&Adapter->DeviceName, L"\\Device\\ne2000");
-    Status = IoCreateDevice(Miniport->DriverObject,
-                            0,
-                            &Adapter->DeviceName,
-                            FILE_DEVICE_PHYSICAL_NETCARD,
-                            0,
-                            FALSE,
-                            &Adapter->NdisMiniportBlock.DeviceObject);
-    if (!NT_SUCCESS(Status)) {
-        NDIS_DbgPrint(MIN_TRACE, ("Could not create device object.\n"));
-        ExFreePool(Adapter);
+    Status = ZwOpenKey(&DeviceKeyHandle, KEY_READ, &DeviceKeyAttributes);
+    if(!NT_SUCCESS(Status))
+    {
+        NDIS_DbgPrint(MIN_TRACE,("Failed to open driver key: 0x%x\n", Status));
         return NDIS_STATUS_FAILURE;
     }
 
-    /* Initialize adapter object */
-
-    KeInitializeSpinLock(&Adapter->NdisMiniportBlock.Lock);
-
-    InitializeListHead(&Adapter->ProtocolListHead);
-
-    Adapter->RefCount = 1;
-
-    Adapter->Miniport = Miniport;
-
-    /* Set handlers (some NDIS macros require these) */
-
-    Adapter->NdisMiniportBlock.EthRxCompleteHandler = MiniEthReceiveComplete;
-    Adapter->NdisMiniportBlock.EthRxIndicateHandler = MiniEthReceiveIndication;
+    RtlInitUnicodeString(&LinkageKeyName, L"Linkage");
+    InitializeObjectAttributes(&LinkageKeyAttributes, &LinkageKeyName, OBJ_CASE_INSENSITIVE, DeviceKeyHandle, NULL);
 
-    Adapter->NdisMiniportBlock.SendCompleteHandler  = MiniSendComplete;
-    Adapter->NdisMiniportBlock.SendResourcesHandler = MiniSendResourcesAvailable;
-    Adapter->NdisMiniportBlock.ResetCompleteHandler = MiniResetComplete;
-    Adapter->NdisMiniportBlock.TDCompleteHandler    = MiniTransferDataComplete;
-
-
-    KeInitializeDpc(&Adapter->MiniportDpc, MiniportDpc, (PVOID)Adapter);
-
-    /* Put adapter in adapter list for this miniport */
-    ExInterlockedInsertTailList(&Miniport->AdapterListHead,
-                                &Adapter->MiniportListEntry,
-                                &Miniport->Lock);
-
-    /* Put adapter in global adapter list */
-    ExInterlockedInsertTailList(&AdapterListHead,
-                                &Adapter->ListEntry,
-                                &AdapterListLock);
-
-    /* Call MiniportInitialize */
-    NdisStatus = (*Miniport->Chars.InitializeHandler)(
-        &OpenErrorStatus,
-        &SelectedMediumIndex,
-        &MediaArray[0],
-        MEDIA_ARRAY_SIZE,
-        Adapter,
-        NULL /* FIXME: WrapperConfigurationContext */);
-
-    if ((NdisStatus == NDIS_STATUS_SUCCESS) &&
-        (SelectedMediumIndex < MEDIA_ARRAY_SIZE)) {
-        
-        Adapter->NdisMiniportBlock.MediaType = MediaArray[SelectedMediumIndex];
+    Status = ZwOpenKey(&LinkageKeyHandle, KEY_READ, &LinkageKeyAttributes);
+    if(!NT_SUCCESS(Status))
+    {
+        NDIS_DbgPrint(MIN_TRACE,("Failed to open Linkage key: 0x%x\n", Status));
+        ZwClose(DeviceKeyHandle);
+        return NDIS_STATUS_FAILURE;
+    }
 
-        switch (Adapter->NdisMiniportBlock.MediaType) {
-        case NdisMedium802_3:
-            Adapter->MediumHeaderSize = 14;
-            AddressOID = OID_802_3_CURRENT_ADDRESS;
-            Adapter->AddressLength = ETH_LENGTH_OF_ADDRESS;
-
-            Adapter->NdisMiniportBlock.FilterDbs.u.EthDB = ExAllocatePool(NonPagedPool,
-                                                        sizeof(ETH_FILTER));
-            if (Adapter->NdisMiniportBlock.FilterDbs.u.EthDB) {
-                RtlZeroMemory(Adapter->NdisMiniportBlock.FilterDbs.u.EthDB, sizeof(ETH_FILTER));
-                Adapter->NdisMiniportBlock.FilterDbs.u.EthDB->Miniport = (PNDIS_MINIPORT_BLOCK)Adapter;
-            } else
-                MemError = TRUE;
-            break;
+    RouteData = ExAllocatePool(PagedPool, ROUTE_DATA_SIZE);
+    if(!RouteData)
+    {
+        NDIS_DbgPrint(MIN_TRACE,("Insufficient resources\n"));
+        ZwClose(LinkageKeyHandle);
+        ZwClose(DeviceKeyHandle);
+        return NDIS_STATUS_RESOURCES;
+    }
 
-        default:
-            /* FIXME: Support other types of medias */
-            ASSERT(FALSE);
-            return NDIS_STATUS_FAILURE;
-        }
+    RtlInitUnicodeString(&RouteVal, L"Route");
 
-        NdisStatus = DoQueries(Adapter, AddressOID);
+    Status = ZwQueryValueKey(LinkageKeyHandle, &RouteVal, KeyValuePartialInformation, RouteData, ROUTE_DATA_SIZE, &RouteDataLength);
+    if(!NT_SUCCESS(Status))
+    {
+        NDIS_DbgPrint(MIN_TRACE,("Failed to query Route value\n"));
+        ZwClose(LinkageKeyHandle);
+        ZwClose(DeviceKeyHandle);
+        ExFreePool(RouteData);
+        return NDIS_STATUS_FAILURE;
     }
 
-    if ((MemError) ||
-        (NdisStatus != NDIS_STATUS_SUCCESS) ||
-        (SelectedMediumIndex >= MEDIA_ARRAY_SIZE)) {
+    ZwClose(LinkageKeyHandle);
+    ZwClose(DeviceKeyHandle);
 
-        /* Remove adapter from adapter list for this miniport */
-        KeAcquireSpinLock(&Miniport->Lock, &OldIrql);
-        RemoveEntryList(&Adapter->MiniportListEntry);
-        KeReleaseSpinLock(&Miniport->Lock, OldIrql);
-
-        /* Remove adapter from global adapter list */
-        KeAcquireSpinLock(&AdapterListLock, &OldIrql);
-        RemoveEntryList(&Adapter->ListEntry);
-        KeReleaseSpinLock(&AdapterListLock, OldIrql);
+    /* route is a REG_MULTI_SZ with each nic object created by NDI - create an adapter for each */
+  while(*(RouteData->Data + NextRouteOffset))
+    {
+      NDIS_DbgPrint(MID_TRACE, ("Starting adapter %ws\n", (WCHAR *)(RouteData->Data + NextRouteOffset)));
 
-        if (Adapter->LookaheadBuffer)
-            ExFreePool(Adapter->LookaheadBuffer);
+      NdisIStartAdapter((WCHAR *)(RouteData->Data + NextRouteOffset), 
+          wcslen((WCHAR *)(RouteData->Data + NextRouteOffset)), Miniport);
 
-        IoDeleteDevice(Adapter->NdisMiniportBlock.DeviceObject);
-        ExFreePool(Adapter);
-        return NDIS_STATUS_FAILURE;
+      NextRouteOffset += wcslen((WCHAR *)(RouteData->Data + NextRouteOffset)); 
     }
 
+    ExFreePool(RouteData);
     return NDIS_STATUS_SUCCESS;
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMResetComplete(
@@ -1166,6 +1553,9 @@ NdisMResetComplete(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMSendComplete(
@@ -1187,6 +1577,9 @@ NdisMSendComplete(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMSendResourcesAvailable(
@@ -1196,6 +1589,9 @@ NdisMSendResourcesAvailable(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMTransferDataComplete(
@@ -1211,6 +1607,9 @@ NdisMTransferDataComplete(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMSetInformationComplete(
@@ -1229,6 +1628,9 @@ NdisMSetInformationComplete(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMSetAttributes(
@@ -1256,6 +1658,9 @@ NdisMSetAttributes(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisMSetAttributesEx(
@@ -1275,10 +1680,33 @@ NdisMSetAttributesEx(
  *     AdapterType               = Specifies the I/O bus interface of the caller's NIC
  */
 {
-    UNIMPLEMENTED
+       // Currently just like NdisMSetAttributesEx
+       // TODO: Take CheckForHandTimeInSeconds into account!
+       PLOGICAL_ADAPTER Adapter = GET_LOGICAL_ADAPTER(MiniportAdapterHandle);
+
+    NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+    NDIS_DbgPrint(MAX_TRACE, ("NdisMSetAttributesEx() is partly-implemented.\n"));
+
+    Adapter->NdisMiniportBlock.MiniportAdapterContext = MiniportAdapterContext;
+
+    /* don't know why this is here - anybody? */
+    Adapter->Attributes = AttributeFlags & NDIS_ATTRIBUTE_BUS_MASTER;
+    Adapter->NdisMiniportBlock.AdapterType   = AdapterType;
+    Adapter->AttributesSet = TRUE;
+
+    if(AttributeFlags & NDIS_ATTRIBUTE_DESERIALIZE)
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("Deserialized miniport - UNIMPLEMENTED\n"));
+#ifdef DBG
+        __asm__("int $3\n");
+#endif
+      }
 }
 
 
+/*
+ * @unimplemented
+ */
 VOID
 EXPORT
 NdisMSleep(
@@ -1288,6 +1716,9 @@ NdisMSleep(
 }
 
 
+/*
+ * @unimplemented
+ */
 BOOLEAN
 EXPORT
 NdisMSynchronizeWithInterrupt(
@@ -1301,6 +1732,9 @@ NdisMSynchronizeWithInterrupt(
 }
 
 
+/*
+ * @unimplemented
+ */
 NDIS_STATUS
 EXPORT
 NdisMWriteLogData(
@@ -1314,6 +1748,9 @@ NdisMWriteLogData(
 }
 
 
+/*
+ * @implemented
+ */
 VOID
 EXPORT
 NdisTerminateWrapper(
@@ -1330,7 +1767,47 @@ NdisTerminateWrapper(
 
     NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
 
+    ExFreePool(Miniport->RegistryPath->Buffer);
+    ExFreePool(Miniport->RegistryPath);
     ExFreePool(Miniport);
 }
 
 /* EOF */
+/* enum test */
+        /*
+    {
+       ULONG KeyInformationSize;
+       KEY_BASIC_INFORMATION *KeyInformation;
+       int i;
+
+       KeyInformation = ExAllocatePool(PagedPool, 1024);
+       ASSERT(KeyInformation);
+
+       RtlInitUnicodeString(&LinkageKeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\NE2000");
+       InitializeObjectAttributes(&LinkageKeyAttributes, &LinkageKeyName, OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);
+
+       Status = ZwOpenKey(&LinkageKeyHandle, KEY_READ, &LinkageKeyAttributes);
+       if(!NT_SUCCESS(Status))
+       {
+               DbgPrint("ndis!NdisMRegisterMiniport: Failed to open Linkage key: 0x%x\n", Status);
+               ASSERT(0);      
+               KeBugCheck(0);
+       }
+
+       for(i=0;i<5;i++)
+       {
+           Status = ZwEnumerateKey(LinkageKeyHandle, i, KeyBasicInformation, KeyInformation, 1024, &KeyInformationSize);
+            if(!NT_SUCCESS(Status))
+           {
+                DbgPrint("ndis!NdisMRegisterMiniport: Failed to enumerate: 0x%x\n", Status);
+               break;
+           }
+          
+           KeyInformation->Name[KeyInformation->NameLength/sizeof(WCHAR)] = 0;
+            DbgPrint("ndis!NdisMRegisterMiniport: enumerated key %ws\n", KeyInformation->Name);
+       }
+
+       ExFreePool(KeyInformation);
+       KeBugCheck(0);
+    }
+        */