at v2.6.18 356 lines 8.2 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/init.h> 39#include <linux/string.h> 40#include <linux/slab.h> 41#include "pci_hotplug.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 u8 hdr_type; 180 if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) { 181 temp->hdr_type = hdr_type & 0x7f; 182 if (!pci_find_slot(bus->number, temp->devfn)) { 183 dev = pci_scan_single_device(bus, temp->devfn); 184 if (dev) { 185 dbg("New device on %s function %x:%x\n", 186 bus->name, temp->devfn >> 3, 187 temp->devfn & 7); 188 pci_bus_add_device(dev); 189 add_slot(dev); 190 } 191 } 192 /* multifunction device? */ 193 if (!(hdr_type & 0x80)) 194 return; 195 196 /* continue scanning for other functions */ 197 for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) { 198 if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) 199 continue; 200 temp->hdr_type = hdr_type & 0x7f; 201 202 if (!pci_find_slot(bus->number, temp->devfn)) { 203 dev = pci_scan_single_device(bus, temp->devfn); 204 if (dev) { 205 dbg("New device on %s function %x:%x\n", 206 bus->name, temp->devfn >> 3, 207 temp->devfn & 7); 208 pci_bus_add_device(dev); 209 add_slot(dev); 210 } 211 } 212 } 213 } 214} 215 216 217/** 218 * Rescan PCI bus. 219 * call pci_rescan_slot for each possible function of the bus 220 * 221 * @param bus 222 */ 223static void pci_rescan_bus(const struct pci_bus *bus) 224{ 225 unsigned int devfn; 226 struct pci_dev *dev; 227 dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); 228 if (!dev) 229 return; 230 231 dev->bus = (struct pci_bus*)bus; 232 dev->sysdata = bus->sysdata; 233 for (devfn = 0; devfn < 0x100; devfn += 8) { 234 dev->devfn = devfn; 235 pci_rescan_slot(dev); 236 } 237 kfree(dev); 238} 239 240/* recursively scan all buses */ 241static void pci_rescan_buses(const struct list_head *list) 242{ 243 const struct list_head *l; 244 list_for_each(l,list) { 245 const struct pci_bus *b = pci_bus_b(l); 246 pci_rescan_bus(b); 247 pci_rescan_buses(&b->children); 248 } 249} 250 251/* initiate rescan of all pci buses */ 252static inline void pci_rescan(void) { 253 pci_rescan_buses(&pci_root_buses); 254} 255 256 257static int enable_slot(struct hotplug_slot *hotplug_slot) 258{ 259 /* mis-use enable_slot for rescanning of the pci bus */ 260 pci_rescan(); 261 return -ENODEV; 262} 263 264/* find the hotplug_slot for the pci_dev */ 265static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev) 266{ 267 struct dummy_slot *dslot; 268 269 list_for_each_entry(dslot, &slot_list, node) { 270 if (dslot->dev == dev) 271 return dslot->slot; 272 } 273 return NULL; 274} 275 276 277static int disable_slot(struct hotplug_slot *slot) 278{ 279 struct dummy_slot *dslot; 280 struct hotplug_slot *hslot; 281 struct pci_dev *dev; 282 int func; 283 284 if (!slot) 285 return -ENODEV; 286 dslot = slot->private; 287 288 dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name); 289 290 /* don't disable bridged devices just yet, we can't handle them easily... */ 291 if (dslot->dev->subordinate) { 292 err("Can't remove PCI devices with other PCI devices behind it yet.\n"); 293 return -ENODEV; 294 } 295 /* search for subfunctions and disable them first */ 296 if (!(dslot->dev->devfn & 7)) { 297 for (func = 1; func < 8; func++) { 298 dev = pci_find_slot(dslot->dev->bus->number, 299 dslot->dev->devfn + func); 300 if (dev) { 301 hslot = get_slot_from_dev(dev); 302 if (hslot) 303 disable_slot(hslot); 304 else { 305 err("Hotplug slot not found for subfunction of PCI device\n"); 306 return -ENODEV; 307 } 308 } else 309 dbg("No device in slot found\n"); 310 } 311 } 312 313 /* remove the device from the pci core */ 314 pci_remove_bus_device(dslot->dev); 315 316 /* blow away this sysfs entry and other parts. */ 317 remove_slot(dslot); 318 319 return 0; 320} 321 322static void cleanup_slots (void) 323{ 324 struct list_head *tmp; 325 struct list_head *next; 326 struct dummy_slot *dslot; 327 328 list_for_each_safe (tmp, next, &slot_list) { 329 dslot = list_entry (tmp, struct dummy_slot, node); 330 remove_slot(dslot); 331 } 332 333} 334 335static int __init dummyphp_init(void) 336{ 337 info(DRIVER_DESC "\n"); 338 339 return pci_scan_buses(); 340} 341 342 343static void __exit dummyphp_exit(void) 344{ 345 cleanup_slots(); 346} 347 348module_init(dummyphp_init); 349module_exit(dummyphp_exit); 350 351MODULE_AUTHOR(DRIVER_AUTHOR); 352MODULE_DESCRIPTION(DRIVER_DESC); 353MODULE_LICENSE("GPL"); 354module_param(debug, bool, S_IRUGO | S_IWUSR); 355MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 356