97 lines · 2.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Common code for drivers creating fake platform devices. * * Provides synchronous device creation: waits for probe completion and * returns the probe success or error status to the device creator. * * Copyright (C) 2021 Bartosz Golaszewski <brgl@bgdev.pl> * Copyright (C) 2025 Koichiro Den <koichiro.den@canonical.com> */ #include <linux/device.h>#include <linux/slab.h> #include "dev-sync-probe.h" static int dev_sync_probe_notifier_call(struct notifier_block *nb,					unsigned long action, void *data){	struct dev_sync_probe_data *pdata;	struct device *dev = data; 	pdata = container_of(nb, struct dev_sync_probe_data, bus_notifier);	if (!device_match_name(dev, pdata->name))		return NOTIFY_DONE; 	switch (action) {	case BUS_NOTIFY_BOUND_DRIVER:		pdata->driver_bound = true;		break;	case BUS_NOTIFY_DRIVER_NOT_BOUND:		pdata->driver_bound = false;		break;	default:		return NOTIFY_DONE;	} 	complete(&pdata->probe_completion);	return NOTIFY_OK;} void dev_sync_probe_init(struct dev_sync_probe_data *data){	memset(data, 0, sizeof(*data));	init_completion(&data->probe_completion);	data->bus_notifier.notifier_call = dev_sync_probe_notifier_call;}EXPORT_SYMBOL_GPL(dev_sync_probe_init); int dev_sync_probe_register(struct dev_sync_probe_data *data,			    struct platform_device_info *pdevinfo){	struct platform_device *pdev;	char *name; 	name = kasprintf(GFP_KERNEL, "%s.%d", pdevinfo->name, pdevinfo->id);	if (!name)		return -ENOMEM; 	data->driver_bound = false;	data->name = name;	reinit_completion(&data->probe_completion);	bus_register_notifier(&platform_bus_type, &data->bus_notifier); 	pdev = platform_device_register_full(pdevinfo);	if (IS_ERR(pdev)) {		bus_unregister_notifier(&platform_bus_type, &data->bus_notifier);		kfree(data->name);		return PTR_ERR(pdev);	} 	wait_for_completion(&data->probe_completion);	bus_unregister_notifier(&platform_bus_type, &data->bus_notifier); 	if (!data->driver_bound) {		platform_device_unregister(pdev);		kfree(data->name);		return -ENXIO;	} 	data->pdev = pdev;	return 0;}EXPORT_SYMBOL_GPL(dev_sync_probe_register); void dev_sync_probe_unregister(struct dev_sync_probe_data *data){	platform_device_unregister(data->pdev);	kfree(data->name);	data->pdev = NULL;}EXPORT_SYMBOL_GPL(dev_sync_probe_unregister); MODULE_AUTHOR("Bartosz Golaszewski <brgl@bgdev.pl>");MODULE_AUTHOR("Koichiro Den <koichiro.den@canonical.com>");MODULE_DESCRIPTION("Utilities for synchronous fake device creation");MODULE_LICENSE("GPL");