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 12 #include <linux/device.h> 13 #include <linux/slab.h> 14 15 #include "dev-sync-probe.h" 16 17 static int dev_sync_probe_notifier_call(struct notifier_block *nb, 18 unsigned long action, void *data) 19 { 20 struct dev_sync_probe_data *pdata; 21 struct device *dev = data; 22 23 pdata = container_of(nb, struct dev_sync_probe_data, bus_notifier); 24 if (!device_match_name(dev, pdata->name)) 25 return NOTIFY_DONE; 26 27 switch (action) { 28 case BUS_NOTIFY_BOUND_DRIVER: 29 pdata->driver_bound = true; 30 break; 31 case BUS_NOTIFY_DRIVER_NOT_BOUND: 32 pdata->driver_bound = false; 33 break; 34 default: 35 return NOTIFY_DONE; 36 } 37 38 complete(&pdata->probe_completion); 39 return NOTIFY_OK; 40 } 41 42 void dev_sync_probe_init(struct dev_sync_probe_data *data) 43 { 44 memset(data, 0, sizeof(*data)); 45 init_completion(&data->probe_completion); 46 data->bus_notifier.notifier_call = dev_sync_probe_notifier_call; 47 } 48 EXPORT_SYMBOL_GPL(dev_sync_probe_init); 49 50 int dev_sync_probe_register(struct dev_sync_probe_data *data, 51 struct platform_device_info *pdevinfo) 52 { 53 struct platform_device *pdev; 54 char *name; 55 56 name = kasprintf(GFP_KERNEL, "%s.%d", pdevinfo->name, pdevinfo->id); 57 if (!name) 58 return -ENOMEM; 59 60 data->driver_bound = false; 61 data->name = name; 62 reinit_completion(&data->probe_completion); 63 bus_register_notifier(&platform_bus_type, &data->bus_notifier); 64 65 pdev = platform_device_register_full(pdevinfo); 66 if (IS_ERR(pdev)) { 67 bus_unregister_notifier(&platform_bus_type, &data->bus_notifier); 68 kfree(data->name); 69 return PTR_ERR(pdev); 70 } 71 72 wait_for_completion(&data->probe_completion); 73 bus_unregister_notifier(&platform_bus_type, &data->bus_notifier); 74 75 if (!data->driver_bound) { 76 platform_device_unregister(pdev); 77 kfree(data->name); 78 return -ENXIO; 79 } 80 81 data->pdev = pdev; 82 return 0; 83 } 84 EXPORT_SYMBOL_GPL(dev_sync_probe_register); 85 86 void dev_sync_probe_unregister(struct dev_sync_probe_data *data) 87 { 88 platform_device_unregister(data->pdev); 89 kfree(data->name); 90 data->pdev = NULL; 91 } 92 EXPORT_SYMBOL_GPL(dev_sync_probe_unregister); 93 94 MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>"); 95 MODULE_AUTHOR("Koichiro Den <koichiro.den@canonical.com>"); 96 MODULE_DESCRIPTION("Utilities for synchronous fake device creation"); 97 MODULE_LICENSE("GPL"); 98