1 /* Works like the fakephp driver used to, except a little better.
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.
9 * Uses nothing from the pci-hotplug subsystem.
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 <linux/device.h>
22 #include <linux/slab.h>
28 struct list_head list
;
31 static LIST_HEAD(legacy_list
);
33 static ssize_t
legacy_show(struct kobject
*kobj
, struct attribute
*attr
,
36 struct legacy_slot
*slot
= container_of(kobj
, typeof(*slot
), kobj
);
41 static void remove_callback(void *data
)
43 pci_remove_bus_device((struct pci_dev
*)data
);
46 static ssize_t
legacy_store(struct kobject
*kobj
, struct attribute
*attr
,
47 const char *buf
, size_t len
)
49 struct legacy_slot
*slot
= container_of(kobj
, typeof(*slot
), kobj
);
52 if (strict_strtoul(buf
, 0, &val
) < 0)
56 pci_rescan_bus(slot
->dev
->bus
);
58 sysfs_schedule_callback(&slot
->dev
->dev
.kobj
, remove_callback
,
59 slot
->dev
, THIS_MODULE
);
63 static struct attribute
*legacy_attrs
[] = {
64 &(struct attribute
){ .name
= "power", .mode
= 0644 },
68 static void legacy_release(struct kobject
*kobj
)
70 struct legacy_slot
*slot
= container_of(kobj
, typeof(*slot
), kobj
);
72 pci_dev_put(slot
->dev
);
76 static struct kobj_type legacy_ktype
= {
77 .sysfs_ops
= &(const struct sysfs_ops
){
78 .store
= legacy_store
, .show
= legacy_show
80 .release
= &legacy_release
,
81 .default_attrs
= legacy_attrs
,
84 static int legacy_add_slot(struct pci_dev
*pdev
)
86 struct legacy_slot
*slot
= kzalloc(sizeof(*slot
), GFP_KERNEL
);
91 if (kobject_init_and_add(&slot
->kobj
, &legacy_ktype
,
92 &pci_slots_kset
->kobj
, "%s",
93 dev_name(&pdev
->dev
))) {
94 dev_warn(&pdev
->dev
, "Failed to created legacy fake slot\n");
97 slot
->dev
= pci_dev_get(pdev
);
99 list_add(&slot
->list
, &legacy_list
);
104 static int legacy_notify(struct notifier_block
*nb
,
105 unsigned long action
, void *data
)
107 struct pci_dev
*pdev
= to_pci_dev(data
);
109 if (action
== BUS_NOTIFY_ADD_DEVICE
) {
110 legacy_add_slot(pdev
);
111 } else if (action
== BUS_NOTIFY_DEL_DEVICE
) {
112 struct legacy_slot
*slot
;
114 list_for_each_entry(slot
, &legacy_list
, list
)
115 if (slot
->dev
== pdev
)
118 dev_warn(&pdev
->dev
, "Missing legacy fake slot?");
121 kobject_del(&slot
->kobj
);
122 list_del(&slot
->list
);
123 kobject_put(&slot
->kobj
);
129 static struct notifier_block legacy_notifier
= {
130 .notifier_call
= legacy_notify
133 static int __init
init_legacy(void)
135 struct pci_dev
*pdev
= NULL
;
137 /* Add existing devices */
138 for_each_pci_dev(pdev
)
139 legacy_add_slot(pdev
);
141 /* Be alerted of any new ones */
142 bus_register_notifier(&pci_bus_type
, &legacy_notifier
);
145 module_init(init_legacy
);
147 static void __exit
remove_legacy(void)
149 struct legacy_slot
*slot
, *tmp
;
151 bus_unregister_notifier(&pci_bus_type
, &legacy_notifier
);
153 list_for_each_entry_safe(slot
, tmp
, &legacy_list
, list
) {
154 list_del(&slot
->list
);
155 kobject_del(&slot
->kobj
);
156 kobject_put(&slot
->kobj
);
159 module_exit(remove_legacy
);
162 MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
163 MODULE_DESCRIPTION("Legacy version of the fakephp interface");
164 MODULE_LICENSE("GPL");