EXAMPLE(9)
β¨ EXAMPLE(9) β¨
okay so this is the EXAMPLE(9) page from the Kernel API Manual, and it's honestly kind of a big deal β it's the whole "how do I split my driver into cute little pieces" tutorial. let's get into it bestie π«Ά
π EXAMPLE
so here's the vibe: auxiliary devices get created and registered by a subsystem-level core device that needs to break up its functionality into smaller fragments. think of it like the main character device deciding it has too much going on and delegating π
one way to extend the scope of an auxiliary_device is to encapsulate it inside a domain-specific structure defined by the parent device. that structure holds the auxiliary_device plus any associated shared data/callbacks you need to establish the connection back to the parent. it's giving "found family" honestly.
an example is:
struct foo {
struct auxiliary_device auxdev;
void (*connect)(struct auxiliary_device *auxdev);
void (*disconnect)(struct auxiliary_device *auxdev);
void *data;
};
so then the parent device registers the auxiliary_device by calling auxiliary_device_init, and then auxiliary_device_add, passing it the pointer to the auxdev member of that structure up there. the parent also provides a name for the auxiliary_device that, combined with the parent's KBUILD_MODNAME, creates a match_name that's used for matching and binding with a driver. match made in heaven, no cap β¨
whenever an auxiliary_driver gets registered, based on that match_name, the auxiliary_driver's probe is invoked for the matching devices. and listen β the auxiliary_driver can also be encapsulated inside custom drivers that make the core device's functionality extensible by adding extra domain-specific ops, like this:
struct my_ops {
void (*send)(struct auxiliary_device *auxdev);
void (*receive)(struct auxiliary_device *auxdev);
};
struct my_driver {
struct auxiliary_driver auxiliary_drv;
const struct my_ops ops;
};
an example of this type of usage is:
const struct auxiliary_device_id my_auxiliary_id_table[] = {
{ .name = "foo_mod.foo_dev" },
{ },
};
const struct my_ops my_custom_ops = {
.send = my_tx,
.receive = my_rx,
};
const struct my_driver my_drv = {
.auxiliary_drv = {
.name = "myauxiliarydrv",
.id_table = my_auxiliary_id_table,
.probe = my_probe,
.remove = my_remove,
.shutdown = my_shutdown,
},
.ops = my_custom_ops,
};
β οΈ okay but please note β this custom ops approach is valid, but it is hard to implement right without global locks per-device to protect from auxiliary_drv removal during a call to those ops. and on top of that, this implementation lacks proper module dependency, which causes load/unload races between auxiliary parent and device modules. it's giving "chaotic" and not in the fun way π
the most easiest way to provide these ops reliably without needing to have a lock is to EXPORT_SYMBOL*() them and rely on the already existing modules infrastructure for validity and correct dependencies chains. slay, let the kernel do the work for you π
π¦ SEE ALSO
- Kernel file
./drivers/base/auxiliary.cPURPOSE(9) USAGE(9)auxiliary_device_init(9)__auxiliary_device_add(9)__auxiliary_driver_register(9)auxiliary_driver_unregister(9)auxiliary_device_create(9)auxiliary_device_destroy(9)__devm_auxiliary_device_create(9)dev_is_auxiliary(9)
π that's the whole page, bestie! this one came from ./drivers/base and it's dated October 2026 β go forth and split your functionality into smaller fragments, ily, bye! πΈπ
π¦ where this page comes from
- nixpkgs unstable: linux-manual 7.3.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms