|
| 1 | +.. SPDX-License-Identifier: GPL-2.0-only |
| 2 | +
|
| 3 | +============= |
| 4 | +Auxiliary Bus |
| 5 | +============= |
| 6 | + |
| 7 | +In some subsystems, the functionality of the core device (PCI/ACPI/other) is |
| 8 | +too complex for a single device to be managed by a monolithic driver |
| 9 | +(e.g. Sound Open Firmware), multiple devices might implement a common |
| 10 | +intersection of functionality (e.g. NICs + RDMA), or a driver may want to |
| 11 | +export an interface for another subsystem to drive (e.g. SIOV Physical Function |
| 12 | +export Virtual Function management). A split of the functinoality into child- |
| 13 | +devices representing sub-domains of functionality makes it possible to |
| 14 | +compartmentalize, layer, and distribute domain-specific concerns via a Linux |
| 15 | +device-driver model. |
| 16 | + |
| 17 | +An example for this kind of requirement is the audio subsystem where a single |
| 18 | +IP is handling multiple entities such as HDMI, Soundwire, local devices such as |
| 19 | +mics/speakers etc. The split for the core's functionality can be arbitrary or |
| 20 | +be defined by the DSP firmware topology and include hooks for test/debug. This |
| 21 | +allows for the audio core device to be minimal and focused on hardware-specific |
| 22 | +control and communication. |
| 23 | + |
| 24 | +Each auxiliary_device represents a part of its parent functionality. The |
| 25 | +generic behavior can be extended and specialized as needed by encapsulating an |
| 26 | +auxiliary_device within other domain-specific structures and the use of .ops |
| 27 | +callbacks. Devices on the auxiliary bus do not share any structures and the use |
| 28 | +of a communication channel with the parent is domain-specific. |
| 29 | + |
| 30 | +Note that ops are intended as a way to augment instance behavior within a class |
| 31 | +of auxiliary devices, it is not the mechanism for exporting common |
| 32 | +infrastructure from the parent. Consider EXPORT_SYMBOL_NS() to convey |
| 33 | +infrastructure from the parent module to the auxiliary module(s). |
| 34 | + |
| 35 | + |
| 36 | +When Should the Auxiliary Bus Be Used |
| 37 | +===================================== |
| 38 | + |
| 39 | +The auxiliary bus is to be used when a driver and one or more kernel modules, |
| 40 | +who share a common header file with the driver, need a mechanism to connect and |
| 41 | +provide access to a shared object allocated by the auxiliary_device's |
| 42 | +registering driver. The registering driver for the auxiliary_device(s) and the |
| 43 | +kernel module(s) registering auxiliary_drivers can be from the same subsystem, |
| 44 | +or from multiple subsystems. |
| 45 | + |
| 46 | +The emphasis here is on a common generic interface that keeps subsystem |
| 47 | +customization out of the bus infrastructure. |
| 48 | + |
| 49 | +One example is a PCI network device that is RDMA-capable and exports a child |
| 50 | +device to be driven by an auxiliary_driver in the RDMA subsystem. The PCI |
| 51 | +driver allocates and registers an auxiliary_device for each physical |
| 52 | +function on the NIC. The RDMA driver registers an auxiliary_driver that claims |
| 53 | +each of these auxiliary_devices. This conveys data/ops published by the parent |
| 54 | +PCI device/driver to the RDMA auxiliary_driver. |
| 55 | + |
| 56 | +Another use case is for the PCI device to be split out into multiple sub |
| 57 | +functions. For each sub function an auxiliary_device is created. A PCI sub |
| 58 | +function driver binds to such devices that creates its own one or more class |
| 59 | +devices. A PCI sub function auxiliary device is likely to be contained in a |
| 60 | +struct with additional attributes such as user defined sub function number and |
| 61 | +optional attributes such as resources and a link to the parent device. These |
| 62 | +attributes could be used by systemd/udev; and hence should be initialized |
| 63 | +before a driver binds to an auxiliary_device. |
| 64 | + |
| 65 | +A key requirement for utilizing the auxiliary bus is that there is no |
| 66 | +dependency on a physical bus, device, register accesses or regmap support. |
| 67 | +These individual devices split from the core cannot live on the platform bus as |
| 68 | +they are not physical devices that are controlled by DT/ACPI. The same |
| 69 | +argument applies for not using MFD in this scenario as MFD relies on individual |
| 70 | +function devices being physical devices. |
| 71 | + |
| 72 | +Auxiliary Device |
| 73 | +================ |
| 74 | + |
| 75 | +An auxiliary_device represents a part of its parent device's functionality. It |
| 76 | +is given a name that, combined with the registering drivers KBUILD_MODNAME, |
| 77 | +creates a match_name that is used for driver binding, and an id that combined |
| 78 | +with the match_name provide a unique name to register with the bus subsystem. |
| 79 | + |
| 80 | +Registering an auxiliary_device is a two-step process. First call |
| 81 | +auxiliary_device_init(), which checks several aspects of the auxiliary_device |
| 82 | +struct and performs a device_initialize(). After this step completes, any |
| 83 | +error state must have a call to auxiliary_device_uninit() in its resolution path. |
| 84 | +The second step in registering an auxiliary_device is to perform a call to |
| 85 | +auxiliary_device_add(), which sets the name of the device and add the device to |
| 86 | +the bus. |
| 87 | + |
| 88 | +Unregistering an auxiliary_device is also a two-step process to mirror the |
| 89 | +register process. First call auxiliary_device_delete(), then call |
| 90 | +auxiliary_device_uninit(). |
| 91 | + |
| 92 | +.. code-block:: c |
| 93 | +
|
| 94 | + struct auxiliary_device { |
| 95 | + struct device dev; |
| 96 | + const char *name; |
| 97 | + u32 id; |
| 98 | + }; |
| 99 | +
|
| 100 | +If two auxiliary_devices both with a match_name "mod.foo" are registered onto |
| 101 | +the bus, they must have unique id values (e.g. "x" and "y") so that the |
| 102 | +registered devices names are "mod.foo.x" and "mod.foo.y". If match_name + id |
| 103 | +are not unique, then the device_add fails and generates an error message. |
| 104 | + |
| 105 | +The auxiliary_device.dev.type.release or auxiliary_device.dev.release must be |
| 106 | +populated with a non-NULL pointer to successfully register the auxiliary_device. |
| 107 | + |
| 108 | +The auxiliary_device.dev.parent must also be populated. |
| 109 | + |
| 110 | +Auxiliary Device Memory Model and Lifespan |
| 111 | +------------------------------------------ |
| 112 | + |
| 113 | +The registering driver is the entity that allocates memory for the |
| 114 | +auxiliary_device and register it on the auxiliary bus. It is important to note |
| 115 | +that, as opposed to the platform bus, the registering driver is wholly |
| 116 | +responsible for the management for the memory used for the driver object. |
| 117 | + |
| 118 | +A parent object, defined in the shared header file, contains the |
| 119 | +auxiliary_device. It also contains a pointer to the shared object(s), which |
| 120 | +also is defined in the shared header. Both the parent object and the shared |
| 121 | +object(s) are allocated by the registering driver. This layout allows the |
| 122 | +auxiliary_driver's registering module to perform a container_of() call to go |
| 123 | +from the pointer to the auxiliary_device, that is passed during the call to the |
| 124 | +auxiliary_driver's probe function, up to the parent object, and then have |
| 125 | +access to the shared object(s). |
| 126 | + |
| 127 | +The memory for the auxiliary_device is freed only in its release() callback |
| 128 | +flow as defined by its registering driver. |
| 129 | + |
| 130 | +The memory for the shared object(s) must have a lifespan equal to, or greater |
| 131 | +than, the lifespan of the memory for the auxiliary_device. The auxiliary_driver |
| 132 | +should only consider that this shared object is valid as long as the |
| 133 | +auxiliary_device is still registered on the auxiliary bus. It is up to the |
| 134 | +registering driver to manage (e.g. free or keep available) the memory for the |
| 135 | +shared object beyond the life of the auxiliary_device. |
| 136 | + |
| 137 | +The registering driver must unregister all auxiliary devices before its own |
| 138 | +driver.remove() is completed. |
| 139 | + |
| 140 | +Auxiliary Drivers |
| 141 | +================= |
| 142 | + |
| 143 | +Auxiliary drivers follow the standard driver model convention, where |
| 144 | +discovery/enumeration is handled by the core, and drivers |
| 145 | +provide probe() and remove() methods. They support power management |
| 146 | +and shutdown notifications using the standard conventions. |
| 147 | + |
| 148 | +.. code-block:: c |
| 149 | +
|
| 150 | + struct auxiliary_driver { |
| 151 | + int (*probe)(struct auxiliary_device *, |
| 152 | + const struct auxiliary_device_id *id); |
| 153 | + int (*remove)(struct auxiliary_device *); |
| 154 | + void (*shutdown)(struct auxiliary_device *); |
| 155 | + int (*suspend)(struct auxiliary_device *, pm_message_t); |
| 156 | + int (*resume)(struct auxiliary_device *); |
| 157 | + struct device_driver driver; |
| 158 | + const struct auxiliary_device_id *id_table; |
| 159 | + }; |
| 160 | +
|
| 161 | +Auxiliary drivers register themselves with the bus by calling |
| 162 | +auxiliary_driver_register(). The id_table contains the match_names of auxiliary |
| 163 | +devices that a driver can bind with. |
| 164 | + |
| 165 | +Example Usage |
| 166 | +============= |
| 167 | + |
| 168 | +Auxiliary devices are created and registered by a subsystem-level core device |
| 169 | +that needs to break up its functionality into smaller fragments. One way to |
| 170 | +extend the scope of an auxiliary_device is to encapsulate it within a domain- |
| 171 | +pecific structure defined by the parent device. This structure contains the |
| 172 | +auxiliary_device and any associated shared data/callbacks needed to establish |
| 173 | +the connection with the parent. |
| 174 | + |
| 175 | +An example is: |
| 176 | + |
| 177 | +.. code-block:: c |
| 178 | +
|
| 179 | + struct foo { |
| 180 | + struct auxiliary_device auxdev; |
| 181 | + void (*connect)(struct auxiliary_device *auxdev); |
| 182 | + void (*disconnect)(struct auxiliary_device *auxdev); |
| 183 | + void *data; |
| 184 | + }; |
| 185 | +
|
| 186 | +The parent device then registers the auxiliary_device by calling |
| 187 | +auxiliary_device_init(), and then auxiliary_device_add(), with the pointer to |
| 188 | +the auxdev member of the above structure. The parent provides a name for the |
| 189 | +auxiliary_device that, combined with the parent's KBUILD_MODNAME, creates a |
| 190 | +match_name that is be used for matching and binding with a driver. |
| 191 | + |
| 192 | +Whenever an auxiliary_driver is registered, based on the match_name, the |
| 193 | +auxiliary_driver's probe() is invoked for the matching devices. The |
| 194 | +auxiliary_driver can also be encapsulated inside custom drivers that make the |
| 195 | +core device's functionality extensible by adding additional domain-specific ops |
| 196 | +as follows: |
| 197 | + |
| 198 | +.. code-block:: c |
| 199 | +
|
| 200 | + struct my_ops { |
| 201 | + void (*send)(struct auxiliary_device *auxdev); |
| 202 | + void (*receive)(struct auxiliary_device *auxdev); |
| 203 | + }; |
| 204 | +
|
| 205 | +
|
| 206 | + struct my_driver { |
| 207 | + struct auxiliary_driver auxiliary_drv; |
| 208 | + const struct my_ops ops; |
| 209 | + }; |
| 210 | +
|
| 211 | +An example of this type of usage is: |
| 212 | + |
| 213 | +.. code-block:: c |
| 214 | +
|
| 215 | + const struct auxiliary_device_id my_auxiliary_id_table[] = { |
| 216 | + { .name = "foo_mod.foo_dev" }, |
| 217 | + { }, |
| 218 | + }; |
| 219 | +
|
| 220 | + const struct my_ops my_custom_ops = { |
| 221 | + .send = my_tx, |
| 222 | + .receive = my_rx, |
| 223 | + }; |
| 224 | +
|
| 225 | + const struct my_driver my_drv = { |
| 226 | + .auxiliary_drv = { |
| 227 | + .name = "myauxiliarydrv", |
| 228 | + .id_table = my_auxiliary_id_table, |
| 229 | + .probe = my_probe, |
| 230 | + .remove = my_remove, |
| 231 | + .shutdown = my_shutdown, |
| 232 | + }, |
| 233 | + .ops = my_custom_ops, |
| 234 | + }; |
0 commit comments