- Add support for PnP IRP to PDO: IRP_MN_QUERY_BUS_INFORMATION (PciQueryBusInformatio...
[reactos.git] / reactos / drivers / bus / pcix / enum.c
index 700f942..3caa988 100644 (file)
 
 /* GLOBALS ********************************************************************/
 
+PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements;
+
+PCI_CONFIGURATOR PciConfigurators[] =
+{
+    {
+        Device_MassageHeaderForLimitsDetermination,
+        Device_RestoreCurrent,
+        Device_SaveLimits,
+        Device_SaveCurrentSettings,
+        Device_ChangeResourceSettings,
+        Device_GetAdditionalResourceDescriptors,
+        Device_ResetDevice
+    },
+    {
+        PPBridge_MassageHeaderForLimitsDetermination,
+        PPBridge_RestoreCurrent,
+        PPBridge_SaveLimits,
+        PPBridge_SaveCurrentSettings,
+        PPBridge_ChangeResourceSettings,
+        PPBridge_GetAdditionalResourceDescriptors,
+        PPBridge_ResetDevice
+    },
+    {
+        Cardbus_MassageHeaderForLimitsDetermination,
+        Cardbus_RestoreCurrent,
+        Cardbus_SaveLimits,
+        Cardbus_SaveCurrentSettings,
+        Cardbus_ChangeResourceSettings,
+        Cardbus_GetAdditionalResourceDescriptors,
+        Cardbus_ResetDevice
+    }
+};
+
 /* FUNCTIONS ******************************************************************/
 
+NTSTATUS
+NTAPI
+PciQueryResources(IN PPCI_PDO_EXTENSION PdoExtension,
+                  OUT PCM_RESOURCE_LIST *Buffer)
+{
+    /* Not yet implemented */
+    UNIMPLEMENTED;
+    while (TRUE);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryTargetDeviceRelations(IN PPCI_PDO_EXTENSION PdoExtension,
+                              IN OUT PDEVICE_RELATIONS *pDeviceRelations)
+{
+    PDEVICE_RELATIONS DeviceRelations;
+    PAGED_CODE();
+
+    /* If there were existing relations, free them */
+    if (*pDeviceRelations) ExFreePoolWithTag(*pDeviceRelations, 0);
+
+    /* Allocate a new structure for the relations */
+    DeviceRelations = ExAllocatePoolWithTag(NonPagedPool,
+                                            sizeof(DEVICE_RELATIONS),
+                                            'BicP');
+    if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Only one relation: the PDO */
+    DeviceRelations->Count = 1;
+    DeviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject;
+    ObReferenceObject(DeviceRelations->Objects[0]);
+
+    /* Return the new relations */
+    *pDeviceRelations = DeviceRelations;
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryEjectionRelations(IN PPCI_PDO_EXTENSION PdoExtension,
+                          IN OUT PDEVICE_RELATIONS *pDeviceRelations)
+{
+    /* Not yet implemented */
+    UNIMPLEMENTED;
+    while (TRUE);
+}
+
+NTSTATUS
+NTAPI
+PciQueryRequirements(IN PPCI_PDO_EXTENSION PdoExtension,
+                     IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList)
+{
+    /* Not yet implemented */
+    UNIMPLEMENTED;
+    while (TRUE);
+    return STATUS_SUCCESS;
+}
+
 /*
  * 7. The IO/MEM/Busmaster decodes are disabled for the device.
  * 8. The PCI bus driver sets the operating mode bits of the Programming
@@ -186,9 +278,6 @@ PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension,
     USHORT Command;
     UCHAR RegValue;
 
-    /* There should always be a PDO extension passed in */
-    ASSERT(PdoExtension);
-
     /* Check what kind of hack operation this is */
     switch (OperationType)
     {
@@ -229,6 +318,9 @@ PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension,
          */
         case PCI_HACK_FIXUP_AFTER_CONFIGURATION:
 
+            /* There should always be a PDO extension passed in */
+            ASSERT(PdoExtension);
+
             /*
              * On the OPTi Viper-M IDE controller, Linux doesn't support IDE-DMA
              * and FreeBSD bug reports indicate that the system crashes when the
@@ -346,6 +438,9 @@ PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension,
          */
         case PCI_HACK_FIXUP_BEFORE_UPDATE:
 
+            /* There should always be a PDO extension passed in */
+            ASSERT(PdoExtension);
+
             /* Is this an IBM 20H2999 PCI Docking Bridge, used on Thinkpads? */
             if ((PdoExtension->VendorId == 0x1014) &&
                 (PdoExtension->DeviceId == 0x95))
@@ -438,7 +533,6 @@ PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension,
     }
 }
 
-
 BOOLEAN
 NTAPI
 PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension,
@@ -667,6 +761,279 @@ PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
     }
 }
 
