dev-sync-probe.c 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Common code for drivers creating fake platform devices.
  4. *
  5. * Provides synchronous device creation: waits for probe completion and
  6. * returns the probe success or error status to the device creator.
  7. *
  8. * Copyright (C) 2021 Bartosz Golaszewski <brgl@bgdev.pl>
  9. * Copyright (C) 2025 Koichiro Den <koichiro.den@canonical.com>
  10. */
  11. #include <linux/device.h>
  12. #include <linux/slab.h>
  13. #include "dev-sync-probe.h"
  14. static int dev_sync_probe_notifier_call(struct notifier_block *nb,
  15. unsigned long action, void *data)
  16. {
  17. struct dev_sync_probe_data *pdata;
  18. struct device *dev = data;
  19. pdata = container_of(nb, struct dev_sync_probe_data, bus_notifier);
  20. if (!device_match_name(dev, pdata->name))
  21. return NOTIFY_DONE;
  22. switch (action) {
  23. case BUS_NOTIFY_BOUND_DRIVER:
  24. pdata->driver_bound = true;
  25. break;
  26. case BUS_NOTIFY_DRIVER_NOT_BOUND:
  27. pdata->driver_bound = false;
  28. break;
  29. default:
  30. return NOTIFY_DONE;
  31. }
  32. complete(&pdata->probe_completion);
  33. return NOTIFY_OK;
  34. }
  35. void dev_sync_probe_init(struct dev_sync_probe_data *data)
  36. {
  37. memset(data, 0, sizeof(*data));
  38. init_completion(&data->probe_completion);
  39. data->bus_notifier.notifier_call = dev_sync_probe_notifier_call;
  40. }
  41. EXPORT_SYMBOL_GPL(dev_sync_probe_init);
  42. int dev_sync_probe_register(struct dev_sync_probe_data *data,
  43. struct platform_device_info *pdevinfo)
  44. {
  45. struct platform_device *pdev;
  46. char *name;
  47. name = kasprintf(GFP_KERNEL, "%s.%d", pdevinfo->name, pdevinfo->id);
  48. if (!name)
  49. return -ENOMEM;
  50. data->driver_bound = false;
  51. data->name = name;
  52. reinit_completion(&data->probe_completion);
  53. bus_register_notifier(&platform_bus_type, &data->bus_notifier);
  54. pdev = platform_device_register_full(pdevinfo);
  55. if (IS_ERR(pdev)) {
  56. bus_unregister_notifier(&platform_bus_type, &data->bus_notifier);
  57. kfree(data->name);
  58. return PTR_ERR(pdev);
  59. }
  60. wait_for_completion(&data->probe_completion);
  61. bus_unregister_notifier(&platform_bus_type, &data->bus_notifier);
  62. if (!data->driver_bound) {
  63. platform_device_unregister(pdev);
  64. kfree(data->name);
  65. return -ENXIO;
  66. }
  67. data->pdev = pdev;
  68. return 0;
  69. }
  70. EXPORT_SYMBOL_GPL(dev_sync_probe_register);
  71. void dev_sync_probe_unregister(struct dev_sync_probe_data *data)
  72. {
  73. platform_device_unregister(data->pdev);
  74. kfree(data->name);
  75. data->pdev = NULL;
  76. }
  77. EXPORT_SYMBOL_GPL(dev_sync_probe_unregister);
  78. MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");
  79. MODULE_AUTHOR("Koichiro Den <koichiro.den@canonical.com>");
  80. MODULE_DESCRIPTION("Utilities for synchronous fake device creation");
  81. MODULE_LICENSE("GPL");