update for HEAD-2003091401
[reactos.git] / drivers / net / ndis / ndis / enum.c
diff --git a/drivers/net/ndis/ndis/enum.c b/drivers/net/ndis/ndis/enum.c
new file mode 100644 (file)
index 0000000..47be389
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS NDIS library
+ * FILE:        ndis/config.c
+ * PURPOSE:     NDIS Configuration Services
+ * PROGRAMMERS: Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *     Vizzini 08-20-2003 Created
+ * NOTES:
+ *     - Currently this only supports enumeration of Root and PCI devices
+ *     - This whole thing is really just a band-aid until we have real PnP
+ *     - Strictly speaking, I'm not even sure it's my job to call 
+ *       HalAssignSlotResources(), but the vmware nic driver likes it
+ *     - Please send me feedback if there is a better way :)
+ * TODO:
+ *     - Break these functions up a bit; i hate 200-line functions
+ */
+
+#include <ndissys.h>
+#include <miniport.h>
+
+/* Registry path to the enumeration database */
+#define ENUM_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum"
+
+/* Registry path to the services database */
+#define SERVICES_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services"
+
+/*
+ * This has to be big enough to hold enumerated registry paths until
+ * registry accesses are properly re-coded
+ */
+#define KEY_INFORMATION_SIZE 512
+
+/* same sort of deal as above */
+#define VALUE_INFORMATION_SIZE 100
+
+/*
+ * NET class GUID, as defined by Microsoft, used to tell if a
+ * device belongs to NDIS or not.
+ */
+#define NET_GUID L"{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define RZ() do { DbgPrint("%s:%i Checking RedZone\n", __FILE__, __LINE__ ); ExAllocatePool(PagedPool,0); } while (0);
+
+/* see miniport.c */
+extern LIST_ENTRY OrphanAdapterListHead;
+extern KSPIN_LOCK OrphanAdapterListLock;
+
+
+BOOLEAN NdisFindDevicePci(UINT VendorID, UINT DeviceID, PUINT BusNumber, PUINT SlotNumber)
+/*
+ * FUNCTION: Find a PCI device given its Vendor and Device IDs
+ * ARGUMENTS:
+ *     VendorID: The card's PCI Vendor ID
+ *     DeviceID: The card's PCI Device ID
+ *     BusNumber: The card's bus number on success
+ *     SlotNumber: The card's slot number on success
+ * RETURNS:
+ *     TRUE if the card is fouund
+ *     FALSE Otherwise
+ * NOTES:
+ *     - This only finds the first card of a type
+ *     - This doesn't handle function enumeration correctly
+ *     - Based loosely on Dekker & Newcomer examples
+ */
+{
+  PCI_COMMON_CONFIG PciData;
+  int i, j, k;
+
+  ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+  /* dekker says there are 256 possible PCI buses */
+  for(i = 0; i < 256; i++)
+    {
+      for(j = 0; j < PCI_MAX_DEVICES; j++)
+        {
+          for(k = 0; k < PCI_MAX_FUNCTION; k++)
+            {
+              /* TODO: figure out what to do with k */
+
+              if(HalGetBusData(PCIConfiguration, i, j, &PciData, sizeof(PciData)))
+                {
+                  if(PciData.VendorID == 0xffff) /* Is this handled right? */
+                    continue;
+
+                  if(PciData.VendorID == VendorID && PciData.DeviceID == DeviceID)
+                    {
+                      if(BusNumber) 
+                        *BusNumber = i;
+                      if(SlotNumber) 
+                        *SlotNumber = j;
+
+                      return TRUE;
+                    }
+                }
+            }
+        }
+    }
+
+  NDIS_DbgPrint(MAX_TRACE, ("Didn't find device 0x%x:0x%x\n", VendorID, DeviceID));
+
+  return FALSE;
+}
+
+\f
+VOID NdisStartDriver(PUNICODE_STRING uBusName, PUNICODE_STRING uDeviceId, PUNICODE_STRING uServiceName)
+/*
+ * FUNCTION: Starts an NDIS driver
+ * ARGUMENTS:
+ *     uBusName: the card's bus type, by name, as extracted from the 
+ *               enumeration database in the registry
+ *     uDeviceId: the card's device ID
+ *     uServiceName: the driver (scm db entry) associated with the card
+ * NOTES:
+ *     - This doesn't prooperly handle multiple instances of the same card or driver
+ *     - for PCI cards, this finds the card and creates an "orphan" adapter object for
+ *       it.  This object is tagged with the registry key to the driver and includes
+ *       the slot number and bus number of the card.  NOTE that some stupid nic drivers
+ *       still depend on a registry key for slot number, so this isn't always enough.
+ *       Whatever the case, all of the card's resources are enumerated and assigned
+ *       via HalAssignSlotResources() and the orphan adapter is put onto the list. 
+ *       When the miniport calls NdisMRegisterMiniport(), ndis loops through the list
+ *       of orphans and associates any orphans that belong to that miniport with
+ *       its corresponding LOGICAL_ADAPTER objects.  
+ */
+{
+  ULONG            VendorId;
+  ULONG            DeviceId;
+  UINT             SlotNumber;
+  UINT             BusNumber;
+  UNICODE_STRING   Temp;
+  UNICODE_STRING   ServiceKey;
+  PWCHAR           ServiceKeyStr;
+  NTSTATUS         NtStatus;
+  PORPHAN_ADAPTER  OrphanAdapter;
+
+  NDIS_DbgPrint(MAX_TRACE, ("Called; Starting %wZ\n", uServiceName));
+
+  ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+  /* prepare a services key */
+  ServiceKeyStr = ExAllocatePool(PagedPool, sizeof(SERVICES_KEY) + sizeof(WCHAR) + uServiceName->Length);
+  if(!ServiceKeyStr)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient Resources.\n"));
+      return;
+    }
+
+  wcscpy(ServiceKeyStr, SERVICES_KEY);
+  wcscat(ServiceKeyStr, L"\\");
+  wcsncat(ServiceKeyStr, uServiceName->Buffer, uServiceName->Length/sizeof(WCHAR));
+  ServiceKeyStr[wcslen(SERVICES_KEY)+1+uServiceName->Length/sizeof(WCHAR)] = 0;
+
+  RtlInitUnicodeString(&ServiceKey, ServiceKeyStr);
+
+  if(!wcsncmp(uBusName->Buffer, L"PCI", uBusName->Length))
+  {
+    /*
+     * first see if a card with the requested id exists. 
+     * PCI IDs are formatted VEN_ABCD&DEV_ABCD[&...] 
+     */
+
+    if(wcsncmp(uDeviceId->Buffer, L"VEN_", 4))
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("Bogus uDeviceId parsing VEN\n"));
+        ExFreePool(ServiceKeyStr);
+        return;
+      }
+
+    Temp.Buffer = &(uDeviceId->Buffer[4]);                      /* offset of vendor id */
+    Temp.Length = Temp.MaximumLength = 4 * sizeof(WCHAR);       /* 4-digit id */
+
+    NtStatus = RtlUnicodeStringToInteger(&Temp, 16, &VendorId);
+    if(!NT_SUCCESS(NtStatus))
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("RtlUnicodeStringToInteger returned 0x%x\n", NtStatus));
+        ExFreePool(ServiceKeyStr);
+        return;
+      }
+
+    if(wcsncmp(&(uDeviceId->Buffer[9]), L"DEV_", 4))    
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("Bogus uDeviceId parsing DEV\n"));
+        ExFreePool(ServiceKeyStr);
+        return;
+      }
+
+    Temp.Buffer = &(uDeviceId->Buffer[13]);     /* offset of device id */
+    Temp.Length = 4 * sizeof(WCHAR);            /* 4-dight id */
+
+    NtStatus = RtlUnicodeStringToInteger(&Temp, 16, &DeviceId);
+    if(!NT_SUCCESS(NtStatus))
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("RtlUnicodeStringToInteger returned 0x%x\n", NtStatus));
+        ExFreePool(ServiceKeyStr);
+        return;
+      }
+
+    if(!NdisFindDevicePci(VendorId, DeviceId, &BusNumber, &SlotNumber))
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("Didn't find a configured card 0x%x 0x%x\n", VendorId, DeviceId));
+        ExFreePool(ServiceKeyStr);
+        return;
+      }
+
+    NDIS_DbgPrint(MAX_TRACE, ("Found a card 0x%x 0x%x at bus 0x%x slot 0x%x\n", VendorId, DeviceId, BusNumber, SlotNumber));
+
+    OrphanAdapter = ExAllocatePool(PagedPool, sizeof(ORPHAN_ADAPTER));
+    if(!OrphanAdapter)
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("Insufficient Resources.\n"));
+        ExFreePool(ServiceKeyStr);
+        return;
+      }
+
+    OrphanAdapter->RegistryPath.Buffer = ExAllocatePool(PagedPool, Temp.MaximumLength);
+    if(!OrphanAdapter->RegistryPath.Buffer)
+      {
+        NDIS_DbgPrint(MIN_TRACE, ("Insufficient Resources.\n"));
+        ExFreePool(ServiceKeyStr);
+        return;
+      }
+
+    OrphanAdapter->RegistryPath.Length = Temp.Length;
+    OrphanAdapter->RegistryPath.MaximumLength = Temp.MaximumLength;
+    memcpy(OrphanAdapter->RegistryPath.Buffer, Temp.Buffer, Temp.Length);
+
+    OrphanAdapter->BusType    = PCIBus; 
+    OrphanAdapter->BusNumber  = BusNumber;
+    OrphanAdapter->SlotNumber = SlotNumber;
+
+    ExInterlockedInsertTailList(&OrphanAdapterListHead, &OrphanAdapter->ListEntry, &OrphanAdapterListLock);
+  }
+
+  /*
+   * found a card; start its driver. this should be done from a 
+   * system thread, after NDIS.SYS's DriverEntry returns, but I 
+   * really don't know how to block on DriverEntry returning, so 
+   * what the hell.
+   */
+
+  NDIS_DbgPrint(MID_TRACE, ("Loading driver %wZ\n", &ServiceKey));
+  ZwLoadDriver(&ServiceKey);
+
+  ExFreePool(ServiceKeyStr);
+}
+
+\f
+VOID NdisStartDevices()
+/*
+ * FUNCTION: Find and start all NDIS Net class devices
+ * NOTES:
+ *     - Not sure if this handles multiple instances of the same
+ *       device or driver correctly yet
+ */
+{
+  HANDLE EnumHandle;
+  OBJECT_ATTRIBUTES ObjectAttributes;
+  UNICODE_STRING KeyName;
+  NTSTATUS NtStatus;
+  ULONG EnumIndex = 0;
+  ULONG ResultLength;
+  PKEY_BASIC_INFORMATION KeyInformation;
+  ULONG KeyInformationLength;
+
+  NDIS_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+  ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+
+  RtlInitUnicodeString(&KeyName, ENUM_KEY);
+  InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, 0, 0);
+  NtStatus = ZwOpenKey(&EnumHandle, KEY_ALL_ACCESS, &ObjectAttributes);
+  if(!NT_SUCCESS(NtStatus))
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Unable to open the Enum key\n"));
+      return;
+    }
+
+  NDIS_DbgPrint(MAX_TRACE, ("Opened the enum key\n"));
+
+  KeyInformation = ExAllocatePool(PagedPool, KEY_INFORMATION_SIZE);    // should be enough for most key names?
+  if(!KeyInformation)
+    {
+      NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+      return;
+    }
+
+  KeyInformationLength = KEY_INFORMATION_SIZE;
+
+  while(NT_SUCCESS(ZwEnumerateKey(EnumHandle, EnumIndex, 
+        KeyBasicInformation, KeyInformation, KeyInformationLength, &ResultLength)))
+    {
+      /* iterate through each enumerator (PCI, Root, ISAPNP, etc) */
+
+      HANDLE EnumeratorHandle;
+      WCHAR *EnumeratorStr;
+      UINT EnumeratorIndex = 0;
+
+      NDIS_DbgPrint(MAX_TRACE, ("Enum iteration 0x%x\n", EnumIndex));
+
+      EnumIndex++;
+
+      EnumeratorStr = ExAllocatePool(PagedPool, sizeof(WCHAR) + KeyInformation->NameLength);
+      if(!EnumeratorStr)
+        {
+          NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+          return;
+        }
+
+      wcsncpy(EnumeratorStr, KeyInformation->Name, KeyInformation->NameLength/sizeof(WCHAR));
+      EnumeratorStr[KeyInformation->NameLength/sizeof(WCHAR)] = 0;
+
+      RtlInitUnicodeString(&KeyName, EnumeratorStr);
+      InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, EnumHandle, 0);
+      if(!NT_SUCCESS(ZwOpenKey(&EnumeratorHandle, KEY_ALL_ACCESS, &ObjectAttributes)))
+        {
+          NDIS_DbgPrint(MIN_TRACE, ("Failed to open key %wZ\n", &KeyName));
+          return;
+        }
+        
+      while(NT_SUCCESS(ZwEnumerateKey(EnumeratorHandle, EnumeratorIndex, KeyBasicInformation,
+          KeyInformation, KeyInformationLength, &ResultLength)))
+        {
+          /* iterate through each device id */
+
+          HANDLE DeviceHandle;
+          WCHAR *DeviceStr;
+          UINT DeviceIndex = 0;
+          UNICODE_STRING BusName;
+
+          BusName.Buffer = KeyName.Buffer;
+          BusName.Length = KeyName.Length;
+          BusName.MaximumLength = KeyName.MaximumLength;
+
+          EnumeratorIndex++;
+
+          DeviceStr = ExAllocatePool(PagedPool, KeyInformation->NameLength + sizeof(WCHAR));
+          if(!DeviceStr)
+            {
+              NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+              return;
+            }
+
+          wcsncpy(DeviceStr, KeyInformation->Name, KeyInformation->NameLength/sizeof(WCHAR));
+          DeviceStr[KeyInformation->NameLength/sizeof(WCHAR)] = 0;
+
+          RtlInitUnicodeString(&KeyName, DeviceStr);
+          InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, EnumeratorHandle, 0);
+          if(!NT_SUCCESS(ZwOpenKey(&DeviceHandle, KEY_ALL_ACCESS, &ObjectAttributes)))
+            {
+              NDIS_DbgPrint(MIN_TRACE, ("Failed to open key %wZ\n", &KeyName));
+              return;
+            }
+
+          while(NT_SUCCESS(ZwEnumerateKey(DeviceHandle, DeviceIndex, KeyBasicInformation, 
+                KeyInformation, KeyInformationLength, &ResultLength)))
+            {
+              /* iterate through each instance id, starting drivers in the process */
+              HANDLE InstanceHandle;
+              WCHAR *InstanceStr;
+              UNICODE_STRING ValueName;
+              UNICODE_STRING ServiceName;
+              PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation;
+              ULONG KeyValueInformationLength;
+              UNICODE_STRING DeviceId;
+
+              DeviceId.Buffer = KeyName.Buffer;
+              DeviceId.Length = KeyName.Length;
+              DeviceId.MaximumLength = KeyName.MaximumLength;
+
+              DeviceIndex++;
+
+              InstanceStr = ExAllocatePool(PagedPool, KeyInformation->NameLength + sizeof(WCHAR));
+              if(!InstanceStr)
+                {
+                  NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+                  return;
+                }
+
+              wcsncpy(InstanceStr, KeyInformation->Name, KeyInformation->NameLength/sizeof(WCHAR));
+              InstanceStr[KeyInformation->NameLength/sizeof(WCHAR)] = 0;
+
+              NDIS_DbgPrint(MAX_TRACE, ("NameLength = 0x%x and InstanceStr: %ws\n", KeyInformation->NameLength, InstanceStr));
+
+              RtlInitUnicodeString(&KeyName, InstanceStr);
+              InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, DeviceHandle, 0);
+              if(!NT_SUCCESS(ZwOpenKey(&InstanceHandle, KEY_ALL_ACCESS, &ObjectAttributes)))
+                {
+                  NDIS_DbgPrint(MIN_TRACE, ("Failed to open key %wZ\n", &KeyName));
+                  return;
+                }
+
+              /* read class, looking for net guid */
+              RtlInitUnicodeString(&ValueName, L"ClassGUID");
+
+              NDIS_DbgPrint(MAX_TRACE, ("About to ask for 0x%x bytes\n", VALUE_INFORMATION_SIZE));
+
+              KeyValueInformation = ExAllocatePool(PagedPool, VALUE_INFORMATION_SIZE);
+              if(!KeyValueInformation)
+                {
+                  NDIS_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+                  return;
+                }
+
+              KeyValueInformationLength = VALUE_INFORMATION_SIZE;
+
+              NtStatus = ZwQueryValueKey(InstanceHandle, &ValueName, KeyValuePartialInformation, 
+                  KeyValueInformation, KeyValueInformationLength, &ResultLength);
+              if(!NT_SUCCESS(NtStatus))
+                {
+                  /* this isn't fatal, it just means that this device isn't ours */
+                  NDIS_DbgPrint(MID_TRACE, ("Failed to query value %wZ from key %wZ\n", &ValueName, &KeyName));
+                  continue;
+                }
+
+              if(!wcsncmp(NET_GUID, (PWCHAR)KeyValueInformation->Data, KeyValueInformation->DataLength/sizeof(WCHAR)))
+                {
+                  RtlInitUnicodeString(&ValueName, L"Service");
+
+                  NtStatus = ZwQueryValueKey(InstanceHandle, &ValueName, KeyValuePartialInformation, 
+                      KeyValueInformation, KeyValueInformationLength, &ResultLength);
+                  if(!NT_SUCCESS(NtStatus))
+                    {
+                      /* non-fatal also */
+                      NDIS_DbgPrint(MID_TRACE, ("Failed to query value %wZ from key %wZ\n", &ValueName, &KeyName));
+                      continue;
+                    }
+
+                  /* this is a net driver; start it */
+                  ServiceName.Length = ServiceName.MaximumLength = KeyValueInformation->DataLength;
+                  ServiceName.Buffer = (PWCHAR)KeyValueInformation->Data;
+                  NdisStartDriver(&BusName, &DeviceId, &ServiceName);
+                }
+              else
+                NDIS_DbgPrint(MAX_TRACE, ("...this device is not ours\n"));
+
+              ExFreePool(KeyValueInformation);
+              ExFreePool(InstanceStr);
+              ZwClose(InstanceHandle);
+            }
+
+          ExFreePool(DeviceStr);
+          ZwClose(DeviceHandle);
+        }
+
+      ExFreePool(EnumeratorStr);
+      ZwClose(EnumeratorHandle);
+    }
+
+  ZwClose(EnumHandle);
+  ExFreePool(KeyInformation);
+}
+