diff -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 02:53:05.000000000 +0200 @@ -165,8 +165,111 @@ err("Problem unregistering a slot %s\n", dslot->slot->name); } +/** + * Rescan slot. + * First, determine whether card in the slot has changed. It is + * done by compare of old and actual devices for function 0. + * If change detected, old device(s) removed, other functions + * scanned if it is multi-function dev, new devices inserted. + * + * @param temp Device template. Should be set: bus and devfn. + * @return Device for function 0 + */ +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) { + printk("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++) { + // printk("%x\n", func); + 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) { + printk("Found new device on %s function %x:%x %x\n", + bus->name, temp->devfn >> 3, + temp->devfn & 7, + hdr_type); + // from drivers/pci/bus.c, pci_bus_add_devices + pci_bus_add_device(dev); + // add fakephp slot + add_slot(dev); + } + } + } + } + } +} + + +/** + * Rescan PCI bus. + * Assumed, some slots may have changed its content. + * Find old information about each slot and the new one. + * If they match, do nothing. Otherwise, + * remove/insert proper devices. + * + * @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); +} + +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); + } +} + +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 pci bus */ + pci_rescan(); return -ENODEV; }