summaryrefslogtreecommitdiff
path: root/drivers/pci/hotplug.c
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@segfault.osdl.org>2002-04-16 01:06:36 -0700
committerLinus Torvalds <torvalds@home.transmeta.com>2002-04-16 01:06:36 -0700
commit9036d433bd2cacb1e0e34219649cd499b733a84d (patch)
treee85c2b58db5bff522f21ac71543730c61e6bafca /drivers/pci/hotplug.c
parent8da5f1d4072c4f0d88fbb7a25b2ba1065150ff1d (diff)
Split drivers/pci/pci.c into smaller, bite-size chunks
Diffstat (limited to 'drivers/pci/hotplug.c')
-rw-r--r--drivers/pci/hotplug.c132
1 files changed, 132 insertions, 0 deletions
diff --git a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c
new file mode 100644
index 000000000000..90eb3d8d426e
--- /dev/null
+++ b/drivers/pci/hotplug.c
@@ -0,0 +1,132 @@
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/kmod.h> /* for hotplug_path */
+
+extern struct list_head pci_drivers;
+extern int pci_announce_device(struct pci_driver *drv, struct pci_dev *dev);
+
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif
+
+static void
+run_sbin_hotplug(struct pci_dev *pdev, int insert)
+{
+ int i;
+ char *argv[3], *envp[8];
+ char id[20], sub_id[24], bus_id[24], class_id[20];
+
+ if (!hotplug_path[0])
+ return;
+
+ sprintf(class_id, "PCI_CLASS=%04X", pdev->class);
+ sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device);
+ sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device);
+ sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name);
+
+ i = 0;
+ argv[i++] = hotplug_path;
+ argv[i++] = "pci";
+ argv[i] = 0;
+
+ i = 0;
+ /* minimal command environment */
+ envp[i++] = "HOME=/";
+ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+ /* other stuff we want to pass to /sbin/hotplug */
+ envp[i++] = class_id;
+ envp[i++] = id;
+ envp[i++] = sub_id;
+ envp[i++] = bus_id;
+ if (insert)
+ envp[i++] = "ACTION=add";
+ else
+ envp[i++] = "ACTION=remove";
+ envp[i] = 0;
+
+ call_usermodehelper (argv [0], argv, envp);
+}
+
+/**
+ * pci_announce_device_to_drivers - tell the drivers a new device has appeared
+ * @dev: the device that has shown up
+ *
+ * Notifys the drivers that a new device has appeared, and also notifys
+ * userspace through /sbin/hotplug.
+ */
+void
+pci_announce_device_to_drivers(struct pci_dev *dev)
+{
+ struct list_head *ln;
+
+ for(ln=pci_drivers.next; ln != &pci_drivers; ln=ln->next) {
+ struct pci_driver *drv = list_entry(ln, struct pci_driver, node);
+ if (drv->remove && pci_announce_device(drv, dev))
+ break;
+ }
+
+ /* notify userspace of new hotplug device */
+ run_sbin_hotplug(dev, TRUE);
+}
+
+/**
+ * pci_insert_device - insert a hotplug device
+ * @dev: the device to insert
+ * @bus: where to insert it
+ *
+ * Add a new device to the device lists and notify userspace (/sbin/hotplug).
+ */
+void
+pci_insert_device(struct pci_dev *dev, struct pci_bus *bus)
+{
+ list_add_tail(&dev->bus_list, &bus->devices);
+ list_add_tail(&dev->global_list, &pci_devices);
+#ifdef CONFIG_PROC_FS
+ pci_proc_attach_device(dev);
+#endif
+ pci_announce_device_to_drivers(dev);
+}
+
+static void
+pci_free_resources(struct pci_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource *res = dev->resource + i;
+ if (res->parent)
+ release_resource(res);
+ }
+}
+
+/**
+ * pci_remove_device - remove a hotplug device
+ * @dev: the device to remove
+ *
+ * Delete the device structure from the device lists and
+ * notify userspace (/sbin/hotplug).
+ */
+void
+pci_remove_device(struct pci_dev *dev)
+{
+ if (dev->driver) {
+ if (dev->driver->remove)
+ dev->driver->remove(dev);
+ dev->driver = NULL;
+ }
+ list_del(&dev->bus_list);
+ list_del(&dev->global_list);
+ pci_free_resources(dev);
+#ifdef CONFIG_PROC_FS
+ pci_proc_detach_device(dev);
+#endif
+
+ /* notify userspace of hotplug device removal */
+ run_sbin_hotplug(dev, FALSE);
+}
+
+EXPORT_SYMBOL(pci_insert_device);
+EXPORT_SYMBOL(pci_remove_device);
+EXPORT_SYMBOL(pci_announce_device_to_drivers);