fakephp.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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. * This driver will "emulate" removing PCI devices from the system. If
  23. * the "power" file is written to with "0" then the specified PCI device
  24. * will be completely removed from the kernel.
  25. *
  26. * WARNING, this does NOT turn off the power to the PCI device. This is
  27. * a "logical" removal, not a physical or electrical removal.
  28. *
  29. * Use this module at your own risk, you have been warned!
  30. *
  31. * Enabling PCI devices is left as an exercise for the reader...
  32. *
  33. */
  34. #include <linux/config.h>
  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. #if !defined(MODULE)
  44. #define MY_NAME "fakephp"
  45. #else
  46. #define MY_NAME THIS_MODULE->name
  47. #endif
  48. #define dbg(format, arg...) \
  49. do { \
  50. if (debug) \
  51. printk(KERN_DEBUG "%s: " format, \
  52. MY_NAME , ## arg); \
  53. } while (0)
  54. #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
  55. #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
  56. #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
  57. #define DRIVER_DESC "Fake PCI Hot Plug Controller Driver"
  58. struct dummy_slot {
  59. struct list_head node;
  60. struct hotplug_slot *slot;
  61. struct pci_dev *dev;
  62. };
  63. static int debug;
  64. static LIST_HEAD(slot_list);
  65. static int enable_slot (struct hotplug_slot *slot);
  66. static int disable_slot (struct hotplug_slot *slot);
  67. static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
  68. .owner = THIS_MODULE,
  69. .enable_slot = enable_slot,
  70. .disable_slot = disable_slot,
  71. };
  72. static void dummy_release(struct hotplug_slot *slot)
  73. {
  74. struct dummy_slot *dslot = slot->private;
  75. list_del(&dslot->node);
  76. kfree(dslot->slot->info);
  77. kfree(dslot->slot);
  78. pci_dev_put(dslot->dev);
  79. kfree(dslot);
  80. }
  81. static int add_slot(struct pci_dev *dev)
  82. {
  83. struct dummy_slot *dslot;
  84. struct hotplug_slot *slot;
  85. int retval = -ENOMEM;
  86. slot = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
  87. if (!slot)
  88. goto error;
  89. memset(slot, 0, sizeof(*slot));
  90. slot->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
  91. if (!slot->info)
  92. goto error_slot;
  93. memset(slot->info, 0, sizeof(struct hotplug_slot_info));
  94. slot->info->power_status = 1;
  95. slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
  96. slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
  97. slot->name = &dev->dev.bus_id[0];
  98. dbg("slot->name = %s\n", slot->name);
  99. dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
  100. if (!dslot)
  101. goto error_info;
  102. slot->ops = &dummy_hotplug_slot_ops;
  103. slot->release = &dummy_release;
  104. slot->private = dslot;
  105. retval = pci_hp_register(slot);
  106. if (retval) {
  107. err("pci_hp_register failed with error %d\n", retval);
  108. goto error_dslot;
  109. }
  110. dslot->slot = slot;
  111. dslot->dev = pci_dev_get(dev);
  112. list_add (&dslot->node, &slot_list);
  113. return retval;
  114. error_dslot:
  115. kfree(dslot);
  116. error_info:
  117. kfree(slot->info);
  118. error_slot:
  119. kfree(slot);
  120. error:
  121. return retval;
  122. }
  123. static int __init pci_scan_buses(void)
  124. {
  125. struct pci_dev *dev = NULL;
  126. int retval = 0;
  127. while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
  128. retval = add_slot(dev);
  129. if (retval) {
  130. pci_dev_put(dev);
  131. break;
  132. }
  133. }
  134. return retval;
  135. }
  136. static void remove_slot(struct dummy_slot *dslot)
  137. {
  138. int retval;
  139. dbg("removing slot %s\n", dslot->slot->name);
  140. retval = pci_hp_deregister(dslot->slot);
  141. if (retval)
  142. err("Problem unregistering a slot %s\n", dslot->slot->name);
  143. }
  144. /**
  145. * Rescan slot.
  146. * Tries hard not to re-enable already existing devices
  147. * also handles scanning of subfunctions
  148. *
  149. * @param temp Device template. Should be set: bus and devfn.
  150. */
  151. static void pci_rescan_slot(struct pci_dev *temp)
  152. {
  153. struct pci_bus *bus = temp->bus;
  154. struct pci_dev *dev;
  155. int func;
  156. u8 hdr_type;
  157. if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
  158. temp->hdr_type = hdr_type & 0x7f;
  159. if (!pci_find_slot(bus->number, temp->devfn)) {
  160. dev = pci_scan_single_device(bus, temp->devfn);
  161. if (dev) {
  162. dbg("New device on %s function %x:%x\n",
  163. bus->name, temp->devfn >> 3,
  164. temp->devfn & 7);
  165. pci_bus_add_device(dev);
  166. add_slot(dev);
  167. }
  168. }
  169. /* multifunction device? */
  170. if (!(hdr_type & 0x80))
  171. return;
  172. /* continue scanning for other functions */
  173. for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
  174. if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
  175. continue;
  176. temp->hdr_type = hdr_type & 0x7f;
  177. if (!pci_find_slot(bus->number, temp->devfn)) {
  178. dev = pci_scan_single_device(bus, temp->devfn);
  179. if (dev) {
  180. dbg("New device on %s function %x:%x\n",
  181. bus->name, temp->devfn >> 3,
  182. temp->devfn & 7);
  183. pci_bus_add_device(dev);
  184. add_slot(dev);
  185. }
  186. }
  187. }
  188. }
  189. }
  190. /**
  191. * Rescan PCI bus.
  192. * call pci_rescan_slot for each possible function of the bus
  193. *
  194. * @param bus
  195. */
  196. static void pci_rescan_bus(const struct pci_bus *bus)
  197. {
  198. unsigned int devfn;
  199. struct pci_dev *dev;
  200. dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL);
  201. if (!dev)
  202. return;
  203. memset(dev, 0, sizeof(dev));
  204. dev->bus = (struct pci_bus*)bus;
  205. dev->sysdata = bus->sysdata;
  206. for (devfn = 0; devfn < 0x100; devfn += 8) {
  207. dev->devfn = devfn;
  208. pci_rescan_slot(dev);
  209. }
  210. kfree(dev);
  211. }
  212. /* recursively scan all buses */
  213. static void pci_rescan_buses(const struct list_head *list)
  214. {
  215. const struct list_head *l;
  216. list_for_each(l,list) {
  217. const struct pci_bus *b = pci_bus_b(l);
  218. pci_rescan_bus(b);
  219. pci_rescan_buses(&b->children);
  220. }
  221. }
  222. /* initiate rescan of all pci buses */
  223. static inline void pci_rescan(void) {
  224. pci_rescan_buses(&pci_root_buses);
  225. }
  226. static int enable_slot(struct hotplug_slot *hotplug_slot)
  227. {
  228. /* mis-use enable_slot for rescanning of the pci bus */
  229. pci_rescan();
  230. return -ENODEV;
  231. }
  232. /* find the hotplug_slot for the pci_dev */
  233. static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
  234. {
  235. struct dummy_slot *dslot;
  236. list_for_each_entry(dslot, &slot_list, node) {
  237. if (dslot->dev == dev)
  238. return dslot->slot;
  239. }
  240. return NULL;
  241. }
  242. static int disable_slot(struct hotplug_slot *slot)
  243. {
  244. struct dummy_slot *dslot;
  245. struct hotplug_slot *hslot;
  246. struct pci_dev *dev;
  247. int func;
  248. if (!slot)
  249. return -ENODEV;
  250. dslot = slot->private;
  251. dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name);
  252. /* don't disable bridged devices just yet, we can't handle them easily... */
  253. if (dslot->dev->subordinate) {
  254. err("Can't remove PCI devices with other PCI devices behind it yet.\n");
  255. return -ENODEV;
  256. }
  257. /* search for subfunctions and disable them first */
  258. if (!(dslot->dev->devfn & 7)) {
  259. for (func = 1; func < 8; func++) {
  260. dev = pci_find_slot(dslot->dev->bus->number,
  261. dslot->dev->devfn + func);
  262. if (dev) {
  263. hslot = get_slot_from_dev(dev);
  264. if (hslot)
  265. disable_slot(hslot);
  266. else {
  267. err("Hotplug slot not found for subfunction of PCI device\n");
  268. return -ENODEV;
  269. }
  270. } else
  271. dbg("No device in slot found\n");
  272. }
  273. }
  274. /* remove the device from the pci core */
  275. pci_remove_bus_device(dslot->dev);
  276. /* blow away this sysfs entry and other parts. */
  277. remove_slot(dslot);
  278. return 0;
  279. }
  280. static void cleanup_slots (void)
  281. {
  282. struct list_head *tmp;
  283. struct list_head *next;
  284. struct dummy_slot *dslot;
  285. list_for_each_safe (tmp, next, &slot_list) {
  286. dslot = list_entry (tmp, struct dummy_slot, node);
  287. remove_slot(dslot);
  288. }
  289. }
  290. static int __init dummyphp_init(void)
  291. {
  292. info(DRIVER_DESC "\n");
  293. return pci_scan_buses();
  294. }
  295. static void __exit dummyphp_exit(void)
  296. {
  297. cleanup_slots();
  298. }
  299. module_init(dummyphp_init);
  300. module_exit(dummyphp_exit);
  301. MODULE_AUTHOR(DRIVER_AUTHOR);
  302. MODULE_DESCRIPTION(DRIVER_DESC);
  303. MODULE_LICENSE("GPL");
  304. module_param(debug, bool, S_IRUGO | S_IWUSR);
  305. MODULE_PARM_DESC(debug, "Debugging mode enabled or not");