diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-07-26 11:37:52 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-07-26 11:37:52 +0200 |
| commit | 5a5fefba28a52feffadf2b810b2f935934aa7b0b (patch) | |
| tree | 4583579a3b964091455344969a3e9641fb8fef33 | |
| parent | ad43e9c9eac69b862789f2e132201cd5ceebaffa (diff) | |
| download | kernel-5a5fefba28a52feffadf2b810b2f935934aa7b0b.tar.xz kernel-5a5fefba28a52feffadf2b810b2f935934aa7b0b.zip | |
kapi/device: allow removing devices from busses
| -rw-r--r-- | kapi/kapi/devices/bus.hpp | 11 | ||||
| -rw-r--r-- | kernel/kapi/devices.cpp | 4 | ||||
| -rw-r--r-- | kernel/kapi/devices/bus.cpp | 66 | ||||
| -rw-r--r-- | kernel/kapi/devices/bus.tests.cpp | 61 |
4 files changed, 140 insertions, 2 deletions
diff --git a/kapi/kapi/devices/bus.hpp b/kapi/kapi/devices/bus.hpp index 26b46830..13951e46 100644 --- a/kapi/kapi/devices/bus.hpp +++ b/kapi/kapi/devices/bus.hpp @@ -37,6 +37,9 @@ namespace kapi::devices //! @param protocol The bus protocol to use. bus(kstd::string const & name, bus_protocol & protocol); + //! Tear down this bus recursively. + ~bus() override; + //! Attach a child device to this bus. //! //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. @@ -44,6 +47,12 @@ namespace kapi::devices //! @param child The child device to attach. auto add_child(kstd::shared_ptr<device> child) -> void; + //! Detach a child device from this bus and tear it down. + //! + //! @param child The device to detach. + //! @return true iff. the child was found an removed, false otherwise. + auto remove_child(device & child) -> bool; + //! Get the children attached to this bus. //! //! @return A vector of all children attached to this bus. @@ -54,6 +63,8 @@ namespace kapi::devices auto query_facet(kapi::capabilities::facet_id facet) -> void * override; private: + auto do_remove_child(device & child) -> void; + bus_protocol * m_protocol{}; kstd::vector<kstd::shared_ptr<device>> m_devices{}; }; diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 2505779a..f289bbf2 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -48,10 +48,10 @@ namespace kapi::test_support::devices { auto deinit() -> void { + kapi::devices::root_bus.reset(); + deinit_driver_registry(); deinit_facet_registry(); deinit_device_registry(); - - kapi::devices::root_bus.reset(); } } // namespace kapi::test_support::devices
\ No newline at end of file diff --git a/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp index dad90a15..d917eb96 100644 --- a/kernel/kapi/devices/bus.cpp +++ b/kernel/kapi/devices/bus.cpp @@ -9,6 +9,7 @@ #include <kstd/string.hpp> #include <kstd/vector.hpp> +#include <algorithm> #include <span> #include <utility> @@ -23,6 +24,15 @@ namespace kapi::devices , m_protocol{&protocol} {} + bus::~bus() + { + auto children = m_devices; + for (auto const & child : children) + { + remove_child(*child); + } + } + auto bus::add_child(kstd::shared_ptr<device> child) -> void { child->set_parent(kstd::static_pointer_cast<bus>(shared_from_this())); @@ -32,12 +42,35 @@ namespace kapi::devices kapi::system::panic("[OS:DEV] Failed to register child device {}", child->name()); } + // TODO: lock bus + auto & attached = m_devices.emplace_back(std::move(child)); attached->set_state(state::present); + // TODO: unlock bus + driver_registry::get().device_attached(attached); } + auto bus::remove_child(device & device) -> bool // NOLINT(misc-no-recursion) + { + // TODO: lock bus + + auto found = std::ranges::find_if(m_devices, [&](auto const & d) { return d.get() == &device; }); + if (!found) + { + return false; + } + + auto lifeline = *found; + m_devices.erase(found); + + // TODO: unlock bus + + do_remove_child(*lifeline); + return true; + } + [[nodiscard]] auto bus::children() const -> std::span<kstd::shared_ptr<device> const> { return {m_devices.data(), m_devices.size()}; @@ -57,4 +90,37 @@ namespace kapi::devices return device::query_facet(facet); } + auto bus::do_remove_child(device & device) -> void // NOLINT(misc-no-recursion) + { + if (device.state() == state::removed) + { + return; + } + + if (auto child_bus = device.facet<bus>()) + { + auto grandchildren = kstd::vector<kstd::shared_ptr<kapi::devices::device>>{}; + // TODO: lock child bus + // NOTE: this could be more efficient one vector::assign is implemented. + grandchildren = kstd::vector(child_bus->m_devices.begin(), child_bus->m_devices.end()); + // TODO: unlock child bus + + for (auto & grandchild : grandchildren) + { + child_bus->remove_child(*grandchild); + } + } + + device.set_state(state::removed); + + if (auto bound_driver = device.bound_driver()) + { + bound_driver->unbind(device); + } + + facet_registry::get().withdraw_all_for(device); + device_registry::get().remove(device); + device.set_parent(kstd::weak_ptr<bus>{}); + } + } // namespace kapi::devices
\ No newline at end of file diff --git a/kernel/kapi/devices/bus.tests.cpp b/kernel/kapi/devices/bus.tests.cpp index 9f95513b..0a33df1a 100644 --- a/kernel/kapi/devices/bus.tests.cpp +++ b/kernel/kapi/devices/bus.tests.cpp @@ -92,3 +92,64 @@ SCENARIO("A bus shares ownership of its children, holding a weak link to its par } } } + +SCENARIO("Removing a child from a bus tears down the while subtree", "[devices][bus]") +{ + GIVEN("A bus with a child bus") + { + auto root = kstd::make_shared<test_bus>("bus_removal_root"); + auto middle = kstd::make_shared<test_bus>("bus_removal_middle"); + auto leaf_destroyed = false; + auto leaf = kstd::make_shared<flagging_device>("bus_removal_leaf", leaf_destroyed); + + root->add_child(middle); + middle->add_child(leaf); + + REQUIRE(root->children().size() == 1); + REQUIRE(middle->children().size() == 1); + + WHEN("the middle bus is removed from the root") + { + REQUIRE(root->remove_child(*middle)); + + THEN("the middle bus's own subtree was torn down") + { + REQUIRE(middle->children().empty()); + } + + THEN("the leaf is marked as removed but not destroyed") + { + REQUIRE(leaf->state() == kapi::devices::state::removed); + REQUIRE(leaf->parent() == nullptr); + REQUIRE_FALSE(leaf_destroyed); + } + + AND_WHEN("dropping the outside leaf reference") + { + leaf.reset(); + + THEN("the leaf is destroyed") + { + REQUIRE(leaf_destroyed); + } + } + + THEN("the root no longer considers the middle its child") + { + REQUIRE(root->children().empty()); + REQUIRE(middle->state() == kapi::devices::state::removed); + } + } + } + + GIVEN("A bus and a device that was never actually attached to it") + { + auto bus = kstd::make_shared<test_bus>("bus_removal_unrelated_bus"); + auto unrelated = kstd::make_shared<test_device>("bus_removal_unrelated_device"); + + THEN("removing the child fails") + { + REQUIRE_FALSE(bus->remove_child(*unrelated)); + } + } +} |
