diff options
| -rw-r--r-- | kapi/kapi/devices/bus.hpp | 5 | ||||
| -rw-r--r-- | kapi/kapi/devices/device.hpp | 27 | ||||
| -rw-r--r-- | kapi/kapi/devices/driver_registry.hpp | 51 | ||||
| -rw-r--r-- | kapi/kapi/devices/manager.hpp | 6 | ||||
| -rw-r--r-- | kapi/kapi/test_support/devices.hpp | 1 | ||||
| -rw-r--r-- | kernel/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | kernel/kapi/devices.cpp | 19 | ||||
| -rw-r--r-- | kernel/kapi/devices/bus.cpp | 5 | ||||
| -rw-r--r-- | kernel/kapi/devices/device.cpp | 29 | ||||
| -rw-r--r-- | kernel/kapi/devices/driver_registry.cpp | 135 |
10 files changed, 277 insertions, 2 deletions
diff --git a/kapi/kapi/devices/bus.hpp b/kapi/kapi/devices/bus.hpp index 6356e166..0e654072 100644 --- a/kapi/kapi/devices/bus.hpp +++ b/kapi/kapi/devices/bus.hpp @@ -41,6 +41,11 @@ namespace kapi::devices [[nodiscard]] auto children() const -> std::span<kstd::shared_ptr<device> const>; + //! Get this bus's protocol facet, if present. + //! + //! @return this bus's protocol facet if it has one, nullptr otherwise. + [[nodiscard]] virtual auto protocol() -> struct bus_protocol *; + protected: //! Enumerate the devices on the bus. //! diff --git a/kapi/kapi/devices/device.hpp b/kapi/kapi/devices/device.hpp index 6ffd052d..c1b2065b 100644 --- a/kapi/kapi/devices/device.hpp +++ b/kapi/kapi/devices/device.hpp @@ -14,6 +14,15 @@ namespace kapi::devices //! @addtogroup kapi-devices-kernel-defined //! @{ + enum struct state + { + uninitialized, + present, + bound, + failed, + removed, + }; + /** * @brief Base device identified by a name. */ @@ -65,6 +74,21 @@ namespace kapi::devices //! @return a shared pointer to the parent bus, or nullptr if this device has no parent. [[nodiscard]] auto parent() const -> kstd::shared_ptr<struct bus>; + //! Get this devices current state. + [[nodiscard]] auto state() const noexcept -> state; + + //! Set the lifecycle state of this device. + auto set_state(enum state state) -> void; + + //! Bind this device to the given driver. + auto bind_driver(kstd::weak_ptr<struct driver> driver) -> void; + + //! Get the driver data associated with this device. + [[nodiscard]] auto driver_data() const -> kstd::shared_ptr<void> const &; + + //! Set the driver data for this device. + auto set_driver_data(kstd::shared_ptr<void> data) -> void; + protected: auto virtual query_interface(interface interface) -> void *; @@ -75,6 +99,9 @@ namespace kapi::devices kstd::string m_name; kstd::weak_ptr<bus> m_parent; + devices::state m_state{state::uninitialized}; + kstd::weak_ptr<struct driver> m_driver{}; + kstd::shared_ptr<void> m_driver_data{}; }; //! @} diff --git a/kapi/kapi/devices/driver_registry.hpp b/kapi/kapi/devices/driver_registry.hpp new file mode 100644 index 00000000..26a81678 --- /dev/null +++ b/kapi/kapi/devices/driver_registry.hpp @@ -0,0 +1,51 @@ +#ifndef TEACHOS_KAPI_DEVICES_DRIVER_REGISTRY_HPP +#define TEACHOS_KAPI_DEVICES_DRIVER_REGISTRY_HPP + +#include <kapi/devices/device.hpp> +#include <kapi/devices/driver.hpp> + +#include <kstd/memory.hpp> +#include <kstd/vector.hpp> + +namespace kapi::devices +{ + + //! @addtogroup kapi-devices-kernel-defined + //! @{ + + struct driver_registry + { + driver_registry() = default; + + auto static init() -> void; + + auto static get() -> driver_registry &; + + //! Register a new driver. + //! + //! The driver will be tried against all devices in the device tree that are not yet bound by a different driver. + //! + //! @param driver The driver to register. + auto add(kstd::shared_ptr<driver> driver) -> void; + + //! Notify the registry about a new device having been attached to the device tree. + //! + //! The device will be tried against all currently registered drivers. + //! + //! @param device The device that was attached. + auto device_attached(kstd::shared_ptr<device> const & device) -> void; + + private: + //! Try to bind a given device against every currently registered driver, if it is not bound yet. + //! + //! Driver matching will occur in driver registration order. The highest priority driver will win. + auto try_bind(kstd::shared_ptr<device> const & device) -> void; + + kstd::vector<kstd::shared_ptr<driver>> m_drivers; + }; + + //! @} + +} // namespace kapi::devices + +#endif
\ No newline at end of file diff --git a/kapi/kapi/devices/manager.hpp b/kapi/kapi/devices/manager.hpp index 5b620b27..de6ebf97 100644 --- a/kapi/kapi/devices/manager.hpp +++ b/kapi/kapi/devices/manager.hpp @@ -6,6 +6,7 @@ #include <kapi/devices/device.hpp> #include <kstd/memory.hpp> +#include <kstd/vector.hpp> #include <string_view> @@ -33,6 +34,11 @@ namespace kapi::devices //! @return a shared pointer to the device iff. the device was found and is still alive, nullptr otherwise. auto find_device(std::string_view name) -> kstd::shared_ptr<device>; + //! Get all currently registered devices. + //! + //! @return every device current reachable through the registry. + [[nodiscard]] auto all() -> kstd::vector<kstd::shared_ptr<device>>; + //! @} } // namespace kapi::devices diff --git a/kapi/kapi/test_support/devices.hpp b/kapi/kapi/test_support/devices.hpp index ab0911fc..b4d410d6 100644 --- a/kapi/kapi/test_support/devices.hpp +++ b/kapi/kapi/test_support/devices.hpp @@ -5,6 +5,7 @@ namespace kapi::test_support::devices { auto deinit() -> void; auto deinit_interface_registry() -> void; + auto deinit_driver_registry() -> void; } // namespace kapi::test_support::devices #endif diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 72994449..d3e0c015 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -16,6 +16,7 @@ target_sources("kernel_lib" PRIVATE "kapi/devices/cpu.cpp" "kapi/devices/device.cpp" "kapi/devices/driver.cpp" + "kapi/devices/driver_registry.cpp" "kapi/devices/error.cpp" "kapi/devices/interface_registry.cpp" "kapi/filesystem.cpp" diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 8f3011fe..5eed1b87 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -2,6 +2,7 @@ #include <kernel/devices/root_bus.hpp> +#include <kapi/devices/driver_registry.hpp> #include <kapi/system.hpp> #include <kapi/test_support/devices.hpp> @@ -9,6 +10,7 @@ #include <kstd/memory.hpp> #include <kstd/print.hpp> #include <kstd/string.hpp> +#include <kstd/vector.hpp> #include <string_view> #include <utility> @@ -29,6 +31,7 @@ namespace kapi::devices } interface_registry::init(); + driver_registry::init(); root_bus = kstd::make_shared<kernel::devices::root_bus>(); register_device(root_bus); @@ -85,6 +88,21 @@ namespace kapi::devices return found->second.lock(); } + auto all() -> kstd::vector<kstd::shared_ptr<device>> + { + auto result = kstd::vector<kstd::shared_ptr<device>>{}; + + for (auto const & [name, weak_device] : device_tree) + { + if (auto device = weak_device.lock()) + { + result.push_back(std::move(device)); + } + } + + return result; + } + } // namespace kapi::devices namespace kapi::test_support::devices @@ -92,6 +110,7 @@ namespace kapi::test_support::devices auto deinit() -> void { deinit_interface_registry(); + deinit_driver_registry(); kapi::devices::root_bus.reset(); } diff --git a/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp index 179a04a8..6c659ef5 100644 --- a/kernel/kapi/devices/bus.cpp +++ b/kernel/kapi/devices/bus.cpp @@ -68,6 +68,11 @@ namespace kapi::devices return {m_devices.data(), m_devices.size()}; } + auto bus::protocol() -> struct bus_protocol * + { + return nullptr; + } + auto bus::enumerate() -> bool { return true; diff --git a/kernel/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp index 540d37b7..cbbd5405 100644 --- a/kernel/kapi/devices/device.cpp +++ b/kernel/kapi/devices/device.cpp @@ -22,9 +22,29 @@ namespace kapi::devices return m_parent.lock(); } - auto device::set_parent(kstd::weak_ptr<bus> parent) -> void + auto device::state() const noexcept -> enum state { - m_parent = parent; + return m_state; + } + + auto device::set_state(enum state state) -> void + { + m_state = state; + } + + auto device::bind_driver(kstd::weak_ptr<struct driver> driver) -> void + { + m_driver = driver; + } + + auto device::driver_data() const -> kstd::shared_ptr<void> const & + { + return m_driver_data; + } + + auto device::set_driver_data(kstd::shared_ptr<void> data) -> void + { + m_driver_data = data; } auto device::query_interface(interface) -> void * @@ -32,4 +52,9 @@ namespace kapi::devices return nullptr; } + auto device::set_parent(kstd::weak_ptr<bus> parent) -> void + { + m_parent = parent; + } + } // namespace kapi::devices
\ No newline at end of file diff --git a/kernel/kapi/devices/driver_registry.cpp b/kernel/kapi/devices/driver_registry.cpp new file mode 100644 index 00000000..67da9877 --- /dev/null +++ b/kernel/kapi/devices/driver_registry.cpp @@ -0,0 +1,135 @@ +#include <kapi/devices/driver_registry.hpp> + +#include <kapi/devices/bus.hpp> +#include <kapi/devices/bus_protocol.hpp> +#include <kapi/devices/device.hpp> +#include <kapi/devices/driver.hpp> +#include <kapi/devices/manager.hpp> +#include <kapi/system.hpp> +#include <kapi/test_support/devices.hpp> + +#include <kstd/memory.hpp> +#include <kstd/print.hpp> +#include <kstd/system_error.hpp> +#include <kstd/vector.hpp> + +#include <algorithm> +#include <cstdint> +#include <functional> +#include <optional> +#include <utility> + +namespace kapi::devices +{ + + namespace + { + constinit auto static registry = std::optional<driver_registry>{}; + + struct candidate + { + std::uint32_t priority; + kstd::shared_ptr<driver> driver_handle; + }; + } // namespace + + auto driver_registry::init() -> void + { + if (registry.has_value()) + { + system::panic("[kernel] Device driver registry has already been initialized."); + } + + registry.emplace(); + } + + auto driver_registry::get() -> driver_registry & + { + if (!registry) + { + system::panic("[kernel] Device driver registry has not been initialized."); + } + + return *registry; + } + + auto driver_registry::add(kstd::shared_ptr<driver> driver) -> void + { + m_drivers.emplace_back(std::move(driver)); + + for (auto const & device : kapi::devices::all()) + { + if (device->state() != state::bound) + { + try_bind(device); + } + } + } + + auto driver_registry::device_attached(kstd::shared_ptr<device> const & device) -> void + { + try_bind(device); + } + + auto driver_registry::try_bind(kstd::shared_ptr<device> const & device) -> void + { + if (!device || device->state() == state::bound) + { + return; + } + + auto parent = device->parent(); + auto protocol = parent ? parent->protocol() : nullptr; + if (!protocol) + { + return; + } + + auto candidates = kstd::vector<candidate>{}; + + for (auto const & driver : m_drivers) + { + auto match = protocol->match(*device, *driver); + if (match) + { + candidates.push_back({.priority = *match, .driver_handle = driver}); + } + else if (match.error() != kstd::errc::not_supported) + { + kstd::println(kstd::print_sink::stderr, "[OS:DRV] match() failed for device {}: {}", device->name(), + match.error().message()); + } + } + + std::ranges::stable_sort(candidates, std::ranges::greater{}, &candidate::priority); + + for (auto const & candidate : candidates) + { + auto probed = candidate.driver_handle->probe(*device); + if (probed) + { + device->bind_driver(candidate.driver_handle); + device->set_state(state::bound); + kstd::println("[OS:DRV] Bound device {} (priority {})", device->name(), candidate.priority); + return; + } + + kstd::println(kstd::print_sink::stderr, "[OS:DRV] probe() failed for device {} (priority {}): {}", device->name(), + candidate.priority, probed.error().message()); + } + + if (!candidates.empty()) + { + device->set_state(state::failed); + } + } + +} // namespace kapi::devices + +namespace kapi::test_support::devices +{ + auto deinit_driver_registry() -> void + { + kapi::devices::registry.reset(); + } +} // namespace kapi::test_support::devices
\ No newline at end of file |
