[PCI]
[reactos.git] / reactos / drivers / bus / pci / fdo.c
index 5a4baf5..b6753e1 100644 (file)
-/* $Id: fdo.c,v 1.4 2003/11/24 16:15:00 gvg Exp $\r
- *\r
- * PROJECT:         ReactOS PCI bus driver\r
- * FILE:            fdo.c\r
- * PURPOSE:         PCI device object dispatch routines\r
- * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)\r
- * UPDATE HISTORY:\r
- *      10-09-2001  CSH  Created\r
- */\r
-#include <pci.h>\r
-\r
-#define NDEBUG\r
-#include <debug.h>\r
-\r
-/*** PRIVATE *****************************************************************/\r
-\r
-NTSTATUS\r
-FdoLocateChildDevice(\r
-  PPCI_DEVICE *Device,\r
-  PFDO_DEVICE_EXTENSION DeviceExtension,\r
-  PPCI_COMMON_CONFIG PciConfig)\r
-{\r
-  PLIST_ENTRY CurrentEntry;\r
-  PPCI_DEVICE CurrentDevice;\r
-\r
-  CurrentEntry = DeviceExtension->DeviceListHead.Flink;\r
-  while (CurrentEntry != &DeviceExtension->DeviceListHead) {\r
-    CurrentDevice = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);\r
-\r
-    /* If both vendor ID and device ID match, it is the same device */\r
-    if ((PciConfig->VendorID == CurrentDevice->PciConfig.VendorID) &&\r
-      (PciConfig->DeviceID == CurrentDevice->PciConfig.DeviceID)) {\r
-      *Device = CurrentDevice;\r
-      return STATUS_SUCCESS;\r
-    }\r
-\r
-    CurrentEntry = CurrentEntry->Flink;\r
-  }\r
-\r
-  *Device = NULL;\r
-  return STATUS_UNSUCCESSFUL;\r
-}\r
-\r
-\r
-NTSTATUS\r
-FdoEnumerateDevices(\r
-  PDEVICE_OBJECT DeviceObject)\r
-{\r
-  PFDO_DEVICE_EXTENSION DeviceExtension;\r
-  PCI_COMMON_CONFIG PciConfig;\r
-  PLIST_ENTRY CurrentEntry;\r
-  PPCI_DEVICE Device;\r
-  NTSTATUS Status;\r
-  ULONG Slot;\r
-  ULONG Size;\r
-\r
-  DPRINT("Called\n");\r
-\r
-  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
-  /* Mark all devices to be removed. If we don't discover them again during\r
-     enumeration, assume that they have been surprise removed */\r
-  CurrentEntry = DeviceExtension->DeviceListHead.Flink;\r
-  while (CurrentEntry != &DeviceExtension->DeviceListHead) {\r
-    Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);\r
-    Device->RemovePending = TRUE;\r
-    CurrentEntry = CurrentEntry->Flink;\r
-  }\r
-\r
-  DeviceExtension->DeviceListCount = 0;\r
-\r
-  /* Enumerate devices on the PCI bus */\r
-  for (Slot = 0; Slot < 256; Slot++)\r
-       {\r
-         Size = PciGetBusData(\r
-      DeviceExtension->BusNumber,\r
-                       Slot,\r
-                       &PciConfig,\r
-      0,\r
-                       sizeof(PCI_COMMON_CONFIG));\r
-         if (Size != 0)\r
-    {\r
-      DPRINT("Bus %1lu  Device %2lu  Func %1lu  VenID 0x%04hx  DevID 0x%04hx\n",\r
-                       DeviceExtension->BusNumber,\r
-                       Slot>>3,\r
-                       Slot & 0x07,\r
-                       PciConfig.VendorID,\r
-                       PciConfig.DeviceID);\r
-\r
-      Status = FdoLocateChildDevice(&Device, DeviceExtension, &PciConfig);\r
-      if (!NT_SUCCESS(Status)) {\r
-        Device = (PPCI_DEVICE)ExAllocatePool(PagedPool, sizeof(PCI_DEVICE));\r
-        if (!Device)\r
-        {\r
-          /* FIXME: Cleanup resources for already discovered devices */\r
-          return STATUS_INSUFFICIENT_RESOURCES;\r
-        }\r
-\r
-        RtlZeroMemory(Device, sizeof(PCI_DEVICE));\r
-\r
-        RtlMoveMemory(&Device->PciConfig, &PciConfig, sizeof(PCI_COMMON_CONFIG));\r
-\r
-        ExInterlockedInsertTailList(\r
-          &DeviceExtension->DeviceListHead,\r
-          &Device->ListEntry,\r
-          &DeviceExtension->DeviceListLock);\r
-      }\r
-\r
-      /* Don't remove this device */\r
-      Device->RemovePending = FALSE;\r
-\r
-      DeviceExtension->DeviceListCount++;\r
-    }\r
-       }\r
-\r
-  return STATUS_SUCCESS;\r
-}\r
-\r
-\r
-NTSTATUS\r
-FdoQueryBusRelations(\r
-  IN PDEVICE_OBJECT DeviceObject,\r
-  IN PIRP Irp,\r
-  PIO_STACK_LOCATION IrpSp)\r
-{\r
-  PPDO_DEVICE_EXTENSION PdoDeviceExtension;\r
-  PFDO_DEVICE_EXTENSION DeviceExtension;\r
-  PDEVICE_RELATIONS Relations;\r
-  PLIST_ENTRY CurrentEntry;\r
-  PPCI_DEVICE Device;\r
-  NTSTATUS Status;\r
-  BOOLEAN ErrorOccurred;\r
-  NTSTATUS ErrorStatus;\r
-  WCHAR Buffer[MAX_PATH];\r
-  ULONG Size;\r
-  ULONG i;\r
-\r
-  DPRINT("Called\n");\r
-\r
-  ErrorStatus = STATUS_INSUFFICIENT_RESOURCES;\r
-\r
-  Status = STATUS_SUCCESS;\r
-\r
-  ErrorOccurred = FALSE;\r
-\r
-  FdoEnumerateDevices(DeviceObject);\r
-\r
-  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
-  if (Irp->IoStatus.Information) {\r
-    /* FIXME: Another bus driver has already created a DEVICE_RELATIONS \r
-              structure so we must merge this structure with our own */\r
-  }\r
-\r
-  Size = sizeof(DEVICE_RELATIONS) + sizeof(Relations->Objects) *\r
-    (DeviceExtension->DeviceListCount - 1);\r
-  Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, Size);\r
-  if (!Relations)\r
-    return STATUS_INSUFFICIENT_RESOURCES;\r
-\r
-  Relations->Count = DeviceExtension->DeviceListCount;\r
-\r
-  i = 0;\r
-  CurrentEntry = DeviceExtension->DeviceListHead.Flink;\r
-  while (CurrentEntry != &DeviceExtension->DeviceListHead) {\r
-    Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);\r
-\r
-    PdoDeviceExtension = NULL;\r
-\r
-    if (!Device->Pdo) {\r
-      /* Create a physical device object for the\r
-         device as it does not already have one */\r
-      Status = IoCreateDevice(\r
-        DeviceObject->DriverObject,\r
-        sizeof(PDO_DEVICE_EXTENSION),\r
-        NULL,\r
-        FILE_DEVICE_CONTROLLER,\r
-        0,\r
-        FALSE,\r
-        &Device->Pdo);\r
-      if (!NT_SUCCESS(Status)) {\r
-        DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);\r
-        ErrorStatus = Status;\r
-        ErrorOccurred = TRUE;\r
-        break;\r
-      }\r
-\r
-      Device->Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;\r
-\r
-      Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;\r
-\r
-      //Device->Pdo->Flags |= DO_POWER_PAGABLE;\r
-\r
-      PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;\r
-\r
-      RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));\r
-\r
-      PdoDeviceExtension->Common.IsFDO = FALSE;\r
-\r
-      PdoDeviceExtension->Common.DeviceObject = Device->Pdo;\r
-\r
-      PdoDeviceExtension->Common.DevicePowerState = PowerDeviceD0;\r
-\r
-      /* FIXME: Get device properties (Hardware IDs, etc.) */\r
-\r
-      swprintf(\r
-        Buffer,\r
-        L"PCI\\VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X",\r
-        Device->PciConfig.VendorID,\r
-        Device->PciConfig.DeviceID,\r
-        (Device->PciConfig.u.type0.SubSystemID << 16) +\r
-        Device->PciConfig.u.type0.SubVendorID,\r
-        Device->PciConfig.RevisionID);\r
-\r
-      if (!PciCreateUnicodeString(\r
-        &PdoDeviceExtension->DeviceID,\r
-        Buffer,\r
-        PagedPool)) {\r
-        ErrorOccurred = TRUE;\r
-        break;\r
-      }\r
-\r
-      DPRINT("DeviceID: %S\n", PdoDeviceExtension->DeviceID.Buffer);\r
-    }\r
-\r
-    if (!Device->RemovePending) {\r
-      /* Reference the physical device object. The PnP manager\r
-         will dereference it again when it is no longer needed */\r
-      ObReferenceObject(Device->Pdo);\r
-\r
-      Relations->Objects[i] = Device->Pdo;\r
-\r
-      i++;\r
-    }\r
-\r
-    CurrentEntry = CurrentEntry->Flink;\r
-  }\r
-\r
-  if (ErrorOccurred) {\r
-    /* FIXME: Cleanup all new PDOs created in this call. Please give me SEH!!! ;-) */\r
-    /* FIXME: Should IoAttachDeviceToDeviceStack() be undone? */\r
-    if (PdoDeviceExtension) {\r
-      RtlFreeUnicodeString(&PdoDeviceExtension->DeviceID);\r
-      ExFreePool(PdoDeviceExtension);\r
-    }\r
-\r
-    ExFreePool(Relations);\r
-    return ErrorStatus;\r
-  }\r
-\r
-  Irp->IoStatus.Information = (ULONG_PTR)Relations;\r
-\r
-  return Status;\r
-}\r
-\r
-\r
-NTSTATUS\r
-FdoStartDevice(\r
-  IN PDEVICE_OBJECT DeviceObject,\r
-  IN PIRP Irp)\r
-{\r
-  PFDO_DEVICE_EXTENSION DeviceExtension;\r
-\r
-  DPRINT("Called\n");\r
-\r
-  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
-  assert(DeviceExtension->State == dsStopped);\r
-\r
-  InitializeListHead(&DeviceExtension->DeviceListHead);\r
-  KeInitializeSpinLock(&DeviceExtension->DeviceListLock);\r
-  DeviceExtension->DeviceListCount = 0;\r
-\r
-  PciBusConfigType = PciGetBusConfigType();\r
-\r
-  DPRINT("Bus configuration is %d\n", PciBusConfigType);\r
-\r
-  if (PciBusConfigType != pbtUnknown) {\r
-    /* At least one PCI bus is found */\r
-  }\r
-\r
-  /* FIXME: Find a way to get this information */\r
-  DeviceExtension->BusNumber = 0;\r
-\r
-  DeviceExtension->State = dsStarted;\r
-\r
-  //Irp->IoStatus.Information = 0;\r
-\r
-       return STATUS_SUCCESS;\r
-}\r
-\r
-\r
-NTSTATUS\r
-FdoSetPower(\r
-  IN PDEVICE_OBJECT DeviceObject,\r
-  IN PIRP Irp,\r
-  PIO_STACK_LOCATION IrpSp)\r
-{\r
-  PFDO_DEVICE_EXTENSION DeviceExtension;\r
-  NTSTATUS Status;\r
-\r
-  DPRINT("Called\n");\r
-\r
-  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
-  if (IrpSp->Parameters.Power.Type == DevicePowerState) {\r
-    /* FIXME: Set device power state for the device */\r
-    Status = STATUS_UNSUCCESSFUL;\r
-  } else {\r
-    Status = STATUS_UNSUCCESSFUL;\r
-  }\r
-\r
-  return Status;\r
-}\r
-\r
-\r
-/*** PUBLIC ******************************************************************/\r
-\r
-NTSTATUS\r
-FdoPnpControl(\r
-  PDEVICE_OBJECT DeviceObject,\r
-  PIRP Irp)\r
-/*\r
- * FUNCTION: Handle Plug and Play IRPs for the PCI device object\r
- * ARGUMENTS:\r
- *     DeviceObject = Pointer to functional device object of the PCI driver\r
- *     Irp          = Pointer to IRP that should be handled\r
- * RETURNS:\r
- *     Status\r
- */\r
-{\r
-  PFDO_DEVICE_EXTENSION DeviceExtension;\r
-  PIO_STACK_LOCATION IrpSp;\r
-  NTSTATUS Status;\r
-\r
-  DPRINT("Called\n");\r
-\r
-  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;\r
-\r
-  IrpSp = IoGetCurrentIrpStackLocation(Irp);\r
-  switch (IrpSp->MinorFunction) {\r
-#if 0\r
-  case IRP_MN_CANCEL_REMOVE_DEVICE:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-\r
-  case IRP_MN_CANCEL_STOP_DEVICE:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-\r
-  case IRP_MN_DEVICE_USAGE_NOTIFICATION:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-\r
-  case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-#endif\r
-  case IRP_MN_QUERY_DEVICE_RELATIONS:\r
-    Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);\r
-    break;\r
-#if 0\r
-  case IRP_MN_QUERY_PNP_DEVICE_STATE:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-\r
-  case IRP_MN_QUERY_REMOVE_DEVICE:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-\r
-  case IRP_MN_QUERY_STOP_DEVICE:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-\r
-  case IRP_MN_REMOVE_DEVICE:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-#endif\r
-  case IRP_MN_START_DEVICE:\r
-    DPRINT("IRP_MN_START_DEVICE received\n");\r
-    Status = FdoStartDevice(DeviceObject, Irp);\r
-    break;\r
-  case IRP_MN_STOP_DEVICE:\r
-    /* Currently not supported */\r
-    Status = STATUS_UNSUCCESSFUL;\r
-    break;\r
-#if 0\r
-  case IRP_MN_SURPRISE_REMOVAL:\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-#endif\r
-  default:\r
-    DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);\r
-\r
-    /*\r
-     * Do NOT complete the IRP as it will be processed by the lower\r
-     * device object, which will complete the IRP\r
-     */\r
-    IoSkipCurrentIrpStackLocation(Irp);\r
-    Status = IoCallDriver(DeviceExtension->Ldo, Irp);\r
-    return Status;\r
-    break;\r
-  }\r
-\r
-\r
-  if (Status != STATUS_PENDING) {\r
-    if (Status != STATUS_NOT_IMPLEMENTED)\r
-      Irp->IoStatus.Status = Status;\r
-    IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
-  }\r
-\r
-  DPRINT("Leaving. Status 0x%X\n", Status);\r
-\r
-  return Status;\r
-}\r
-\r
-\r
-NTSTATUS\r
-FdoPowerControl(\r
-  PDEVICE_OBJECT DeviceObject,\r
-  PIRP Irp)\r
-/*\r
- * FUNCTION: Handle power management IRPs for the PCI device object\r
- * ARGUMENTS:\r
- *     DeviceObject = Pointer to functional device object of the PCI driver\r
- *     Irp          = Pointer to IRP that should be handled\r
- * RETURNS:\r
- *     Status\r
- */\r
-{\r
-  PIO_STACK_LOCATION IrpSp;\r
-  NTSTATUS Status;\r
-\r
-  DPRINT("Called\n");\r
-\r
-  IrpSp = IoGetCurrentIrpStackLocation(Irp);\r
-\r
-  switch (IrpSp->MinorFunction) {\r
-  case IRP_MN_SET_POWER:\r
-    Status = FdoSetPower(DeviceObject, Irp, IrpSp);\r
-    break;\r
-\r
-  default:\r
-    DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);\r
-    Status = STATUS_NOT_IMPLEMENTED;\r
-    break;\r
-  }\r
-\r
-  if (Status != STATUS_PENDING) {\r
-    Irp->IoStatus.Status = Status;\r
-    IoCompleteRequest(Irp, IO_NO_INCREMENT);\r
-  }\r
-\r
-  DPRINT("Leaving. Status 0x%X\n", Status);\r
-\r
-  return Status;\r
-}\r
-\r
-/* EOF */\r
+/*
+ * PROJECT:         ReactOS PCI bus driver
+ * FILE:            fdo.c
+ * PURPOSE:         PCI device object dispatch routines
+ * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * UPDATE HISTORY:
+ *      10-09-2001  CSH  Created
+ */
+
+#include "pci.h"
+
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+#include <debug.h>
+
+/*** PRIVATE *****************************************************************/
+
+static IO_COMPLETION_ROUTINE ForwardIrpAndWaitCompletion;
+
+static NTSTATUS NTAPI
+ForwardIrpAndWaitCompletion(
+       IN PDEVICE_OBJECT DeviceObject,
+       IN PIRP Irp,
+       IN PVOID Context)
+{
+       UNREFERENCED_PARAMETER(DeviceObject);
+       if (Irp->PendingReturned)
+               KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
+       return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS NTAPI
+ForwardIrpAndWait(
+       IN PDEVICE_OBJECT DeviceObject,
+       IN PIRP Irp)
+{
+       KEVENT Event;
+       NTSTATUS Status;
+       PDEVICE_OBJECT LowerDevice = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Ldo;
+       ASSERT(LowerDevice);
+
+       KeInitializeEvent(&Event, NotificationEvent, FALSE);
+       IoCopyCurrentIrpStackLocationToNext(Irp);
+
+       IoSetCompletionRoutine(Irp, ForwardIrpAndWaitCompletion, &Event, TRUE, TRUE, TRUE);
+
+       Status = IoCallDriver(LowerDevice, Irp);
+       if (Status == STATUS_PENDING)
+       {
+               Status = KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
+               if (NT_SUCCESS(Status))
+                       Status = Irp->IoStatus.Status;
+       }
+
+       return Status;
+}
+
+static NTSTATUS
+FdoLocateChildDevice(
+  PPCI_DEVICE *Device,
+  PFDO_DEVICE_EXTENSION DeviceExtension,
+  PCI_SLOT_NUMBER SlotNumber,
+  PPCI_COMMON_CONFIG PciConfig)
+{
+  PLIST_ENTRY CurrentEntry;
+  PPCI_DEVICE CurrentDevice;
+
+  DPRINT("Called\n");
+
+  CurrentEntry = DeviceExtension->DeviceListHead.Flink;
+  while (CurrentEntry != &DeviceExtension->DeviceListHead) {
+    CurrentDevice = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
+
+    /* If both vendor ID and device ID match, it is the same device */
+    if ((PciConfig->VendorID == CurrentDevice->PciConfig.VendorID) &&
+        (PciConfig->DeviceID == CurrentDevice->PciConfig.DeviceID) &&
+        (SlotNumber.u.AsULONG == CurrentDevice->SlotNumber.u.AsULONG)) {
+      *Device = CurrentDevice;
+      DPRINT("Done\n");
+      return STATUS_SUCCESS;
+    }
+
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  *Device = NULL;
+  DPRINT("Done\n");
+  return STATUS_UNSUCCESSFUL;
+}
+
+
+static NTSTATUS
+FdoEnumerateDevices(
+  PDEVICE_OBJECT DeviceObject)
+{
+  PFDO_DEVICE_EXTENSION DeviceExtension;
+  PCI_COMMON_CONFIG PciConfig;
+  PLIST_ENTRY CurrentEntry;
+  PPCI_DEVICE Device;
+  PCI_SLOT_NUMBER SlotNumber;
+  ULONG DeviceNumber;
+  ULONG FunctionNumber;
+  ULONG Size;
+  NTSTATUS Status;
+
+  DPRINT("Called\n");
+
+  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+  /* Mark all devices to be removed. If we don't discover them again during
+     enumeration, assume that they have been surprise removed */
+  CurrentEntry = DeviceExtension->DeviceListHead.Flink;
+  while (CurrentEntry != &DeviceExtension->DeviceListHead) {
+    Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
+    Device->RemovePending = TRUE;
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  DeviceExtension->DeviceListCount = 0;
+
+  /* Enumerate devices on the PCI bus */
+  SlotNumber.u.AsULONG = 0;
+  for (DeviceNumber = 0; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++)
+  {
+    SlotNumber.u.bits.DeviceNumber = DeviceNumber;
+    for (FunctionNumber = 0; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++)
+    {
+      SlotNumber.u.bits.FunctionNumber = FunctionNumber;
+
+      DPRINT("Bus %1lu  Device %2lu  Func %1lu\n",
+        DeviceExtension->BusNumber,
+        DeviceNumber,
+        FunctionNumber);
+
+      RtlZeroMemory(&PciConfig,
+                    sizeof(PCI_COMMON_CONFIG));
+
+      Size = HalGetBusData(PCIConfiguration,
+                           DeviceExtension->BusNumber,
+                           SlotNumber.u.AsULONG,
+                           &PciConfig,
+                           PCI_COMMON_HDR_LENGTH);
+      DPRINT("Size %lu\n", Size);
+      if (Size < PCI_COMMON_HDR_LENGTH)
+      {
+        if (FunctionNumber == 0)
+        {
+          break;
+        }
+        else
+        {
+          continue;
+        }
+      }
+
+      DPRINT("Bus %1lu  Device %2lu  Func %1lu  VenID 0x%04hx  DevID 0x%04hx\n",
+        DeviceExtension->BusNumber,
+        DeviceNumber,
+        FunctionNumber,
+        PciConfig.VendorID,
+        PciConfig.DeviceID);
+
+      Status = FdoLocateChildDevice(&Device, DeviceExtension, SlotNumber, &PciConfig);
+      if (!NT_SUCCESS(Status))
+      {
+        Device = (PPCI_DEVICE)ExAllocatePoolWithTag(NonPagedPool, sizeof(PCI_DEVICE),TAG_PCI);
+        if (!Device)
+        {
+          /* FIXME: Cleanup resources for already discovered devices */
+          return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        RtlZeroMemory(Device,
+                      sizeof(PCI_DEVICE));
+
+        Device->BusNumber = DeviceExtension->BusNumber;
+
+        RtlCopyMemory(&Device->SlotNumber,
+                      &SlotNumber,
+                      sizeof(PCI_SLOT_NUMBER));
+
+        RtlCopyMemory(&Device->PciConfig,
+                      &PciConfig,
+                      sizeof(PCI_COMMON_CONFIG));
+
+        ExInterlockedInsertTailList(
+          &DeviceExtension->DeviceListHead,
+          &Device->ListEntry,
+          &DeviceExtension->DeviceListLock);
+      }
+
+      /* Don't remove this device */
+      Device->RemovePending = FALSE;
+
+      DeviceExtension->DeviceListCount++;
+
+      /* Skip to next device if the current one is not a multifunction device */
+      if ((FunctionNumber == 0) &&
+          ((PciConfig.HeaderType & 0x80) == 0))
+      {
+        break;
+      }
+    }
+  }
+
+  DPRINT("Done\n");
+
+  return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+FdoQueryBusRelations(
+  IN PDEVICE_OBJECT DeviceObject,
+  IN PIRP Irp,
+  PIO_STACK_LOCATION IrpSp)
+{
+  PPDO_DEVICE_EXTENSION PdoDeviceExtension = NULL;
+  PFDO_DEVICE_EXTENSION DeviceExtension;
+  PDEVICE_RELATIONS Relations;
+  PLIST_ENTRY CurrentEntry;
+  PPCI_DEVICE Device;
+  NTSTATUS Status;
+  BOOLEAN ErrorOccurred;
+  NTSTATUS ErrorStatus;
+  ULONG Size;
+  ULONG i;
+
+  UNREFERENCED_PARAMETER(IrpSp);
+
+  DPRINT("Called\n");
+
+  ErrorStatus = STATUS_INSUFFICIENT_RESOURCES;
+
+  Status = STATUS_SUCCESS;
+
+  ErrorOccurred = FALSE;
+
+  FdoEnumerateDevices(DeviceObject);
+
+  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+  if (Irp->IoStatus.Information) {
+    /* FIXME: Another bus driver has already created a DEVICE_RELATIONS
+              structure so we must merge this structure with our own */
+  }
+
+  Size = sizeof(DEVICE_RELATIONS) + sizeof(Relations->Objects) *
+    (DeviceExtension->DeviceListCount - 1);
+  Relations = (PDEVICE_RELATIONS)ExAllocatePool(PagedPool, Size);
+  if (!Relations)
+    return STATUS_INSUFFICIENT_RESOURCES;
+
+  Relations->Count = DeviceExtension->DeviceListCount;
+
+  i = 0;
+  CurrentEntry = DeviceExtension->DeviceListHead.Flink;
+  while (CurrentEntry != &DeviceExtension->DeviceListHead) {
+    Device = CONTAINING_RECORD(CurrentEntry, PCI_DEVICE, ListEntry);
+
+    PdoDeviceExtension = NULL;
+
+    if (!Device->Pdo) {
+      /* Create a physical device object for the
+         device as it does not already have one */
+      Status = IoCreateDevice(
+        DeviceObject->DriverObject,
+        sizeof(PDO_DEVICE_EXTENSION),
+        NULL,
+        FILE_DEVICE_CONTROLLER,
+        FILE_AUTOGENERATED_DEVICE_NAME,
+        FALSE,
+        &Device->Pdo);
+      if (!NT_SUCCESS(Status)) {
+        DPRINT("IoCreateDevice() failed with status 0x%X\n", Status);
+        ErrorStatus = Status;
+        ErrorOccurred = TRUE;
+        break;
+      }
+
+      Device->Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
+
+      //Device->Pdo->Flags |= DO_POWER_PAGABLE;
+
+      PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Device->Pdo->DeviceExtension;
+
+      RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
+
+      PdoDeviceExtension->Common.IsFDO = FALSE;
+
+      PdoDeviceExtension->Common.DeviceObject = Device->Pdo;
+
+      PdoDeviceExtension->Common.DevicePowerState = PowerDeviceD0;
+
+      PdoDeviceExtension->Fdo = DeviceObject;
+
+      PdoDeviceExtension->PciDevice = Device;
+
+      /* Add Device ID string */
+      Status = PciCreateDeviceIDString(&PdoDeviceExtension->DeviceID, Device);
+      if (!NT_SUCCESS(Status))
+      {
+        ErrorStatus = Status;
+        ErrorOccurred = TRUE;
+        break;
+      }
+
+      DPRINT("DeviceID: %S\n", PdoDeviceExtension->DeviceID.Buffer);
+
+      /* Add Instance ID string */
+      Status = PciCreateInstanceIDString(&PdoDeviceExtension->InstanceID, Device);
+      if (!NT_SUCCESS(Status))
+      {
+        ErrorStatus = Status;
+        ErrorOccurred = TRUE;
+        break;
+      }
+
+      /* Add Hardware IDs string */
+      Status = PciCreateHardwareIDsString(&PdoDeviceExtension->HardwareIDs, Device);
+      if (!NT_SUCCESS(Status))
+      {
+        ErrorStatus = Status;
+        ErrorOccurred = TRUE;
+        break;
+      }
+
+      /* Add Compatible IDs string */
+      Status = PciCreateCompatibleIDsString(&PdoDeviceExtension->CompatibleIDs, Device);
+      if (!NT_SUCCESS(Status))
+      {
+        ErrorStatus = Status;
+        ErrorOccurred = TRUE;
+        break;
+      }
+
+      /* Add device description string */
+      Status = PciCreateDeviceDescriptionString(&PdoDeviceExtension->DeviceDescription, Device);
+      if (!NT_SUCCESS(Status))
+      {
+        ErrorStatus = Status;
+        ErrorOccurred = TRUE;
+        break;
+      }
+
+      /* Add device location string */
+      Status = PciCreateDeviceLocationString(&PdoDeviceExtension->DeviceLocation, Device);
+      if (!NT_SUCCESS(Status))
+      {
+        ErrorStatus = Status;
+        ErrorOccurred = TRUE;
+        break;
+      }
+    }
+
+    if (!Device->RemovePending) {
+      /* Reference the physical device object. The PnP manager
+         will dereference it again when it is no longer needed */
+      ObReferenceObject(Device->Pdo);
+
+      Relations->Objects[i] = Device->Pdo;
+
+      i++;
+    }
+
+    CurrentEntry = CurrentEntry->Flink;
+  }
+
+  if (ErrorOccurred) {
+    /* FIXME: Cleanup all new PDOs created in this call. Please give me SEH!!! ;-) */
+    /* FIXME: Should IoAttachDeviceToDeviceStack() be undone? */
+    if (PdoDeviceExtension) {
+      RtlFreeUnicodeString(&PdoDeviceExtension->DeviceID);
+      RtlFreeUnicodeString(&PdoDeviceExtension->InstanceID);
+      RtlFreeUnicodeString(&PdoDeviceExtension->HardwareIDs);
+      RtlFreeUnicodeString(&PdoDeviceExtension->CompatibleIDs);
+      RtlFreeUnicodeString(&PdoDeviceExtension->DeviceDescription);
+      RtlFreeUnicodeString(&PdoDeviceExtension->DeviceLocation);
+    }
+
+    ExFreePool(Relations);
+    return ErrorStatus;
+  }
+
+  Irp->IoStatus.Information = (ULONG_PTR)Relations;
+
+  DPRINT("Done\n");
+
+  return Status;
+}
+
+
+static NTSTATUS
+FdoStartDevice(
+  IN PDEVICE_OBJECT DeviceObject,
+  IN PIRP Irp)
+{
+  PFDO_DEVICE_EXTENSION DeviceExtension;
+  PCM_RESOURCE_LIST AllocatedResources;
+  PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
+  ULONG FoundBusNumber = FALSE;
+  ULONG i;
+
+  DPRINT("Called\n");
+
+  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+  AllocatedResources = IoGetCurrentIrpStackLocation(Irp)->Parameters.StartDevice.AllocatedResources;
+  if (!AllocatedResources)
+  {
+    DPRINT("No allocated resources sent to driver\n");
+    return STATUS_INSUFFICIENT_RESOURCES;
+  }
+  if (AllocatedResources->Count < 1)
+  {
+    DPRINT("Not enough allocated resources sent to driver\n");
+    return STATUS_INSUFFICIENT_RESOURCES;
+  }
+  if (AllocatedResources->List[0].PartialResourceList.Version != 1
+    || AllocatedResources->List[0].PartialResourceList.Revision != 1)
+    return STATUS_REVISION_MISMATCH;
+
+  ASSERT(DeviceExtension->State == dsStopped);
+
+  /* By default, use the bus number in the resource list header */
+  DeviceExtension->BusNumber = AllocatedResources->List[0].BusNumber;
+
+  for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
+  {
+    ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
+    switch (ResourceDescriptor->Type)
+    {
+      case CmResourceTypeBusNumber:
+      {
+        if (FoundBusNumber || ResourceDescriptor->u.BusNumber.Length != 1)
+          return STATUS_INVALID_PARAMETER;
+        /* Use this one instead */
+        ASSERT(AllocatedResources->List[0].BusNumber == ResourceDescriptor->u.BusNumber.Start);
+        DeviceExtension->BusNumber = ResourceDescriptor->u.BusNumber.Start;
+        DPRINT("Found bus number resource: %lu\n", DeviceExtension->BusNumber);
+        FoundBusNumber = TRUE;
+        break;
+      }
+      default:
+        DPRINT("Unknown resource descriptor type 0x%x\n", ResourceDescriptor->Type);
+    }
+  }
+
+  InitializeListHead(&DeviceExtension->DeviceListHead);
+  KeInitializeSpinLock(&DeviceExtension->DeviceListLock);
+  DeviceExtension->DeviceListCount = 0;
+  DeviceExtension->State = dsStarted;
+
+  ExInterlockedInsertTailList(
+    &DriverExtension->BusListHead,
+    &DeviceExtension->ListEntry,
+    &DriverExtension->BusListLock);
+
+  Irp->IoStatus.Information = 0;
+
+  return STATUS_SUCCESS;
+}
+
+
+static NTSTATUS
+FdoSetPower(
+  IN PDEVICE_OBJECT DeviceObject,
+  IN PIRP Irp,
+  PIO_STACK_LOCATION IrpSp)
+{
+  PFDO_DEVICE_EXTENSION DeviceExtension;
+  NTSTATUS Status;
+
+  UNREFERENCED_PARAMETER(Irp);
+
+  DPRINT("Called\n");
+
+  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+  if (IrpSp->Parameters.Power.Type == DevicePowerState) {
+    /* FIXME: Set device power state for the device */
+    Status = STATUS_UNSUCCESSFUL;
+  } else {
+    Status = STATUS_UNSUCCESSFUL;
+  }
+
+  return Status;
+}
+
+
+/*** PUBLIC ******************************************************************/
+
+NTSTATUS
+FdoPnpControl(
+  PDEVICE_OBJECT DeviceObject,
+  PIRP Irp)
+/*
+ * FUNCTION: Handle Plug and Play IRPs for the PCI device object
+ * ARGUMENTS:
+ *     DeviceObject = Pointer to functional device object of the PCI driver
+ *     Irp          = Pointer to IRP that should be handled
+ * RETURNS:
+ *     Status
+ */
+{
+  PFDO_DEVICE_EXTENSION DeviceExtension;
+  PIO_STACK_LOCATION IrpSp;
+  NTSTATUS Status = Irp->IoStatus.Status;
+
+  DPRINT("Called\n");
+
+  DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+  IrpSp = IoGetCurrentIrpStackLocation(Irp);
+  switch (IrpSp->MinorFunction) {
+#if 0
+  case IRP_MN_CANCEL_REMOVE_DEVICE:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+
+  case IRP_MN_CANCEL_STOP_DEVICE:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+
+  case IRP_MN_DEVICE_USAGE_NOTIFICATION:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+
+  case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+#endif
+  case IRP_MN_QUERY_DEVICE_RELATIONS:
+    if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations)
+        break;
+
+    Status = FdoQueryBusRelations(DeviceObject, Irp, IrpSp);
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+#if 0
+  case IRP_MN_QUERY_PNP_DEVICE_STATE:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+
+  case IRP_MN_QUERY_REMOVE_DEVICE:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+
+  case IRP_MN_QUERY_STOP_DEVICE:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+
+  case IRP_MN_REMOVE_DEVICE:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+#endif
+  case IRP_MN_START_DEVICE:
+    DPRINT("IRP_MN_START_DEVICE received\n");
+    Status = ForwardIrpAndWait(DeviceObject, Irp);
+    if (NT_SUCCESS(Status))
+       Status = FdoStartDevice(DeviceObject, Irp);
+
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+  case IRP_MN_STOP_DEVICE:
+    /* Currently not supported */
+    Status = STATUS_UNSUCCESSFUL;
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+#if 0
+  case IRP_MN_SURPRISE_REMOVAL:
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+#endif
+  case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:
+    break;
+  case IRP_MN_REMOVE_DEVICE:
+    DPRINT1("IRP_MN_REMOVE_DEVICE is UNIMPLEMENTED!\n");
+    break;
+  default:
+    DPRINT1("Unknown IOCTL 0x%lx\n", IrpSp->MinorFunction);
+    break;
+  }
+
+  Irp->IoStatus.Status = Status;
+  IoSkipCurrentIrpStackLocation(Irp);
+  Status = IoCallDriver(DeviceExtension->Ldo, Irp);
+
+  DPRINT("Leaving. Status 0x%X\n", Status);
+
+  return Status;
+}
+
+
+NTSTATUS
+FdoPowerControl(
+  PDEVICE_OBJECT DeviceObject,
+  PIRP Irp)
+/*
+ * FUNCTION: Handle power management IRPs for the PCI device object
+ * ARGUMENTS:
+ *     DeviceObject = Pointer to functional device object of the PCI driver
+ *     Irp          = Pointer to IRP that should be handled
+ * RETURNS:
+ *     Status
+ */
+{
+  PIO_STACK_LOCATION IrpSp;
+  NTSTATUS Status;
+
+  DPRINT("Called\n");
+
+  IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+  switch (IrpSp->MinorFunction) {
+  case IRP_MN_SET_POWER:
+    Status = FdoSetPower(DeviceObject, Irp, IrpSp);
+    break;
+
+  default:
+    DPRINT("Unknown IOCTL 0x%X\n", IrpSp->MinorFunction);
+    Status = STATUS_NOT_IMPLEMENTED;
+    break;
+  }
+
+  if (Status != STATUS_PENDING) {
+    Irp->IoStatus.Status = Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+  }
+
+  DPRINT("Leaving. Status 0x%X\n", Status);
+
+  return Status;
+}
+
+/* EOF */