diff -X /home/jdittmer/stuff/diffignore -ur linus/drivers/pci/hotplug/fakephp.c pcirescan/drivers/pci/hotplug/fakephp.c --- linus/drivers/pci/hotplug/fakephp.c 2004-01-09 07:59:45.000000000 +0100 +++ pcirescan/drivers/pci/hotplug/fakephp.c 2004-10-10 15:15:55.000000000 +0200 @@ -165,14 +165,126 @@ err("Problem unregistering a slot %s\n", dslot->slot->name); } +/** + * Rescan slot. + * Tries hard not to re-enable already existing devices + * also handle scanning of subfunctions + * + * @param temp Device template. Should be set: bus and devfn. + */ +static void pci_rescan_slot(struct pci_dev *temp) +{ + struct pci_bus *bus = temp->bus; + struct pci_dev* old_dev = pci_find_slot(bus->number, temp->devfn); + struct pci_dev *dev = NULL; + int func = 0; + int new_multi = 0; /* new multifunction device */ + u8 hdr_type; + if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { + temp->hdr_type = hdr_type & 0x7f; + new_multi=hdr_type & 0x80; + if (!old_dev) { + dev = pci_scan_single_device(bus, temp->devfn); + if (dev) { + dbg("Found new device on %s function %x:%x %x\n", + bus->name, temp->devfn >> 3, + temp->devfn & 7, + hdr_type); + pci_bus_add_device(dev); + add_slot(dev); + } + } + if (new_multi) { /* continue scanning for other functions */ + for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) { + if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) + continue; + temp->hdr_type = hdr_type & 0x7f; + + old_dev = pci_find_slot(bus->number, temp->devfn); + if (!old_dev) { + dev = pci_scan_single_device(bus, temp->devfn); + if (dev) { + dbg("Found new device on %s function %x:%x %x\n", + bus->name, temp->devfn >> 3, + temp->devfn & 7, + hdr_type); + pci_bus_add_device(dev); + add_slot(dev); + } + } + } + } + } +} + + +/** + * Rescan PCI bus. + * call pci_rescan_slot for each possible function of the bus + * + * @param bus + */ +static void pci_rescan_bus(const struct pci_bus *bus) +{ + unsigned int devfn; + struct pci_dev *dev; + dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); + if (!dev) + return; + + memset(dev, 0, sizeof(dev)); + dev->bus = (struct pci_bus*)bus; + dev->sysdata = bus->sysdata; + for (devfn = 0; devfn < 0x100; devfn += 8) { + dev->devfn = devfn; + pci_rescan_slot(dev); + } + kfree(dev); +} + +/* recursively scan all buses */ +static void pci_rescan_buses(const struct list_head *list) +{ + const struct list_head *l; + list_for_each(l,list) { + const struct pci_bus *b = pci_bus_b(l); + pci_rescan_bus(b); + pci_rescan_buses(&b->children); + } +} + +/* initiate rescan of all pci buses */ +static void pci_rescan(void) { + pci_rescan_buses(&pci_root_buses); +} + + static int enable_slot(struct hotplug_slot *hotplug_slot) { + /* mis-use enable_slot for rescanning of the pci bus */ + pci_rescan(); return -ENODEV; } +/* find the hotplug_slot for the pci_dev */ +static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev) +{ + struct dummy_slot *dslot; + + list_for_each_entry(dslot, &slot_list, node) { + if (dslot->dev == dev) + return dslot->slot; + } + return NULL; +} + + static int disable_slot(struct hotplug_slot *slot) { struct dummy_slot *dslot; + struct hotplug_slot *hslot; + struct pci_dev *dev; + int func; if (!slot) return -ENODEV; @@ -185,6 +297,28 @@ err("Can't remove PCI devices with other PCI devices behind it yet.\n"); return -ENODEV; } + /* search for subfunctions and disable them first */ + if (!(dslot->dev->devfn & 7)) { + dbg("Searching for subfunctions\n"); + for (func=1;func<8;func++) { + dbg("Considering %x %x+%x\n", + dslot->dev->bus->number, + dslot->dev->devfn, + func); + dev = pci_find_slot(dslot->dev->bus->number, dslot->dev->devfn + func); + if (dev) { + dbg("Slot %s Removed <%s>\n", dslot->dev->slot_name, dslot->dev->pretty_name); + hslot = get_slot_from_dev(dev); + if (hslot) + disable_slot(hslot); + else { + err("Hotplug slot not found for subordinate pci-device\n"); + return -ENODEV; + } + } else + dbg("No device in slot found\n"); + } + } /* remove the device from the pci core */ pci_remove_bus_device(dslot->dev);