History

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.

Greg Kroah-Hartman committed 1 year ago

Commit 35fa2d81 parent a64dcfb6 files · +310 −1Browse files

Unified · Split

6 files
  1. Documentation/driver-api/infrastructure.rst +6 −0
  2. drivers/base/Makefile +1 −1
  3. drivers/base/base.h +1 −0
  4. drivers/base/faux.c +232 −0
  5. drivers/base/init.c +1 −0
  6. include/linux/device/faux.h +69 −0
Documentation/driver-api/infrastructure.rst+6−0
@@ -41,6 +41,12 @@ Device Drivers Base · 40 unmodified lines
41.. kernel-doc:: drivers/base/class.c41.. kernel-doc:: drivers/base/class.c
42 :export:42 :export:
43 43
44.. kernel-doc:: include/linux/device/faux.h
45 :internal:
46
47.. kernel-doc:: drivers/base/faux.c
48 :export:
49
44.. kernel-doc:: drivers/base/node.c50.. kernel-doc:: drivers/base/node.c
45 :internal:51 :internal:
46 52
drivers/base/Makefile+1−1
@@ -6,7 +6,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \ · 5 unmodified lines
6 cpu.o firmware.o init.o map.o devres.o \6 cpu.o firmware.o init.o map.o devres.o \
7 attribute_container.o transport_class.o \7 attribute_container.o transport_class.o \
8 topology.o container.o property.o cacheinfo.o \8 topology.o container.o property.o cacheinfo.o \
9 swnode.o9 swnode.o faux.o
10obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o10obj-$(CONFIG_AUXILIARY_BUS) += auxiliary.o
11obj-$(CONFIG_DEVTMPFS) += devtmpfs.o11obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
12obj-y += power/12obj-y += power/
drivers/base/base.h+1−0
@@ -137,6 +137,7 @@ int hypervisor_init(void); · 136 unmodified lines
137static inline int hypervisor_init(void) { return 0; }137static inline int hypervisor_init(void) { return 0; }
138#endif138#endif
139int platform_bus_init(void);139int platform_bus_init(void);
140int faux_bus_init(void);
140void cpu_dev_init(void);141void cpu_dev_init(void);
141void container_dev_init(void);142void container_dev_init(void);
142#ifdef CONFIG_AUXILIARY_BUS143#ifdef CONFIG_AUXILIARY_BUS
drivers/base/faux.c+232−0
@@ -0,0 +1,232 @@
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 */
25struct 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
31static struct device faux_bus_root = {
32 .init_name = "faux",
33};
34
35static 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
41static 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
54static 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
64static const struct bus_type faux_bus_type = {
65 .name = "faux",
66 .match = faux_match,
67 .probe = faux_probe,
68 .remove = faux_remove,
69};
70
71static struct device_driver faux_driver = {
72 .name = "faux_driver",
73 .bus = &faux_bus_type,
74 .probe_type = PROBE_FORCE_SYNCHRONOUS,
75};
76
77static 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 */
111struct 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}
152EXPORT_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 */
177struct 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}
183EXPORT_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 */
192void 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}
204EXPORT_SYMBOL_GPL(faux_device_destroy);
205
206int __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
226error_driver:
227 bus_unregister(&faux_bus_type);
228
229error_bus:
230 device_unregister(&faux_bus_root);
231 return ret;
232}
drivers/base/init.c+1−0
@@ -32,6 +32,7 @@ void __init driver_init(void) · 31 unmodified lines
32 /* These are also core pieces, but must come after the32 /* These are also core pieces, but must come after the
33 * core core pieces.33 * core core pieces.
34 */34 */
35 faux_bus_init();
35 of_core_init();36 of_core_init();
36 platform_bus_init();37 platform_bus_init();
37 auxiliary_bus_init();38 auxiliary_bus_init();
include/linux/device/faux.h+69−0
@@ -0,0 +1,69 @@
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#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 */
29struct 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 */
45struct faux_device_ops {
46 int (*probe)(struct faux_device *faux_dev);
47 void (*remove)(struct faux_device *faux_dev);
48};
49
50struct faux_device *faux_device_create(const char *name,
51 struct device *parent,
52 const struct faux_device_ops *faux_ops);
53struct 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);
57void faux_device_destroy(struct faux_device *faux_dev);
58
59static inline void *faux_device_get_drvdata(const struct faux_device *faux_dev)
60{
61 return dev_get_drvdata(&faux_dev->dev);
62}
63
64static 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_ */