summaryrefslogtreecommitdiff
path: root/drivers/base/driver.cc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/driver.cc')
-rw-r--r--drivers/base/driver.cc72
1 files changed, 72 insertions, 0 deletions
diff --git a/drivers/base/driver.cc b/drivers/base/driver.cc
new file mode 100644
index 0000000..ce15b6d
--- /dev/null
+++ b/drivers/base/driver.cc
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+/*
+ * drivers/base/driver.cc — driver_register / driver_unregister.
+ *
+ * Mirrors Linux drivers/base/driver.c. Registers a device_driver on
+ * its bus and tries to bind any unbound matching devices.
+ *
+ * Reference: docs/specs/storage_stack.md §4.3
+ */
+
+#include <lume/device.h>
+#include <lume/container_of.h>
+#include <lume/klog.h>
+#include <lume/kprintf.h>
+#include <lume/errno.h>
+
+/* Implemented in core.cc — bind a device to a driver on its bus.
+ * Returns 1 on success, 0 on no-match-or-probe-fail. */
+int device_try_bind(device* dev, device_driver* drv);
+
+int driver_register(device_driver* drv)
+{
+ if (drv == nullptr || drv->bus == nullptr) {
+ return -EINVAL;
+ }
+ if (!drv->bus->registered) {
+ kprintf("[driver] register '%s' fail: bus '%s' not registered\n",
+ drv->name != nullptr ? drv->name : "?",
+ drv->bus->name != nullptr ? drv->bus->name : "?");
+ return -ENODEV;
+ }
+
+ drv->bus_node.init();
+ drv->devices.init();
+
+ LockGuard g(drv->bus->lock);
+ list_add_tail(&drv->bus_node, &drv->bus->drivers);
+
+ /* Try to bind any unbound devices already on the bus. */
+ list_node* cur = drv->bus->devices.next;
+ while (cur != &drv->bus->devices) {
+ device* dev = list_entry<device, &device::bus_list>(cur);
+ cur = cur->next;
+ if (dev->driver == nullptr) {
+ device_try_bind(dev, drv);
+ }
+ }
+ return 0;
+}
+
+void driver_unregister(device_driver* drv)
+{
+ if (drv == nullptr || drv->bus == nullptr) {
+ return;
+ }
+
+ LockGuard g(drv->bus->lock);
+
+ /* Unbind every device bound to this driver. */
+ while (!drv->devices.is_empty()) {
+ list_node* first = drv->devices.next;
+ device* dev = list_entry<device, &device::driver_list>(first);
+ if (drv->remove != nullptr) {
+ drv->remove(dev);
+ }
+ list_del_init(&dev->driver_list);
+ dev->driver = nullptr;
+ }
+ list_del_init(&drv->bus_node);
+}