fakephp.c 3.7 KB

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