aboutsummaryrefslogtreecommitdiff
path: root/kernel/kapi/devices/driver.tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/kapi/devices/driver.tests.cpp')
-rw-r--r--kernel/kapi/devices/driver.tests.cpp230
1 files changed, 230 insertions, 0 deletions
diff --git a/kernel/kapi/devices/driver.tests.cpp b/kernel/kapi/devices/driver.tests.cpp
new file mode 100644
index 00000000..abce92d2
--- /dev/null
+++ b/kernel/kapi/devices/driver.tests.cpp
@@ -0,0 +1,230 @@
+#include <kapi/devices.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/result.hpp>
+#include <kstd/string.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <algorithm>
+#include <array>
+#include <cstdint>
+#include <span>
+#include <string_view>
+
+namespace
+{
+ struct controller_identification
+ {
+ constexpr auto static id = kapi::devices::interface{"stacking_test_controller_identification"};
+ virtual ~controller_identification() = default;
+ [[nodiscard]] virtual auto controller_name() const -> std::string_view = 0;
+ };
+
+ struct controller_driver_identification
+ {
+ constexpr auto static id = kapi::devices::interface{"stacking_test_controller_driver_identification"};
+ virtual ~controller_driver_identification() = default;
+ [[nodiscard]] virtual auto supported_names() const -> std::span<std::string_view const> = 0;
+ };
+
+ struct controller_bus final : kapi::devices::bus, kapi::devices::bus_protocol
+ {
+ using kapi::devices::bus::bus;
+
+ auto enumerate(kapi::devices::bus &) -> void override {}
+
+ [[nodiscard]] auto match(kapi::devices::device const & dev, kapi::devices::driver const & drv) const
+ -> kstd::result<std::uint32_t> override
+ {
+ auto const * ident = dev.as<controller_identification>();
+ auto const * claims = drv.as<controller_driver_identification>();
+ if (!ident || !claims || !std::ranges::contains(claims->supported_names(), ident->controller_name()))
+ {
+ return kstd::failure(kapi::devices::driver_match_errc::no_match);
+ }
+ return 0u;
+ }
+
+ [[nodiscard]] auto protocol() -> kapi::devices::bus_protocol * override
+ {
+ return this;
+ }
+ };
+
+ struct controller_device final : kapi::devices::bus, controller_identification
+ {
+ controller_device()
+ : kapi::devices::bus{"stacking_test_controller"}
+ {}
+
+ [[nodiscard]] auto controller_name() const -> std::string_view override
+ {
+ return "stacking_test_controller";
+ }
+
+ protected:
+ auto query_interface(kapi::devices::interface interface) -> void * override
+ {
+ if (interface == controller_identification::id)
+ {
+ return static_cast<controller_identification *>(this);
+ }
+ return kapi::devices::bus::query_interface(interface);
+ }
+ };
+
+ struct leaf_identification
+ {
+ constexpr kapi::devices::interface static id{"stacking_test_leaf_identification"};
+ virtual ~leaf_identification() = default;
+ [[nodiscard]] virtual auto leaf_name() const -> std::string_view = 0;
+ };
+
+ struct leaf_driver_identification
+ {
+ constexpr kapi::devices::interface static id{"stacking_test_leaf_driver_identification"};
+ virtual ~leaf_driver_identification() = default;
+ [[nodiscard]] virtual auto supported_names() const -> std::span<std::string_view const> = 0;
+ };
+
+ struct leaf_device final : kapi::devices::device, leaf_identification
+ {
+ leaf_device()
+ : kapi::devices::device{"stacking_test_leaf"}
+ {}
+
+ [[nodiscard]] auto leaf_name() const -> std::string_view override
+ {
+ return "stacking_test_leaf";
+ }
+
+ protected:
+ auto query_interface(kapi::devices::interface interface) -> void * override
+ {
+ if (interface == leaf_identification::id)
+ {
+ return static_cast<leaf_identification *>(this);
+ }
+ return kapi::devices::device::query_interface(interface);
+ }
+ };
+
+ struct leaf_bus final : kapi::devices::bus, kapi::devices::bus_protocol
+ {
+ using kapi::devices::bus::bus;
+
+ auto enumerate(kapi::devices::bus & self) -> void override
+ {
+ self.add_child(kstd::make_shared<leaf_device>());
+ }
+
+ [[nodiscard]] auto match(kapi::devices::device const & dev, kapi::devices::driver const & drv) const
+ -> kstd::result<std::uint32_t> override
+ {
+ auto const * ident = dev.as<leaf_identification>();
+ auto const * claims = drv.as<leaf_driver_identification>();
+ if (!ident || !claims || !std::ranges::contains(claims->supported_names(), ident->leaf_name()))
+ {
+ return kstd::failure(kapi::devices::driver_match_errc::no_match);
+ }
+ return 0u;
+ }
+
+ [[nodiscard]] auto protocol() -> kapi::devices::bus_protocol * override
+ {
+ return this;
+ }
+ };
+
+ struct controller_driver final : kapi::devices::driver, controller_driver_identification
+ {
+ [[nodiscard]] auto probe(kapi::devices::device & dev) -> kstd::result<void> override
+ {
+ auto self_as_bus = kstd::static_pointer_cast<kapi::devices::bus>(dev.shared_from_this());
+ auto leaf = kstd::make_shared<leaf_bus>("stacking_test_leaf_bus");
+ self_as_bus->add_child(leaf);
+ leaf->enumerate(*leaf);
+
+ return kstd::success();
+ }
+
+ auto unbind(kapi::devices::device &) -> void override {}
+
+ [[nodiscard]] auto supported_names() const -> std::span<std::string_view const> override
+ {
+ constexpr auto static names = std::array<std::string_view, 1>{"stacking_test_controller"};
+ return names;
+ }
+
+ protected:
+ auto query_interface(kapi::devices::interface interface) -> void * override
+ {
+ if (interface == controller_driver_identification::id)
+ {
+ return static_cast<controller_driver_identification *>(this);
+ }
+
+ return kapi::devices::driver::query_interface(interface);
+ }
+ };
+
+ struct leaf_driver final : kapi::devices::driver, leaf_driver_identification
+ {
+ [[nodiscard]] auto probe(kapi::devices::device &) -> kstd::result<void> override
+ {
+ return kstd::success();
+ }
+
+ auto unbind(kapi::devices::device &) -> void override {}
+
+ [[nodiscard]] auto supported_names() const -> std::span<std::string_view const> override
+ {
+ constexpr auto static names = std::array<std::string_view, 1>{"stacking_test_leaf"};
+ return names;
+ }
+
+ protected:
+ auto query_interface(kapi::devices::interface interface) -> void * override
+ {
+ if (interface == leaf_driver_identification::id)
+ {
+ return static_cast<leaf_driver_identification *>(this);
+ }
+
+ return kapi::devices::driver::query_interface(interface);
+ }
+ };
+} // namespace
+
+SCENARIO("a bound controller driver can attach a further, independently-typed bus protocol of its own",
+ "[devices][driver][stacking]")
+{
+ GIVEN("a controller bus, a controller driver, and a leaf driver, none aware of each other's identification scheme")
+ {
+ auto outer_bus = kstd::make_shared<controller_bus>("stacking_test_controller_bus");
+
+ kapi::devices::driver_registry::get().add(kstd::make_shared<controller_driver>());
+ kapi::devices::driver_registry::get().add(kstd::make_shared<leaf_driver>());
+
+ WHEN("the controller device is attached to the controller bus")
+ {
+ outer_bus->add_child(kstd::make_shared<controller_device>());
+
+ THEN("the controller device bound the controller driver")
+ {
+ auto controller = kapi::devices::find_device("stacking_test_controller");
+ REQUIRE(controller != nullptr);
+ REQUIRE(controller->state() == kapi::devices::state::bound);
+ }
+
+ THEN("the leaf device the controller driver attached is reachable and bound to the leaf driver")
+ {
+ auto leaf = kapi::devices::find_device("stacking_test_leaf");
+ REQUIRE(leaf != nullptr);
+ REQUIRE(leaf->state() == kapi::devices::state::bound);
+ REQUIRE(leaf->bound_driver() != nullptr);
+ }
+ }
+ }
+}