PageRenderTime 41ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/release/src-rt/linux/linux-2.6/drivers/pci/hotplug/fakephp.c

https://gitlab.com/envieidoc/advancedtomato2
C | 402 lines | 285 code | 59 blank | 58 comment | 37 complexity | d1410468edd8332cef04f938c293a318 MD5 | raw file
  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/kernel.h>
  35. #include <linux/module.h>
  36. #include <linux/pci.h>
  37. #include <linux/pci_hotplug.h>
  38. #include <linux/init.h>
  39. #include <linux/string.h>
  40. #include <linux/slab.h>
  41. #include <linux/workqueue.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. struct work_struct remove_work;
  63. unsigned long removed;
  64. };
  65. static int debug;
  66. static LIST_HEAD(slot_list);
  67. static struct workqueue_struct *dummyphp_wq;
  68. static void pci_rescan_worker(struct work_struct *work);
  69. static DECLARE_WORK(pci_rescan_work, pci_rescan_worker);
  70. static int enable_slot (struct hotplug_slot *slot);
  71. static int disable_slot (struct hotplug_slot *slot);
  72. static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
  73. .owner = THIS_MODULE,
  74. .enable_slot = enable_slot,
  75. .disable_slot = disable_slot,
  76. };
  77. static void dummy_release(struct hotplug_slot *slot)
  78. {
  79. struct dummy_slot *dslot = slot->private;
  80. list_del(&dslot->node);
  81. kfree(dslot->slot->info);
  82. kfree(dslot->slot);
  83. pci_dev_put(dslot->dev);
  84. kfree(dslot);
  85. }
  86. static int add_slot(struct pci_dev *dev)
  87. {
  88. struct dummy_slot *dslot;
  89. struct hotplug_slot *slot;
  90. int retval = -ENOMEM;
  91. slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
  92. if (!slot)
  93. goto error;
  94. slot->info = kzalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
  95. if (!slot->info)
  96. goto error_slot;
  97. slot->info->power_status = 1;
  98. slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
  99. slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
  100. slot->name = &dev->dev.bus_id[0];
  101. dbg("slot->name = %s\n", slot->name);
  102. dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
  103. if (!dslot)
  104. goto error_info;
  105. slot->ops = &dummy_hotplug_slot_ops;
  106. slot->release = &dummy_release;
  107. slot->private = dslot;
  108. retval = pci_hp_register(slot);
  109. if (retval) {
  110. err("pci_hp_register failed with error %d\n", retval);
  111. goto error_dslot;
  112. }
  113. dslot->slot = slot;
  114. dslot->dev = pci_dev_get(dev);
  115. list_add (&dslot->node, &slot_list);
  116. return retval;
  117. error_dslot:
  118. kfree(dslot);
  119. error_info:
  120. kfree(slot->info);
  121. error_slot:
  122. kfree(slot);
  123. error:
  124. return retval;
  125. }
  126. static int __init pci_scan_buses(void)
  127. {
  128. struct pci_dev *dev = NULL;
  129. int retval = 0;
  130. while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
  131. retval = add_slot(dev);
  132. if (retval) {
  133. pci_dev_put(dev);
  134. break;
  135. }
  136. }
  137. return retval;
  138. }
  139. static void remove_slot(struct dummy_slot *dslot)
  140. {
  141. int retval;
  142. dbg("removing slot %s\n", dslot->slot->name);
  143. retval = pci_hp_deregister(dslot->slot);
  144. if (retval)
  145. err("Problem unregistering a slot %s\n", dslot->slot->name);
  146. }
  147. /* called from the single-threaded workqueue handler to remove a slot */
  148. static void remove_slot_worker(struct work_struct *work)
  149. {
  150. struct dummy_slot *dslot =
  151. container_of(work, struct dummy_slot, remove_work);
  152. remove_slot(dslot);
  153. }
  154. /**
  155. * Rescan slot.
  156. * Tries hard not to re-enable already existing devices
  157. * also handles scanning of subfunctions
  158. *
  159. * @param temp Device template. Should be set: bus and devfn.
  160. */
  161. static void pci_rescan_slot(struct pci_dev *temp)
  162. {
  163. struct pci_bus *bus = temp->bus;
  164. struct pci_dev *dev;
  165. int func;
  166. int retval;
  167. u8 hdr_type;
  168. if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
  169. temp->hdr_type = hdr_type & 0x7f;
  170. if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
  171. pci_dev_put(dev);
  172. else {
  173. dev = pci_scan_single_device(bus, temp->devfn);
  174. if (dev) {
  175. dbg("New device on %s function %x:%x\n",
  176. bus->name, temp->devfn >> 3,
  177. temp->devfn & 7);
  178. retval = pci_bus_add_device(dev);
  179. if (retval)
  180. dev_err(&dev->dev, "error adding "
  181. "device, continuing.\n");
  182. else
  183. add_slot(dev);
  184. }
  185. }
  186. /* multifunction device? */
  187. if (!(hdr_type & 0x80))
  188. return;
  189. /* continue scanning for other functions */
  190. for (func = 1, temp->devfn++; func < 8; func++, temp->devfn++) {
  191. if (pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type))
  192. continue;
  193. temp->hdr_type = hdr_type & 0x7f;
  194. if ((dev = pci_get_slot(bus, temp->devfn)) != NULL)
  195. pci_dev_put(dev);
  196. else {
  197. dev = pci_scan_single_device(bus, temp->devfn);
  198. if (dev) {
  199. dbg("New device on %s function %x:%x\n",
  200. bus->name, temp->devfn >> 3,
  201. temp->devfn & 7);
  202. retval = pci_bus_add_device(dev);
  203. if (retval)
  204. dev_err(&dev->dev, "error adding "
  205. "device, continuing.\n");
  206. else
  207. add_slot(dev);
  208. }
  209. }
  210. }
  211. }
  212. }
  213. /**
  214. * Rescan PCI bus.
  215. * call pci_rescan_slot for each possible function of the bus
  216. *
  217. * @param bus
  218. */
  219. static void pci_rescan_bus(const struct pci_bus *bus)
  220. {
  221. unsigned int devfn;
  222. struct pci_dev *dev;
  223. dev = alloc_pci_dev();
  224. if (!dev)
  225. return;
  226. dev->bus = (struct pci_bus*)bus;
  227. dev->sysdata = bus->sysdata;
  228. for (devfn = 0; devfn < 0x100; devfn += 8) {
  229. dev->devfn = devfn;
  230. pci_rescan_slot(dev);
  231. }
  232. kfree(dev);
  233. }
  234. /* recursively scan all buses */
  235. static void pci_rescan_buses(const struct list_head *list)
  236. {
  237. const struct list_head *l;
  238. list_for_each(l,list) {
  239. const struct pci_bus *b = pci_bus_b(l);
  240. pci_rescan_bus(b);
  241. pci_rescan_buses(&b->children);
  242. }
  243. }
  244. /* initiate rescan of all pci buses */
  245. static inline void pci_rescan(void) {
  246. pci_rescan_buses(&pci_root_buses);
  247. }
  248. /* called from the single-threaded workqueue handler to rescan all pci buses */
  249. static void pci_rescan_worker(struct work_struct *work)
  250. {
  251. pci_rescan();
  252. }
  253. static int enable_slot(struct hotplug_slot *hotplug_slot)
  254. {
  255. /* mis-use enable_slot for rescanning of the pci bus */
  256. cancel_work_sync(&pci_rescan_work);
  257. queue_work(dummyphp_wq, &pci_rescan_work);
  258. return -ENODEV;
  259. }
  260. /* find the hotplug_slot for the pci_dev */
  261. static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
  262. {
  263. struct dummy_slot *dslot;
  264. list_for_each_entry(dslot, &slot_list, node) {
  265. if (dslot->dev == dev)
  266. return dslot->slot;
  267. }
  268. return NULL;
  269. }
  270. static int disable_slot(struct hotplug_slot *slot)
  271. {
  272. struct dummy_slot *dslot;
  273. struct hotplug_slot *hslot;
  274. struct pci_dev *dev;
  275. int func;
  276. if (!slot)
  277. return -ENODEV;
  278. dslot = slot->private;
  279. dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name);
  280. /* don't disable bridged devices just yet, we can't handle them easily... */
  281. if (dslot->dev->subordinate) {
  282. err("Can't remove PCI devices with other PCI devices behind it yet.\n");
  283. return -ENODEV;
  284. }
  285. if (test_and_set_bit(0, &dslot->removed)) {
  286. dbg("Slot already scheduled for removal\n");
  287. return -ENODEV;
  288. }
  289. /* search for subfunctions and disable them first */
  290. if (!(dslot->dev->devfn & 7)) {
  291. for (func = 1; func < 8; func++) {
  292. dev = pci_get_slot(dslot->dev->bus,
  293. dslot->dev->devfn + func);
  294. if (dev) {
  295. hslot = get_slot_from_dev(dev);
  296. if (hslot)
  297. disable_slot(hslot);
  298. else {
  299. err("Hotplug slot not found for subfunction of PCI device\n");
  300. return -ENODEV;
  301. }
  302. pci_dev_put(dev);
  303. } else
  304. dbg("No device in slot found\n");
  305. }
  306. }
  307. /* remove the device from the pci core */
  308. pci_remove_bus_device(dslot->dev);
  309. /* queue work item to blow away this sysfs entry and other parts. */
  310. INIT_WORK(&dslot->remove_work, remove_slot_worker);
  311. queue_work(dummyphp_wq, &dslot->remove_work);
  312. return 0;
  313. }
  314. static void cleanup_slots (void)
  315. {
  316. struct list_head *tmp;
  317. struct list_head *next;
  318. struct dummy_slot *dslot;
  319. destroy_workqueue(dummyphp_wq);
  320. list_for_each_safe (tmp, next, &slot_list) {
  321. dslot = list_entry (tmp, struct dummy_slot, node);
  322. remove_slot(dslot);
  323. }
  324. }
  325. static int __init dummyphp_init(void)
  326. {
  327. info(DRIVER_DESC "\n");
  328. dummyphp_wq = create_singlethread_workqueue(MY_NAME);
  329. if (!dummyphp_wq)
  330. return -ENOMEM;
  331. return pci_scan_buses();
  332. }
  333. static void __exit dummyphp_exit(void)
  334. {
  335. cleanup_slots();
  336. }
  337. module_init(dummyphp_init);
  338. module_exit(dummyphp_exit);
  339. MODULE_AUTHOR(DRIVER_AUTHOR);
  340. MODULE_DESCRIPTION(DRIVER_DESC);
  341. MODULE_LICENSE("GPL");
  342. module_param(debug, bool, S_IRUGO | S_IWUSR);
  343. MODULE_PARM_DESC(debug, "Debugging mode enabled or not");