Skip to content

Commit 35fa2d8

Browse files
committed
driver core: add a faux bus for use when a simple device/bus is needed
Many drivers abuse the platform driver/bus system as it provides a simple way to create and bind a device to a driver-specific set of probe/release functions. Instead of doing that, and wasting all of the memory associated with a platform device, here is a "faux" bus that can be used instead. Reviewed-by: Jonathan Cameron <[email protected]> Reviewed-by: Danilo Krummrich <[email protected]> Reviewed-by: Lyude Paul <[email protected]> Reviewed-by: Thomas Weißschuh <[email protected]> Reviewed-by: Zijun Hu <[email protected]> Link: https://lore.kernel.org/r/2025021026-atlantic-gibberish-3f0c@gregkh Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a64dcfb commit 35fa2d8

File tree

6 files changed

+310
-1
lines changed

6 files changed

+310
-1
lines changed

Documentation/driver-api/infrastructure.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ Device Drivers Base
4141
.. kernel-doc:: drivers/base/class.c
4242
:export:
4343

44+
.. kernel-doc:: include/linux/device/faux.h
45+
:internal:
46+
47+
.. kernel-doc:: drivers/base/faux.c
48+
:export:
49+
4450
.. kernel-doc:: drivers/base/node.c
4551
:internal:
4652

drivers/base/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
66
cpu.o firmware.o init.o map.o devres.o \
77
attribute_container.o transport_class.o \
88
topology.o container.o property.o cacheinfo.o \
9-
swnode.o
9+
swnode.o faux.o
1010
obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
1111
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
1212
obj-y += power/

drivers/base/base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ int hypervisor_init(void);
137137
static inline int hypervisor_init(void) { return 0; }
138138
#endif
139139
int platform_bus_init(void);
140+
int faux_bus_init(void);
140141
void cpu_dev_init(void);
141142
void container_dev_init(void);
142143
#ifdef CONFIG_AUXILIARY_BUS

drivers/base/faux.c

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

drivers/base/init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ void __init driver_init(void)
3232
/* These are also core pieces, but must come after the
3333
* core core pieces.
3434
*/
35+
faux_bus_init();
3536
of_core_init();
3637
platform_bus_init();
3738
auxiliary_bus_init();

include/linux/device/faux.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2025 Greg Kroah-Hartman <[email protected]>
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+
#ifndef _FAUX_DEVICE_H_
14+
#define _FAUX_DEVICE_H_
15+
16+
#include <linux/container_of.h>
17+
#include <linux/device.h>
18+
19+
/**
20+
* struct faux_device - a "faux" device
21+
* @dev: internal struct device of the object
22+
*
23+
* A simple faux device that can be created/destroyed. To be used when a
24+
* driver only needs to have a device to "hang" something off. This can be
25+
* used for downloading firmware or other basic tasks. Use this instead of
26+
* a struct platform_device if the device has no resources assigned to
27+
* it at all.
28+
*/
29+
struct faux_device {
30+
struct device dev;
31+
};
32+
#define to_faux_device(x) container_of_const((x), struct faux_device, dev)
33+
34+
/**
35+
* struct faux_device_ops - a set of callbacks for a struct faux_device
36+
* @probe: called when a faux device is probed by the driver core
37+
* before the device is fully bound to the internal faux bus
38+
* code. If probe succeeds, return 0, otherwise return a
39+
* negative error number to stop the probe sequence from
40+
* succeeding.
41+
* @remove: called when a faux device is removed from the system
42+
*
43+
* Both @probe and @remove are optional, if not needed, set to NULL.
44+
*/
45+
struct faux_device_ops {
46+
int (*probe)(struct faux_device *faux_dev);
47+
void (*remove)(struct faux_device *faux_dev);
48+
};
49+
50+
struct faux_device *faux_device_create(const char *name,
51+
struct device *parent,
52+
const struct faux_device_ops *faux_ops);
53+
struct faux_device *faux_device_create_with_groups(const char *name,
54+
struct device *parent,
55+
const struct faux_device_ops *faux_ops,
56+
const struct attribute_group **groups);
57+
void faux_device_destroy(struct faux_device *faux_dev);
58+
59+
static inline void *faux_device_get_drvdata(const struct faux_device *faux_dev)
60+
{
61+
return dev_get_drvdata(&faux_dev->dev);
62+
}
63+
64+
static inline void faux_device_set_drvdata(struct faux_device *faux_dev, void *data)
65+
{
66+
dev_set_drvdata(&faux_dev->dev, data);
67+
}
68+
69+
#endif /* _FAUX_DEVICE_H_ */

0 commit comments

Comments
 (0)