| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * Copyright (c) 2025 Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 
|---|
| 4 | * Copyright (c) 2025 The Linux Foundation | 
|---|
| 5 | * | 
|---|
| 6 | * A "simple" faux bus that allows devices to be created and added | 
|---|
| 7 | * automatically to it.  This is to be used whenever you need to create a | 
|---|
| 8 | * device that is not associated with any "real" system resources, and do | 
|---|
| 9 | * not want to have to deal with a bus/driver binding logic.  It is | 
|---|
| 10 | * intended to be very simple, with only a create and a destroy function | 
|---|
| 11 | * available. | 
|---|
| 12 | */ | 
|---|
| 13 | #include <linux/err.h> | 
|---|
| 14 | #include <linux/init.h> | 
|---|
| 15 | #include <linux/slab.h> | 
|---|
| 16 | #include <linux/string.h> | 
|---|
| 17 | #include <linux/container_of.h> | 
|---|
| 18 | #include <linux/device/faux.h> | 
|---|
| 19 | #include "base.h" | 
|---|
| 20 |  | 
|---|
| 21 | /* | 
|---|
| 22 | * Internal wrapper structure so we can hold a pointer to the | 
|---|
| 23 | * faux_device_ops for this device. | 
|---|
| 24 | */ | 
|---|
| 25 | struct faux_object { | 
|---|
| 26 | struct faux_device faux_dev; | 
|---|
| 27 | const struct faux_device_ops *faux_ops; | 
|---|
| 28 | const struct attribute_group **groups; | 
|---|
| 29 | }; | 
|---|
| 30 | #define to_faux_object(dev) container_of_const(dev, struct faux_object, faux_dev.dev) | 
|---|
| 31 |  | 
|---|
| 32 | static struct device faux_bus_root = { | 
|---|
| 33 | .init_name	= "faux", | 
|---|
| 34 | }; | 
|---|
| 35 |  | 
|---|
| 36 | static int faux_match(struct device *dev, const struct device_driver *drv) | 
|---|
| 37 | { | 
|---|
| 38 | /* Match always succeeds, we only have one driver */ | 
|---|
| 39 | return 1; | 
|---|
| 40 | } | 
|---|
| 41 |  | 
|---|
| 42 | static int faux_probe(struct device *dev) | 
|---|
| 43 | { | 
|---|
| 44 | struct faux_object *faux_obj = to_faux_object(dev); | 
|---|
| 45 | struct faux_device *faux_dev = &faux_obj->faux_dev; | 
|---|
| 46 | const struct faux_device_ops *faux_ops = faux_obj->faux_ops; | 
|---|
| 47 | int ret; | 
|---|
| 48 |  | 
|---|
| 49 | if (faux_ops && faux_ops->probe) { | 
|---|
| 50 | ret = faux_ops->probe(faux_dev); | 
|---|
| 51 | if (ret) | 
|---|
| 52 | return ret; | 
|---|
| 53 | } | 
|---|
| 54 |  | 
|---|
| 55 | /* | 
|---|
| 56 | * Add groups after the probe succeeds to ensure resources are | 
|---|
| 57 | * initialized correctly | 
|---|
| 58 | */ | 
|---|
| 59 | ret = device_add_groups(dev, groups: faux_obj->groups); | 
|---|
| 60 | if (ret && faux_ops && faux_ops->remove) | 
|---|
| 61 | faux_ops->remove(faux_dev); | 
|---|
| 62 |  | 
|---|
| 63 | return ret; | 
|---|
| 64 | } | 
|---|
| 65 |  | 
|---|
| 66 | static void faux_remove(struct device *dev) | 
|---|
| 67 | { | 
|---|
| 68 | struct faux_object *faux_obj = to_faux_object(dev); | 
|---|
| 69 | struct faux_device *faux_dev = &faux_obj->faux_dev; | 
|---|
| 70 | const struct faux_device_ops *faux_ops = faux_obj->faux_ops; | 
|---|
| 71 |  | 
|---|
| 72 | device_remove_groups(dev, groups: faux_obj->groups); | 
|---|
| 73 |  | 
|---|
| 74 | if (faux_ops && faux_ops->remove) | 
|---|
| 75 | faux_ops->remove(faux_dev); | 
|---|
| 76 | } | 
|---|
| 77 |  | 
|---|
| 78 | static const struct bus_type faux_bus_type = { | 
|---|
| 79 | .name		= "faux", | 
|---|
| 80 | .match		= faux_match, | 
|---|
| 81 | .probe		= faux_probe, | 
|---|
| 82 | .remove		= faux_remove, | 
|---|
| 83 | }; | 
|---|
| 84 |  | 
|---|
| 85 | static struct device_driver faux_driver = { | 
|---|
| 86 | .name		= "faux_driver", | 
|---|
| 87 | .bus		= &faux_bus_type, | 
|---|
| 88 | .probe_type	= PROBE_FORCE_SYNCHRONOUS, | 
|---|
| 89 | .suppress_bind_attrs = true, | 
|---|
| 90 | }; | 
|---|
| 91 |  | 
|---|
| 92 | static void faux_device_release(struct device *dev) | 
|---|
| 93 | { | 
|---|
| 94 | struct faux_object *faux_obj = to_faux_object(dev); | 
|---|
| 95 |  | 
|---|
| 96 | kfree(objp: faux_obj); | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | /** | 
|---|
| 100 | * faux_device_create_with_groups - Create and register with the driver | 
|---|
| 101 | *		core a faux device and populate the device with an initial | 
|---|
| 102 | *		set of sysfs attributes. | 
|---|
| 103 | * @name:	The name of the device we are adding, must be unique for | 
|---|
| 104 | *		all faux devices. | 
|---|
| 105 | * @parent:	Pointer to a potential parent struct device.  If set to | 
|---|
| 106 | *		NULL, the device will be created in the "root" of the faux | 
|---|
| 107 | *		device tree in sysfs. | 
|---|
| 108 | * @faux_ops:	struct faux_device_ops that the new device will call back | 
|---|
| 109 | *		into, can be NULL. | 
|---|
| 110 | * @groups:	The set of sysfs attributes that will be created for this | 
|---|
| 111 | *		device when it is registered with the driver core. | 
|---|
| 112 | * | 
|---|
| 113 | * Create a new faux device and register it in the driver core properly. | 
|---|
| 114 | * If present, callbacks in @faux_ops will be called with the device that | 
|---|
| 115 | * for the caller to do something with at the proper time given the | 
|---|
| 116 | * device's lifecycle. | 
|---|
| 117 | * | 
|---|
| 118 | * Note, when this function is called, the functions specified in struct | 
|---|
| 119 | * faux_ops can be called before the function returns, so be prepared for | 
|---|
| 120 | * everything to be properly initialized before that point in time.  If the | 
|---|
| 121 | * probe callback (if one is present) does NOT succeed, the creation of the | 
|---|
| 122 | * device will fail and NULL will be returned. | 
|---|
| 123 | * | 
|---|
| 124 | * Return: | 
|---|
| 125 | * * NULL if an error happened with creating the device | 
|---|
| 126 | * * pointer to a valid struct faux_device that is registered with sysfs | 
|---|
| 127 | */ | 
|---|
| 128 | struct faux_device *faux_device_create_with_groups(const char *name, | 
|---|
| 129 | struct device *parent, | 
|---|
| 130 | const struct faux_device_ops *faux_ops, | 
|---|
| 131 | const struct attribute_group **groups) | 
|---|
| 132 | { | 
|---|
| 133 | struct faux_object *faux_obj; | 
|---|
| 134 | struct faux_device *faux_dev; | 
|---|
| 135 | struct device *dev; | 
|---|
| 136 | int ret; | 
|---|
| 137 |  | 
|---|
| 138 | faux_obj = kzalloc(sizeof(*faux_obj), GFP_KERNEL); | 
|---|
| 139 | if (!faux_obj) | 
|---|
| 140 | return NULL; | 
|---|
| 141 |  | 
|---|
| 142 | /* Save off the callbacks and groups so we can use them in the future */ | 
|---|
| 143 | faux_obj->faux_ops = faux_ops; | 
|---|
| 144 | faux_obj->groups = groups; | 
|---|
| 145 |  | 
|---|
| 146 | /* Initialize the device portion and register it with the driver core */ | 
|---|
| 147 | faux_dev = &faux_obj->faux_dev; | 
|---|
| 148 | dev = &faux_dev->dev; | 
|---|
| 149 |  | 
|---|
| 150 | device_initialize(dev); | 
|---|
| 151 | dev->release = faux_device_release; | 
|---|
| 152 | if (parent) | 
|---|
| 153 | dev->parent = parent; | 
|---|
| 154 | else | 
|---|
| 155 | dev->parent = &faux_bus_root; | 
|---|
| 156 | dev->bus = &faux_bus_type; | 
|---|
| 157 | dev_set_name(dev, name: "%s", name); | 
|---|
| 158 | device_set_pm_not_required(dev); | 
|---|
| 159 |  | 
|---|
| 160 | ret = device_add(dev); | 
|---|
| 161 | if (ret) { | 
|---|
| 162 | pr_err( "%s: device_add for faux device '%s' failed with %d\n", | 
|---|
| 163 | __func__, name, ret); | 
|---|
| 164 | put_device(dev); | 
|---|
| 165 | return NULL; | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | /* | 
|---|
| 169 | * Verify that we did bind the driver to the device (i.e. probe worked), | 
|---|
| 170 | * if not, let's fail the creation as trying to guess if probe was | 
|---|
| 171 | * successful is almost impossible to determine by the caller. | 
|---|
| 172 | */ | 
|---|
| 173 | if (!dev->driver) { | 
|---|
| 174 | dev_dbg(dev, "probe did not succeed, tearing down the device\n"); | 
|---|
| 175 | faux_device_destroy(faux_dev); | 
|---|
| 176 | faux_dev = NULL; | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | return faux_dev; | 
|---|
| 180 | } | 
|---|
| 181 | EXPORT_SYMBOL_GPL(faux_device_create_with_groups); | 
|---|
| 182 |  | 
|---|
| 183 | /** | 
|---|
| 184 | * faux_device_create - create and register with the driver core a faux device | 
|---|
| 185 | * @name:	The name of the device we are adding, must be unique for all | 
|---|
| 186 | *		faux devices. | 
|---|
| 187 | * @parent:	Pointer to a potential parent struct device.  If set to | 
|---|
| 188 | *		NULL, the device will be created in the "root" of the faux | 
|---|
| 189 | *		device tree in sysfs. | 
|---|
| 190 | * @faux_ops:	struct faux_device_ops that the new device will call back | 
|---|
| 191 | *		into, can be NULL. | 
|---|
| 192 | * | 
|---|
| 193 | * Create a new faux device and register it in the driver core properly. | 
|---|
| 194 | * If present, callbacks in @faux_ops will be called with the device that | 
|---|
| 195 | * for the caller to do something with at the proper time given the | 
|---|
| 196 | * device's lifecycle. | 
|---|
| 197 | * | 
|---|
| 198 | * Note, when this function is called, the functions specified in struct | 
|---|
| 199 | * faux_ops can be called before the function returns, so be prepared for | 
|---|
| 200 | * everything to be properly initialized before that point in time. | 
|---|
| 201 | * | 
|---|
| 202 | * Return: | 
|---|
| 203 | * * NULL if an error happened with creating the device | 
|---|
| 204 | * * pointer to a valid struct faux_device that is registered with sysfs | 
|---|
| 205 | */ | 
|---|
| 206 | struct faux_device *faux_device_create(const char *name, | 
|---|
| 207 | struct device *parent, | 
|---|
| 208 | const struct faux_device_ops *faux_ops) | 
|---|
| 209 | { | 
|---|
| 210 | return faux_device_create_with_groups(name, parent, faux_ops, NULL); | 
|---|
| 211 | } | 
|---|
| 212 | EXPORT_SYMBOL_GPL(faux_device_create); | 
|---|
| 213 |  | 
|---|
| 214 | /** | 
|---|
| 215 | * faux_device_destroy - destroy a faux device | 
|---|
| 216 | * @faux_dev:	faux device to destroy | 
|---|
| 217 | * | 
|---|
| 218 | * Unregisters and cleans up a device that was created with a call to | 
|---|
| 219 | * faux_device_create() | 
|---|
| 220 | */ | 
|---|
| 221 | void faux_device_destroy(struct faux_device *faux_dev) | 
|---|
| 222 | { | 
|---|
| 223 | struct device *dev = &faux_dev->dev; | 
|---|
| 224 |  | 
|---|
| 225 | if (!faux_dev) | 
|---|
| 226 | return; | 
|---|
| 227 |  | 
|---|
| 228 | device_del(dev); | 
|---|
| 229 |  | 
|---|
| 230 | /* The final put_device() will clean up the memory we allocated for this device. */ | 
|---|
| 231 | put_device(dev); | 
|---|
| 232 | } | 
|---|
| 233 | EXPORT_SYMBOL_GPL(faux_device_destroy); | 
|---|
| 234 |  | 
|---|
| 235 | int __init faux_bus_init(void) | 
|---|
| 236 | { | 
|---|
| 237 | int ret; | 
|---|
| 238 |  | 
|---|
| 239 | ret = device_register(dev: &faux_bus_root); | 
|---|
| 240 | if (ret) { | 
|---|
| 241 | put_device(dev: &faux_bus_root); | 
|---|
| 242 | return ret; | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | ret = bus_register(bus: &faux_bus_type); | 
|---|
| 246 | if (ret) | 
|---|
| 247 | goto error_bus; | 
|---|
| 248 |  | 
|---|
| 249 | ret = driver_register(drv: &faux_driver); | 
|---|
| 250 | if (ret) | 
|---|
| 251 | goto error_driver; | 
|---|
| 252 |  | 
|---|
| 253 | return ret; | 
|---|
| 254 |  | 
|---|
| 255 | error_driver: | 
|---|
| 256 | bus_unregister(bus: &faux_bus_type); | 
|---|
| 257 |  | 
|---|
| 258 | error_bus: | 
|---|
| 259 | device_unregister(dev: &faux_bus_root); | 
|---|
| 260 | return ret; | 
|---|
| 261 | } | 
|---|
| 262 |  | 
|---|