aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/hotplug/legacy_fakephp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/hotplug/legacy_fakephp.c')
-rw-r--r--drivers/pci/hotplug/legacy_fakephp.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/legacy_fakephp.c b/drivers/pci/hotplug/legacy_fakephp.c
new file mode 100644
index 000000000000..2dc7828df480
--- /dev/null
+++ b/drivers/pci/hotplug/legacy_fakephp.c
@@ -0,0 +1,162 @@
1/* Works like the fakephp driver used to, except a little better.
2 *
3 * - It's possible to remove devices with subordinate busses.
4 * - New PCI devices that appear via any method, not just a fakephp triggered
5 * rescan, will be noticed.
6 * - Devices that are removed via any method, not just a fakephp triggered
7 * removal, will also be noticed.
8 *
9 * Uses nothing from the pci-hotplug subsystem.
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/kernel.h>
15#include <linux/types.h>
16#include <linux/list.h>
17#include <linux/kobject.h>
18#include <linux/sysfs.h>
19#include <linux/init.h>
20#include <linux/pci.h>
21#include "../pci.h"
22
23struct legacy_slot {
24 struct kobject kobj;
25 struct pci_dev *dev;
26 struct list_head list;
27};
28
29static LIST_HEAD(legacy_list);
30
31static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
32 char *buf)
33{
34 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
35 strcpy(buf, "1\n");
36 return 2;
37}
38
39static void remove_callback(void *data)
40{
41 pci_remove_bus_device((struct pci_dev *)data);
42}
43
44static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
45 const char *buf, size_t len)
46{
47 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
48 unsigned long val;
49
50 if (strict_strtoul(buf, 0, &val) < 0)
51 return -EINVAL;
52
53 if (val)
54 pci_rescan_bus(slot->dev->bus);
55 else
56 sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
57 slot->dev, THIS_MODULE);
58 return len;
59}
60
61static struct attribute *legacy_attrs[] = {
62 &(struct attribute){ .name = "power", .mode = 0644 },
63 NULL,
64};
65
66static void legacy_release(struct kobject *kobj)
67{
68 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
69
70 pci_dev_put(slot->dev);
71 kfree(slot);
72}
73
74static struct kobj_type legacy_ktype = {
75 .sysfs_ops = &(struct sysfs_ops){
76 .store = legacy_store, .show = legacy_show
77 },
78 .release = &legacy_release,
79 .default_attrs = legacy_attrs,
80};
81
82static int legacy_add_slot(struct pci_dev *pdev)
83{
84 struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
85
86 if (!slot)
87 return -ENOMEM;
88
89 if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
90 &pci_slots_kset->kobj, "%s",
91 pdev->dev.bus_id)) {
92 dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
93 return -EINVAL;
94 }
95 slot->dev = pci_dev_get(pdev);
96
97 list_add(&slot->list, &legacy_list);
98
99 return 0;
100}
101
102static int legacy_notify(struct notifier_block *nb,
103 unsigned long action, void *data)
104{
105 struct pci_dev *pdev = to_pci_dev(data);
106
107 if (action == BUS_NOTIFY_ADD_DEVICE) {
108 legacy_add_slot(pdev);
109 } else if (action == BUS_NOTIFY_DEL_DEVICE) {
110 struct legacy_slot *slot;
111
112 list_for_each_entry(slot, &legacy_list, list)
113 if (slot->dev == pdev)
114 goto found;
115
116 dev_warn(&pdev->dev, "Missing legacy fake slot?");
117 return -ENODEV;
118found:
119 kobject_del(&slot->kobj);
120 list_del(&slot->list);
121 kobject_put(&slot->kobj);
122 }
123
124 return 0;
125}
126
127static struct notifier_block legacy_notifier = {
128 .notifier_call = legacy_notify
129};
130
131static int __init init_legacy(void)
132{
133 struct pci_dev *pdev = NULL;
134
135 /* Add existing devices */
136 while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)))
137 legacy_add_slot(pdev);
138
139 /* Be alerted of any new ones */
140 bus_register_notifier(&pci_bus_type, &legacy_notifier);
141 return 0;
142}
143module_init(init_legacy);
144
145static void __exit remove_legacy(void)
146{
147 struct legacy_slot *slot, *tmp;
148
149 bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
150
151 list_for_each_entry_safe(slot, tmp, &legacy_list, list) {
152 list_del(&slot->list);
153 kobject_del(&slot->kobj);
154 kobject_put(&slot->kobj);
155 }
156}
157module_exit(remove_legacy);
158
159
160MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
161MODULE_DESCRIPTION("Legacy version of the fakephp interface");
162MODULE_LICENSE("GPL");