at master 2.6 kB view raw
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 17static 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 42void 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} 48EXPORT_SYMBOL_GPL(dev_sync_probe_init); 49 50int 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} 84EXPORT_SYMBOL_GPL(dev_sync_probe_register); 85 86void 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} 92EXPORT_SYMBOL_GPL(dev_sync_probe_unregister); 93 94MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>"); 95MODULE_AUTHOR("Koichiro Den <koichiro.den@canonical.com>"); 96MODULE_DESCRIPTION("Utilities for synchronous fake device creation"); 97MODULE_LICENSE("GPL");