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 
dev_sync_probe_notifier_call(struct notifier_block * nb,unsigned long action,void * data)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 
dev_sync_probe_init(struct dev_sync_probe_data * data)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 
dev_sync_probe_register(struct dev_sync_probe_data * data,struct platform_device_info * pdevinfo)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 
dev_sync_probe_unregister(struct dev_sync_probe_data * data)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