+VOID
+NTAPI
+PciWriteLimitsAndRestoreCurrent(IN PVOID Reserved,
+                                IN PPCI_CONFIGURATOR_CONTEXT Context)
+{
+    PPCI_COMMON_HEADER PciData, Current;
+    PPCI_PDO_EXTENSION PdoExtension;
+
+    /* Grab all parameters from the context */
+    PdoExtension = Context->PdoExtension;
+    Current = Context->Current;
+    PciData = Context->PciData;
+
+    /* Write the limit discovery header */
+    PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+    /* Now read what the device indicated the limits are */
+    PciReadDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+    /* Then write back the original configuration header */
+    PciWriteDeviceConfig(PdoExtension, Current, 0, PCI_COMMON_HDR_LENGTH);
+
+    /* Copy back the original command that was saved in the context */
+    Current->Command = Context->Command;
+    if (Context->Command)
+    {
+        /* Program it back into the device */
+        PciWriteDeviceConfig(PdoExtension,
+                             &Context->Command,
+                             FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+                             sizeof(USHORT));
+    }
+
+    /* Copy back the original status that was saved as well */
+    Current->Status = Context->Status;
+
+    /* Call the configurator to restore any other data that might've changed */
+    Context->Configurator->RestoreCurrent(Context);
+}
+
+NTSTATUS
+NTAPI
+PcipGetFunctionLimits(IN PPCI_CONFIGURATOR_CONTEXT Context)
+{
+    PPCI_CONFIGURATOR Configurator;
+    PPCI_COMMON_HEADER PciData, Current;
+    PPCI_PDO_EXTENSION PdoExtension;
+    PCI_IPI_CONTEXT IpiContext;
+    PIO_RESOURCE_DESCRIPTOR IoDescriptor;
+    ULONG Offset;
+    PAGED_CODE();
+
+    /* Grab all parameters from the context */
+    PdoExtension = Context->PdoExtension;
+    Current = Context->Current;
+    PciData = Context->PciData;
+
+    /* Save the current PCI Command and Status word */
+    Context->Status = Current->Status;
+    Context->Command = Current->Command;
+
+    /* Now that they're saved, clear the status, and disable all decodes */
+    Current->Status = 0;
+    Current->Command &= ~(PCI_ENABLE_IO_SPACE |
+                          PCI_ENABLE_MEMORY_SPACE |
+                          PCI_ENABLE_BUS_MASTER);
+
+    /* Make a copy of the current PCI configuration header (with decodes off) */
+    RtlCopyMemory(PciData, Current, PCI_COMMON_HDR_LENGTH);
+
+    /* Locate the correct resource configurator for this type of device */
+    Configurator = &PciConfigurators[PdoExtension->HeaderType];
+    Context->Configurator = Configurator;
+
+    /* Initialize it, which will typically setup the BARs for limit discovery */
+    Configurator->Initialize(Context);
+
+    /* Check for critical devices and PCI Debugging devices */
+    if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) ||
+        (PdoExtension->OnDebugPath))
+    {
+        /* Specifically check for a PCI Debugging device */
+        if (PdoExtension->OnDebugPath)
+        {
+            /* Was it enabled for bus mastering? */
+            if (Context->Command & PCI_ENABLE_BUS_MASTER)
+            {
+                /* This decode needs to be re-enabled so debugging can work */
+                PciData->Command |= PCI_ENABLE_BUS_MASTER;
+                Current->Command |= PCI_ENABLE_BUS_MASTER;
+            }
+
+            /* Disable the debugger while the discovery is happening */
+            KdDisableDebugger();
+        }
+
+        /* For these devices, an IPI must be sent to force high-IRQL discovery */
+        IpiContext.Barrier = 1;
+        IpiContext.RunCount = 1;
+        IpiContext.PdoExtension = PdoExtension;
+        IpiContext.Function = PciWriteLimitsAndRestoreCurrent;
+        IpiContext.Context = Context;
+        KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&IpiContext);
+
+        /* Re-enable the debugger if this was a PCI Debugging Device */
+        if (PdoExtension->OnDebugPath) KdEnableDebugger();
+    }
+    else
+    {
+        /* Otherwise, it's safe to do this in-line at low IRQL */
+        PciWriteLimitsAndRestoreCurrent(PdoExtension, Context);
+    }
+
+    /*
+     * Check if it's valid to compare the headers to see if limit discovery mode
+     * has properly exited (the expected case is that the PCI header would now
+     * be equal to what it was before). In some cases, it is known that this will
+     * fail, because during PciApplyHacks (among other places), software hacks
+     * had to be applied to the header, which the hardware-side will not see, and
+     * thus the headers would appear "different".
+     */
+    if (!PdoExtension->ExpectedWritebackFailure)
+    {
+        /* Read the current PCI header now, after discovery has completed */
+        PciReadDeviceConfig(PdoExtension, PciData + 1, 0, PCI_COMMON_HDR_LENGTH);
+
+        /* Check if the current header at entry, is equal to the header now */
+        Offset = RtlCompareMemory(PciData + 1, Current, PCI_COMMON_HDR_LENGTH);
+        if (Offset != PCI_COMMON_HDR_LENGTH)
+        {
+            /* It's not, which means configuration somehow changed, dump this */
+            DPRINT1("PCI - CFG space write verify failed at offset 0x%x\n", Offset);
+            PciDebugDumpCommonConfig(PciData + 1);
+            DPRINT1("----------\n");
+            PciDebugDumpCommonConfig(Current);
+        }
+    }
+
+    /* This PDO should not already have resources, since this is only done once */
+    ASSERT(PdoExtension->Resources == NULL);
+
+    /* Allocate the structure that will hold the discovered resources and limits */
+    PdoExtension->Resources = ExAllocatePoolWithTag(NonPagedPool,
+                                                    sizeof(PCI_FUNCTION_RESOURCES),
+                                                    'BicP');
+    if (!PdoExtension->Resources) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Clear it out for now */
+    RtlZeroMemory(PdoExtension->Resources, sizeof(PCI_FUNCTION_RESOURCES));
+
+    /* Now call the configurator, which will first store the limits... */
+    Configurator->SaveLimits(Context);
+
+    /* ...and then store the current resources being used */
+    Configurator->SaveCurrentSettings(Context);
+
+    /* Loop all the limit descriptors backwards */
+    IoDescriptor = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1];
+    while (TRUE)
+    {
+        /* Keep going until a non-null descriptor is found */
+        IoDescriptor--;
+        if (IoDescriptor->Type != CmResourceTypeNull) break;
+
+        /* This is a null descriptor, is it the last one? */
+        if (IoDescriptor == &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1])
+        {
+            /* This means the descriptor is NULL, which means discovery failed */
+            DPRINT1("PCI Resources fail!\n");
+
+            /* No resources will be assigned for the device */
+            ExFreePoolWithTag(PdoExtension->Resources, 0);
+            PdoExtension->Resources = NULL;
+            break;
+        }
+    }
+
+    /* Return success here, even if the device has no assigned resources */
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciGetFunctionLimits(IN PPCI_PDO_EXTENSION PdoExtension,
+                     IN PPCI_COMMON_HEADER Current,
+                     IN ULONGLONG HackFlags)
+{
+    NTSTATUS Status;
+    PPCI_COMMON_HEADER PciData;
+    PCI_CONFIGURATOR_CONTEXT Context;
+    PAGED_CODE();
+
+    /* Do the hackflags indicate this device should be skipped? */
+    if (PciSkipThisFunction(Current,
+                            PdoExtension->Slot,
+                            PCI_SKIP_RESOURCE_ENUMERATION,
+                            HackFlags))
+    {
+        /* Do not process its resources */
+        return STATUS_SUCCESS;
+    }
+
+    /* Allocate a buffer to hold two PCI configuration headers */
+    PciData = ExAllocatePoolWithTag(0, 2 * PCI_COMMON_HDR_LENGTH, 'BicP');
+    if (!PciData) return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* Set up the context for the resource enumeration, and do it */
+    Context.Current = Current;
+    Context.PciData = PciData;
+    Context.PdoExtension = PdoExtension;
+    Status = PcipGetFunctionLimits(&Context);
+
+    /* Enumeration is completed, free the PCI headers and return the status */
+    ExFreePoolWithTag(PciData, 0);
+    return Status;
+}
+
+VOID
+NTAPI
+PciProcessBus(IN PPCI_FDO_EXTENSION DeviceExtension)
+{
+    PPCI_PDO_EXTENSION PdoExtension;
+    PDEVICE_OBJECT PhysicalDeviceObject;
+    PAGED_CODE();
+
+    /* Get the PDO Extension */
+    PhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject;
+    PdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension;
+
+    /* Cheeck if this is the root bus */
+    if (!PCI_IS_ROOT_FDO(DeviceExtension))
+    {
+        /* Not really handling this year */
+        UNIMPLEMENTED;
+        while (TRUE);
+
+        /* Check for PCI bridges with the ISA bit set, or required */
+        if ((PdoExtension) &&
+            (PciClassifyDeviceType(PdoExtension) == PciTypePciBridge) &&
+            ((PdoExtension->Dependent.type1.IsaBitRequired) ||
+             (PdoExtension->Dependent.type1.IsaBitSet)))
+        {
+            /* We'll need to do some legacy support */
+            UNIMPLEMENTED;
+            while (TRUE);
+        }
+    }
+    else
+    {
+        /* Scan all of the root bus' children bridges */
+        for (PdoExtension = DeviceExtension->ChildBridgePdoList;
+             PdoExtension;
+             PdoExtension = PdoExtension->NextBridge)
+        {
+            /* Find any that have the VGA decode bit on */
+            if (PdoExtension->Dependent.type1.VgaBitSet)
+            {
+                /* Again, some more legacy support we'll have to do */
+                UNIMPLEMENTED;
+                while (TRUE);
+            }
+        }
+    }
+
+    /* Check for ACPI systems where the OS assigns bus numbers */
+    if (PciAssignBusNumbers)
+    {
+        /* Not yet supported */
+        UNIMPLEMENTED;
+        while (TRUE);
+    }
+}
+
 NTSTATUS
 NTAPI
 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
@@ -937,6 +1304,9 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
             /* Get power, AGP, and other capability data */
             PciGetEnhancedCapabilities(NewExtension, PciData);
 
+            /* Now configure the BARs */
+            Status = PciGetFunctionLimits(NewExtension, PciData, HackFlags);
+
             /* Power up the device */
             PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
 
@@ -1110,7 +1480,8 @@ PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
         }
     }
 
-    /* Enumeration is completed */
+    /* Enumeration completed, do a final pass now that all devices are found */
+    if (ProcessFlag) PciProcessBus(DeviceExtension);
     return STATUS_SUCCESS;
 }