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