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