+static void
+sysfs_fill_info(struct pci_dev *d, unsigned int flags)
+{
+ int value, want_class, want_class_ext;
+
+ if (!d->access->buscentric)
+ {
+ /*
+ * These fields can be read from the config registers, but we want to show
+ * the kernel's view, which has regions and IRQs remapped and other fields
+ * (most importantly classes) possibly fixed if the device is known broken.
+ */
+ if (want_fill(d, flags, PCI_FILL_IDENT))
+ {
+ d->vendor_id = sysfs_get_value(d, "vendor", 1);
+ d->device_id = sysfs_get_value(d, "device", 1);
+ }
+ want_class = want_fill(d, flags, PCI_FILL_CLASS);
+ want_class_ext = want_fill(d, flags, PCI_FILL_CLASS_EXT);
+ if (want_class || want_class_ext)
+ {
+ value = sysfs_get_value(d, "class", 1);
+ if (want_class)
+ d->device_class = value >> 8;
+ if (want_class_ext)
+ {
+ d->prog_if = value & 0xff;
+ value = sysfs_get_value(d, "revision", 0);
+ if (value < 0)
+ value = pci_read_byte(d, PCI_REVISION_ID);
+ if (value >= 0)
+ d->rev_id = value;
+ }
+ }
+ if (want_fill(d, flags, PCI_FILL_SUBSYS))
+ {
+ value = sysfs_get_value(d, "subsystem_vendor", 0);
+ if (value >= 0)
+ {
+ d->subsys_vendor_id = value;
+ value = sysfs_get_value(d, "subsystem_device", 0);
+ if (value >= 0)
+ d->subsys_id = value;
+ }
+ else
+ clear_fill(d, PCI_FILL_SUBSYS);
+ }
+ if (want_fill(d, flags, PCI_FILL_IRQ))
+ d->irq = sysfs_get_value(d, "irq", 1);
+ if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_IO_FLAGS | PCI_FILL_BRIDGE_BASES))
+ sysfs_get_resources(d);
+ if (want_fill(d, flags, PCI_FILL_PARENT))
+ {
+ unsigned int domain, bus, dev, func;
+ char *path_abs, *path_canon, *name;
+ char path_rel[OBJNAMELEN];
+ struct pci_dev *parent;
+
+ /* Construct sysfs path for parent device */
+ sysfs_obj_name(d, "..", path_rel);
+ path_abs = realpath(path_rel, NULL);
+ name = path_abs ? strrchr(path_abs, '/') : NULL;
+ name = name ? name+1 : name;
+ parent = NULL;
+
+ if (name && sscanf(name, "%x:%x:%x.%d", &domain, &bus, &dev, &func) == 4 && domain <= 0x7fffffff)
+ for (parent = d->access->devices; parent; parent = parent->next)
+ if (parent->domain == (int)domain && parent->bus == bus && parent->dev == dev && parent->func == func)
+ break;
+
+ if (parent)
+ {
+ /* Check if parsed BDF address from parent sysfs device is really expected PCI device */
+ sysfs_obj_name(parent, ".", path_rel);
+ path_canon = realpath(path_rel, NULL);
+ if (!path_canon || strcmp(path_canon, path_abs) != 0)
+ parent = NULL;
+ }
+
+ if (parent)
+ d->parent = parent;
+ else
+ clear_fill(d, PCI_FILL_PARENT);
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_PHYS_SLOT))
+ {
+ struct pci_dev *pd;
+ sysfs_fill_slots(d->access);
+ for (pd = d->access->devices; pd; pd = pd->next)
+ pd->known_fields |= PCI_FILL_PHYS_SLOT;
+ }
+
+ if (want_fill(d, flags, PCI_FILL_MODULE_ALIAS))
+ {
+ char buf[OBJBUFSIZE];
+ if (sysfs_get_string(d, "modalias", buf, 0))
+ d->module_alias = pci_set_property(d, PCI_FILL_MODULE_ALIAS, buf);
+ }
+
+ if (want_fill(d, flags, PCI_FILL_LABEL))
+ {
+ char buf[OBJBUFSIZE];
+ if (sysfs_get_string(d, "label", buf, 0))
+ d->label = pci_set_property(d, PCI_FILL_LABEL, buf);
+ }
+
+ if (want_fill(d, flags, PCI_FILL_NUMA_NODE))
+ d->numa_node = sysfs_get_value(d, "numa_node", 0);
+
+ if (want_fill(d, flags, PCI_FILL_IOMMU_GROUP))
+ {
+ char *group_link = sysfs_deref_link(d, "iommu_group");
+ if (group_link)
+ {
+ pci_set_property(d, PCI_FILL_IOMMU_GROUP, basename(group_link));
+ free(group_link);
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_DT_NODE))
+ {
+ char *node = sysfs_deref_link(d, "of_node");
+ if (node)
+ {
+ pci_set_property(d, PCI_FILL_DT_NODE, node);
+ free(node);
+ }
+ }
+
+ if (want_fill(d, flags, PCI_FILL_DRIVER))
+ {
+ char *driver_path = sysfs_deref_link(d, "driver");
+ if (driver_path)
+ {
+ char *driver = strrchr(driver_path, '/');
+ driver = driver ? driver+1 : driver_path;
+ pci_set_property(d, PCI_FILL_DRIVER, driver);
+ free(driver_path);
+ }
+ else
+ clear_fill(d, PCI_FILL_DRIVER);
+ }
+
+ pci_generic_fill_info(d, flags);
+}
+
+/* Intent of the sysfs_setup() caller */
+enum
+ {
+ SETUP_READ_CONFIG = 0,
+ SETUP_WRITE_CONFIG = 1,
+ SETUP_READ_VPD = 2
+ };
+