| #include <linux/kernel.h> |
| #include <linux/pci.h> |
| #include <linux/module.h> |
| #include "pci.h" |
| |
| int pci_hotplug (struct device *dev, char **envp, int num_envp, |
| char *buffer, int buffer_size) |
| { |
| struct pci_dev *pdev; |
| char *scratch; |
| int i = 0; |
| int length = 0; |
| |
| if (!dev) |
| return -ENODEV; |
| |
| pdev = to_pci_dev(dev); |
| if (!pdev) |
| return -ENODEV; |
| |
| scratch = buffer; |
| |
| /* stuff we want to pass to /sbin/hotplug */ |
| envp[i++] = scratch; |
| length += scnprintf (scratch, buffer_size - length, "PCI_CLASS=%04X", |
| pdev->class); |
| if ((buffer_size - length <= 0) || (i >= num_envp)) |
| return -ENOMEM; |
| ++length; |
| scratch += length; |
| |
| envp[i++] = scratch; |
| length += scnprintf (scratch, buffer_size - length, "PCI_ID=%04X:%04X", |
| pdev->vendor, pdev->device); |
| if ((buffer_size - length <= 0) || (i >= num_envp)) |
| return -ENOMEM; |
| ++length; |
| scratch += length; |
| |
| envp[i++] = scratch; |
| length += scnprintf (scratch, buffer_size - length, |
| "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, |
| pdev->subsystem_device); |
| if ((buffer_size - length <= 0) || (i >= num_envp)) |
| return -ENOMEM; |
| ++length; |
| scratch += length; |
| |
| envp[i++] = scratch; |
| length += scnprintf (scratch, buffer_size - length, "PCI_SLOT_NAME=%s", |
| pci_name(pdev)); |
| if ((buffer_size - length <= 0) || (i >= num_envp)) |
| return -ENOMEM; |
| |
| envp[i] = NULL; |
| |
| return 0; |
| } |
| |
| static int pci_visit_bus (struct pci_visit * fn, struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_parent) |
| { |
| struct list_head *ln; |
| struct pci_dev *dev; |
| struct pci_dev_wrapped wrapped_dev; |
| int result = 0; |
| |
| pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(wrapped_bus->bus), |
| wrapped_bus->bus->number); |
| |
| if (fn->pre_visit_pci_bus) { |
| result = fn->pre_visit_pci_bus(wrapped_bus, wrapped_parent); |
| if (result) |
| return result; |
| } |
| |
| ln = wrapped_bus->bus->devices.next; |
| while (ln != &wrapped_bus->bus->devices) { |
| dev = pci_dev_b(ln); |
| ln = ln->next; |
| |
| memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); |
| wrapped_dev.dev = dev; |
| |
| result = pci_visit_dev(fn, &wrapped_dev, wrapped_bus); |
| if (result) |
| return result; |
| } |
| |
| if (fn->post_visit_pci_bus) |
| result = fn->post_visit_pci_bus(wrapped_bus, wrapped_parent); |
| |
| return result; |
| } |
| |
| static int pci_visit_bridge (struct pci_visit * fn, |
| struct pci_dev_wrapped *wrapped_dev, |
| struct pci_bus_wrapped *wrapped_parent) |
| { |
| struct pci_bus *bus; |
| struct pci_bus_wrapped wrapped_bus; |
| int result = 0; |
| |
| pr_debug("PCI: Scanning bridge %s\n", pci_name(wrapped_dev->dev)); |
| |
| if (fn->visit_pci_dev) { |
| result = fn->visit_pci_dev(wrapped_dev, wrapped_parent); |
| if (result) |
| return result; |
| } |
| |
| bus = wrapped_dev->dev->subordinate; |
| if (bus) { |
| memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); |
| wrapped_bus.bus = bus; |
| |
| result = pci_visit_bus(fn, &wrapped_bus, wrapped_dev); |
| } |
| return result; |
| } |
| |
| /** |
| * pci_visit_dev - scans the pci buses. |
| * @fn: callback functions that are called while visiting |
| * @wrapped_dev: the device to scan |
| * @wrapped_parent: the bus where @wrapped_dev is connected to |
| * |
| * Every bus and every function is presented to a custom |
| * function that can act upon it. |
| */ |
| int pci_visit_dev(struct pci_visit *fn, struct pci_dev_wrapped *wrapped_dev, |
| struct pci_bus_wrapped *wrapped_parent) |
| { |
| struct pci_dev* dev = wrapped_dev ? wrapped_dev->dev : NULL; |
| int result = 0; |
| |
| if (!dev) |
| return 0; |
| |
| if (fn->pre_visit_pci_dev) { |
| result = fn->pre_visit_pci_dev(wrapped_dev, wrapped_parent); |
| if (result) |
| return result; |
| } |
| |
| switch (dev->class >> 8) { |
| case PCI_CLASS_BRIDGE_PCI: |
| result = pci_visit_bridge(fn, wrapped_dev, |
| wrapped_parent); |
| if (result) |
| return result; |
| break; |
| default: |
| pr_debug("PCI: Scanning device %s\n", pci_name(dev)); |
| if (fn->visit_pci_dev) { |
| result = fn->visit_pci_dev (wrapped_dev, |
| wrapped_parent); |
| if (result) |
| return result; |
| } |
| } |
| |
| if (fn->post_visit_pci_dev) |
| result = fn->post_visit_pci_dev(wrapped_dev, wrapped_parent); |
| |
| return result; |
| } |
| EXPORT_SYMBOL(pci_visit_dev); |