Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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");