blob: 5317e4d7d96eab58ac6c24a450022ae82a5c13e5 [file] [log] [blame]
Trent Piepho83dbf662009-03-20 14:56:46 -06001/* 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>
Stephen Rothwell7bb2cb32009-03-26 18:34:33 +110021#include <linux/device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090022#include <linux/slab.h>
Trent Piepho83dbf662009-03-20 14:56:46 -060023#include "../pci.h"
24
25struct legacy_slot {
26 struct kobject kobj;
27 struct pci_dev *dev;
28 struct list_head list;
29};
30
31static LIST_HEAD(legacy_list);
32
33static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
34 char *buf)
35{
36 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
37 strcpy(buf, "1\n");
38 return 2;
39}
40
41static void remove_callback(void *data)
42{
43 pci_remove_bus_device((struct pci_dev *)data);
44}
45
46static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
47 const char *buf, size_t len)
48{
49 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
50 unsigned long val;
51
52 if (strict_strtoul(buf, 0, &val) < 0)
53 return -EINVAL;
54
55 if (val)
56 pci_rescan_bus(slot->dev->bus);
57 else
58 sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
59 slot->dev, THIS_MODULE);
60 return len;
61}
62
63static struct attribute *legacy_attrs[] = {
64 &(struct attribute){ .name = "power", .mode = 0644 },
65 NULL,
66};
67
68static void legacy_release(struct kobject *kobj)
69{
70 struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
71
72 pci_dev_put(slot->dev);
73 kfree(slot);
74}
75
76static struct kobj_type legacy_ktype = {
Emese Revfy52cf25d2010-01-19 02:58:23 +010077 .sysfs_ops = &(const struct sysfs_ops){
Trent Piepho83dbf662009-03-20 14:56:46 -060078 .store = legacy_store, .show = legacy_show
79 },
80 .release = &legacy_release,
81 .default_attrs = legacy_attrs,
82};
83
84static int legacy_add_slot(struct pci_dev *pdev)
85{
86 struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
87
88 if (!slot)
89 return -ENOMEM;
90
91 if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
92 &pci_slots_kset->kobj, "%s",
Stephen Rothwell7bb2cb32009-03-26 18:34:33 +110093 dev_name(&pdev->dev))) {
Trent Piepho83dbf662009-03-20 14:56:46 -060094 dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
95 return -EINVAL;
96 }
97 slot->dev = pci_dev_get(pdev);
98
99 list_add(&slot->list, &legacy_list);
100
101 return 0;
102}
103
104static int legacy_notify(struct notifier_block *nb,
105 unsigned long action, void *data)
106{
107 struct pci_dev *pdev = to_pci_dev(data);
108
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;
113
114 list_for_each_entry(slot, &legacy_list, list)
115 if (slot->dev == pdev)
116 goto found;
117
118 dev_warn(&pdev->dev, "Missing legacy fake slot?");
119 return -ENODEV;
120found:
121 kobject_del(&slot->kobj);
122 list_del(&slot->list);
123 kobject_put(&slot->kobj);
124 }
125
126 return 0;
127}
128
129static struct notifier_block legacy_notifier = {
130 .notifier_call = legacy_notify
131};
132
133static int __init init_legacy(void)
134{
135 struct pci_dev *pdev = NULL;
136
137 /* Add existing devices */
138 while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)))
139 legacy_add_slot(pdev);
140
141 /* Be alerted of any new ones */
142 bus_register_notifier(&pci_bus_type, &legacy_notifier);
143 return 0;
144}
145module_init(init_legacy);
146
147static void __exit remove_legacy(void)
148{
149 struct legacy_slot *slot, *tmp;
150
151 bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
152
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);
157 }
158}
159module_exit(remove_legacy);
160
161
162MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
163MODULE_DESCRIPTION("Legacy version of the fakephp interface");
164MODULE_LICENSE("GPL");