at v2.6.23 371 lines 8.5 kB view raw
1/* 2 * Fake PCI Hot Plug Controller Driver 3 * 4 * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> 5 * Copyright (C) 2003 IBM Corp. 6 * Copyright (C) 2003 Rolf Eike Beer <eike-kernel@sf-tec.de> 7 * 8 * Based on ideas and code from: 9 * Vladimir Kondratiev <vladimir.kondratiev@intel.com> 10 * Rolf Eike Beer <eike-kernel@sf-tec.de> 11 * 12 * All rights reserved. 13 * 14 * This program is free software; you can redistribute it and/or modify 15 * it under the terms of the GNU General Public License as published by 16 * the Free Software Foundation, version 2 of the License. 17 * 18 * Send feedback to <greg@kroah.com> 19 */ 20 21/* 22 * 23 * This driver will "emulate" removing PCI devices from the system. If 24 * the "power" file is written to with "0" then the specified PCI device 25 * will be completely removed from the kernel. 26 * 27 * WARNING, this does NOT turn off the power to the PCI device. This is 28 * a "logical" removal, not a physical or electrical removal. 29 * 30 * Use this module at your own risk, you have been warned! 31 * 32 * Enabling PCI devices is left as an exercise for the reader... 33 * 34 */ 35#include <linux/kernel.h> 36#include <linux/module.h> 37#include <linux/pci.h> 38#include <linux/pci_hotplug.h> 39#include <linux/init.h> 40#include <linux/string.h> 41#include <linux/slab.h> 42#include "../pci.h" 43 44#if !defined(MODULE) 45 #define MY_NAME "fakephp" 46#else 47 #define MY_NAME THIS_MODULE->name 48#endif 49 50#define dbg(format, arg...) \ 51 do { \ 52 if (debug) \ 53 printk(KERN_DEBUG "%s: " format, \ 54 MY_NAME , ## arg); \ 55 } while (0) 56#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) 57#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) 58 59#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" 60#define DRIVER_DESC "Fake PCI Hot Plug Controller Driver" 61 62struct dummy_slot { 63 struct list_head node; 64 struct hotplug_slot *slot; 65 struct pci_dev *dev; 66}; 67 68static int debug; 69static LIST_HEAD(slot_list); 70 71static int enable_slot (struct hotplug_slot *slot); 72static int disable_slot (struct hotplug_slot *slot); 73 74static struct hotplug_slot_ops dummy_hotplug_slot_ops = { 75 .owner = THIS_MODULE, 76 .enable_slot = enable_slot, 77 .disable_slot = disable_slot, 78}; 79 80static void dummy_release(struct hotplug_slot *slot) 81{ 82 struct dummy_slot *dslot = slot->private; 83 84 list_del(&dslot->node); 85 kfree(dslot->slot->info); 86 kfree(dslot->slot); 87 pci_dev_put(dslot->dev); 88 kfree(dslot); 89} 90 91static int add_slot(struct pci_dev *dev) 92{ 93 struct dummy_slot *dslot; 94 struct hotplug_slot *slot; 95 int retval = -ENOMEM; 96 97 slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL); 98 if (!slot) 99 goto error; 100 101 slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL); 102 if (!slot->info) 103 goto error_slot; 104 105 slot->info->power_status = 1; 106 slot->info->max_bus_speed = PCI_SPEED_UNKNOWN; 107 slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN; 108 109 slot->name = &dev->dev.bus_id[0]; 110 dbg("slot->name = %s\n", slot->name); 111 112 dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL); 113 if (!dslot) 114 goto error_info; 115 116 slot->ops = &dummy_hotplug_slot_ops; 117 slot->release = &dummy_release; 118 slot->private = dslot; 119 120 retval = pci_hp_register(slot); 121 if (retval) { 122 err("pci_hp_register failed with error %d\n", retval); 123 goto error_dslot; 124 } 125 126 dslot->slot = slot; 127 dslot->dev = pci_dev_get(dev); 128 list_add (&dslot->node, &slot_list); 129 return retval; 130 131error_dslot: 132 kfree(dslot); 133error_info: 134 kfree(slot->info); 135error_slot: 136 kfree(slot); 137error: 138 return retval; 139} 140 141static int __init pci_scan_buses(void) 142{ 143 struct pci_dev *dev = NULL; 144 int retval = 0; 145 146 while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { 147 retval = add_slot(dev); 148 if (retval) { 149 pci_dev_put(dev); 150 break; 151 } 152 } 153 154 return retval; 155} 156 157static void remove_slot(struct dummy_slot *dslot) 158{ 159 int retval; 160 161 dbg("removing slot %s\n", dslot->slot->name); 162 retval = pci_hp_deregister(dslot->slot); 163 if (retval) 164 err("Problem unregistering a slot %s\n", dslot->slot->name); 165} 166 167/** 168 * Rescan slot. 169 * Tries hard not to re-enable already existing devices 170 * also handles scanning of subfunctions 171 * 172 * @param temp Device template. Should be set: bus and devfn. 173 */ 174static void pci_rescan_slot(struct pci_dev *temp) 175{ 176 struct pci_bus *bus = temp->bus; 177 struct pci_dev *dev; 178 int func; 179 int retval; 180 u8 hdr_type; 181 182 if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { 183 temp->hdr_type = hdr_type & 0x7f; 184 if ((dev = pci_get_slot(bus, temp->devfn)) != NULL) 185 pci_dev_put(dev); 186 else { 187 dev = pci_scan_single_device(bus, temp->devfn); 188 if (dev) { 189 dbg("New device on %s function %x:%x\n", 190 bus->name, temp->devfn >> 3, 191 temp->devfn & 7); 192 retval = pci_bus_add_device(dev); 193 if (retval) 194 dev_err(&dev->dev, "error adding " 195 "device, continuing.\n"); 196 else 197 add_slot(dev); 198 } 199 } 200 /* multifunction device? */ 201 if (!(hdr_type & 0x80)) 202 return; 203 204 /* continue scanning for other functions */ 205 for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) { 206 if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) 207 continue; 208 temp->hdr_type = hdr_type & 0x7f; 209 210 if ((dev = pci_get_slot(bus, temp->devfn)) != NULL) 211 pci_dev_put(dev); 212 else { 213 dev = pci_scan_single_device(bus, temp->devfn); 214 if (dev) { 215 dbg("New device on %s function %x:%x\n", 216 bus->name, temp->devfn >> 3, 217 temp->devfn & 7); 218 retval = pci_bus_add_device(dev); 219 if (retval) 220 dev_err(&dev->dev, "error adding " 221 "device, continuing.\n"); 222 else 223 add_slot(dev); 224 } 225 } 226 } 227 } 228} 229 230 231/** 232 * Rescan PCI bus. 233 * call pci_rescan_slot for each possible function of the bus 234 * 235 * @param bus 236 */ 237static void pci_rescan_bus(const struct pci_bus *bus) 238{ 239 unsigned int devfn; 240 struct pci_dev *dev; 241 dev = alloc_pci_dev(); 242 if (!dev) 243 return; 244 245 dev->bus = (struct pci_bus*)bus; 246 dev->sysdata = bus->sysdata; 247 for (devfn = 0; devfn < 0x100; devfn += 8) { 248 dev->devfn = devfn; 249 pci_rescan_slot(dev); 250 } 251 kfree(dev); 252} 253 254/* recursively scan all buses */ 255static void pci_rescan_buses(const struct list_head *list) 256{ 257 const struct list_head *l; 258 list_for_each(l,list) { 259 const struct pci_bus *b = pci_bus_b(l); 260 pci_rescan_bus(b); 261 pci_rescan_buses(&b->children); 262 } 263} 264 265/* initiate rescan of all pci buses */ 266static inline void pci_rescan(void) { 267 pci_rescan_buses(&pci_root_buses); 268} 269 270 271static int enable_slot(struct hotplug_slot *hotplug_slot) 272{ 273 /* mis-use enable_slot for rescanning of the pci bus */ 274 pci_rescan(); 275 return -ENODEV; 276} 277 278/* find the hotplug_slot for the pci_dev */ 279static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev) 280{ 281 struct dummy_slot *dslot; 282 283 list_for_each_entry(dslot, &slot_list, node) { 284 if (dslot->dev == dev) 285 return dslot->slot; 286 } 287 return NULL; 288} 289 290 291static int disable_slot(struct hotplug_slot *slot) 292{ 293 struct dummy_slot *dslot; 294 struct hotplug_slot *hslot; 295 struct pci_dev *dev; 296 int func; 297 298 if (!slot) 299 return -ENODEV; 300 dslot = slot->private; 301 302 dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name); 303 304 /* don't disable bridged devices just yet, we can't handle them easily... */ 305 if (dslot->dev->subordinate) { 306 err("Can't remove PCI devices with other PCI devices behind it yet.\n"); 307 return -ENODEV; 308 } 309 /* search for subfunctions and disable them first */ 310 if (!(dslot->dev->devfn & 7)) { 311 for (func = 1; func < 8; func++) { 312 dev = pci_get_slot(dslot->dev->bus, 313 dslot->dev->devfn + func); 314 if (dev) { 315 hslot = get_slot_from_dev(dev); 316 if (hslot) 317 disable_slot(hslot); 318 else { 319 err("Hotplug slot not found for subfunction of PCI device\n"); 320 return -ENODEV; 321 } 322 pci_dev_put(dev); 323 } else 324 dbg("No device in slot found\n"); 325 } 326 } 327 328 /* remove the device from the pci core */ 329 pci_remove_bus_device(dslot->dev); 330 331 /* blow away this sysfs entry and other parts. */ 332 remove_slot(dslot); 333 334 return 0; 335} 336 337static void cleanup_slots (void) 338{ 339 struct list_head *tmp; 340 struct list_head *next; 341 struct dummy_slot *dslot; 342 343 list_for_each_safe (tmp, next, &slot_list) { 344 dslot = list_entry (tmp, struct dummy_slot, node); 345 remove_slot(dslot); 346 } 347 348} 349 350static int __init dummyphp_init(void) 351{ 352 info(DRIVER_DESC "\n"); 353 354 return pci_scan_buses(); 355} 356 357 358static void __exit dummyphp_exit(void) 359{ 360 cleanup_slots(); 361} 362 363module_init(dummyphp_init); 364module_exit(dummyphp_exit); 365 366MODULE_AUTHOR(DRIVER_AUTHOR); 367MODULE_DESCRIPTION(DRIVER_DESC); 368MODULE_LICENSE("GPL"); 369module_param(debug, bool, S_IRUGO | S_IWUSR); 370MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 371