legacy_fakephp.c 3.7 KB

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