fakephp.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /* 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. #include <linux/module.h>
  13. #include <linux/kernel.h>
  14. #include <linux/types.h>
  15. #include <linux/list.h>
  16. #include <linux/kobject.h>
  17. #include <linux/sysfs.h>
  18. #include <linux/init.h>
  19. #include <linux/pci.h>
  20. #include <linux/device.h>
  21. #include <linux/slab.h>
  22. #include "../pci.h"
  23. struct legacy_slot {
  24. struct kobject kobj;
  25. struct pci_dev *dev;
  26. struct list_head list;
  27. };
  28. static LIST_HEAD(legacy_list);
  29. static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
  30. char *buf)
  31. {
  32. struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
  33. strcpy(buf, "1\n");
  34. return 2;
  35. }
  36. static void remove_callback(void *data)
  37. {
  38. pci_stop_and_remove_bus_device((struct pci_dev *)data);
  39. }
  40. static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
  41. const char *buf, size_t len)
  42. {
  43. struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
  44. unsigned long val;
  45. if (strict_strtoul(buf, 0, &val) < 0)
  46. return -EINVAL;
  47. if (val)
  48. pci_rescan_bus(slot->dev->bus);
  49. else
  50. sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
  51. slot->dev, THIS_MODULE);
  52. return len;
  53. }
  54. static struct attribute *legacy_attrs[] = {
  55. &(struct attribute){ .name = "power", .mode = 0644 },
  56. NULL,
  57. };
  58. static void legacy_release(struct kobject *kobj)
  59. {
  60. struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
  61. pci_dev_put(slot->dev);
  62. kfree(slot);
  63. }
  64. static struct kobj_type legacy_ktype = {
  65. .sysfs_ops = &(const struct sysfs_ops){
  66. .store = legacy_store, .show = legacy_show
  67. },
  68. .release = &legacy_release,
  69. .default_attrs = legacy_attrs,
  70. };
  71. static int legacy_add_slot(struct pci_dev *pdev)
  72. {
  73. struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
  74. if (!slot)
  75. return -ENOMEM;
  76. if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
  77. &pci_slots_kset->kobj, "%s",
  78. dev_name(&pdev->dev))) {
  79. dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
  80. return -EINVAL;
  81. }
  82. slot->dev = pci_dev_get(pdev);
  83. list_add(&slot->list, &legacy_list);
  84. return 0;
  85. }
  86. static int legacy_notify(struct notifier_block *nb,
  87. unsigned long action, void *data)
  88. {
  89. struct pci_dev *pdev = to_pci_dev(data);
  90. if (action == BUS_NOTIFY_ADD_DEVICE) {
  91. legacy_add_slot(pdev);
  92. } else if (action == BUS_NOTIFY_DEL_DEVICE) {
  93. struct legacy_slot *slot;
  94. list_for_each_entry(slot, &legacy_list, list)
  95. if (slot->dev == pdev)
  96. goto found;
  97. dev_warn(&pdev->dev, "Missing legacy fake slot?");
  98. return -ENODEV;
  99. found:
  100. kobject_del(&slot->kobj);
  101. list_del(&slot->list);
  102. kobject_put(&slot->kobj);
  103. }
  104. return 0;
  105. }
  106. static struct notifier_block legacy_notifier = {
  107. .notifier_call = legacy_notify
  108. };
  109. static int __init init_legacy(void)
  110. {
  111. struct pci_dev *pdev = NULL;
  112. /* Add existing devices */
  113. for_each_pci_dev(pdev)
  114. legacy_add_slot(pdev);
  115. /* Be alerted of any new ones */
  116. bus_register_notifier(&pci_bus_type, &legacy_notifier);
  117. return 0;
  118. }
  119. module_init(init_legacy);
  120. static void __exit remove_legacy(void)
  121. {
  122. struct legacy_slot *slot, *tmp;
  123. bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
  124. list_for_each_entry_safe(slot, tmp, &legacy_list, list) {
  125. list_del(&slot->list);
  126. kobject_del(&slot->kobj);
  127. kobject_put(&slot->kobj);
  128. }
  129. }
  130. module_exit(remove_legacy);
  131. MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
  132. MODULE_DESCRIPTION("Legacy version of the fakephp interface");
  133. MODULE_LICENSE("GPL");