From e7abfb7bf87ac605b2168891973d7e04a84d627e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 31 Mar 2026 21:56:37 +0200 Subject: kapi/devices: introduce basic bus abstraction --- kapi/include/kapi/devices/bus.hpp | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 kapi/include/kapi/devices/bus.hpp diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp new file mode 100644 index 0000000..cf8d090 --- /dev/null +++ b/kapi/include/kapi/devices/bus.hpp @@ -0,0 +1,40 @@ +#ifndef TEACHOS_KAPI_DEVICES_BUS_HPP +#define TEACHOS_KAPI_DEVICES_BUS_HPP + +#include "kapi/devices/device.hpp" + +#include +#include +#include + +#include + +namespace kapi::devices +{ + //! A bus device that represents a logical/physical tree of devices and busses. + struct bus : device + { + //! Construct a bus with the given major number, minor number, and name. + //! + //! @param major The major number of the bus. + //! @param minor The minor number of the bus. + //! @param name The name of the bus. + bus(std::size_t major, std::size_t minor, kstd::string const & name) + : device(major, minor, name) + {} + + //! Attach a child device to this bus. + //! + //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. + //! + //! @param child The child device to attach. + virtual auto add_child(kstd::unique_ptr child) -> void = 0; + + //! Get a list of all child devices attached to this bus. + //! + //! @return A reference to list of child devices of this bus. + [[nodiscard]] virtual auto children() const -> kstd::vector const & = 0; + }; +} // namespace kapi::devices + +#endif \ No newline at end of file -- cgit v1.2.3 From 77473afe9d5acb9450443b07b56d3dbc2f0639a6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 31 Mar 2026 22:22:25 +0200 Subject: kstd: introduce observer_ptr --- kapi/include/kapi/devices/bus.hpp | 3 +- libs/kstd/include/kstd/observer_ptr | 154 ++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 libs/kstd/include/kstd/observer_ptr diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index cf8d090..0b25ac1 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -4,6 +4,7 @@ #include "kapi/devices/device.hpp" #include +#include #include #include @@ -33,7 +34,7 @@ namespace kapi::devices //! Get a list of all child devices attached to this bus. //! //! @return A reference to list of child devices of this bus. - [[nodiscard]] virtual auto children() const -> kstd::vector const & = 0; + [[nodiscard]] virtual auto children() const -> kstd::vector> const & = 0; }; } // namespace kapi::devices diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr new file mode 100644 index 0000000..d3d24b4 --- /dev/null +++ b/libs/kstd/include/kstd/observer_ptr @@ -0,0 +1,154 @@ +#ifndef KSTD_OBSERVER_PTR_HPP +#define KSTD_OBSERVER_PTR_HPP + +#include "kstd/os/error.hpp" + +#include +#include +#include +#include + +namespace kstd +{ + + template + struct observer_ptr + { + //! The type of the element being pointed to. + using element_type = ElementType; + + //! Construct an empty observer pointer. + constexpr observer_ptr() noexcept = default; + + //! Construct an empty observer pointer from a null pointer. + constexpr observer_ptr(std::nullptr_t) noexcept {} + + //! Construct an observer pointer from a raw pointer. + constexpr explicit observer_ptr(element_type * pointer) + : m_ptr{pointer} + {} + + //! Construct an observer pointer from another observer pointer. + template + requires std::convertible_to + constexpr observer_ptr(observer_ptr other) noexcept + : m_ptr{other.get()} + {} + + //! Copy construct an observer pointer. + constexpr observer_ptr(observer_ptr const & other) noexcept = default; + + //! Move construct an observer pointer. + constexpr observer_ptr(observer_ptr && other) noexcept = default; + + //! Stop watching the the watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto release() noexcept -> element_type * + { + return std::exchange(m_ptr, nullptr); + } + + //! Reset the observer pointer. + //! + //! @param pointer The new object to watch. + constexpr auto reset(element_type * pointer) noexcept -> void + { + m_ptr = pointer; + } + + //! Swap the observer pointer with another observer pointer. + //! + //! @param other The other observer pointer to swap with. + constexpr auto swap(observer_ptr & other) noexcept -> void + { + std::swap(m_ptr, other.m_ptr); + } + + //! Get the currently watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto get() const noexcept -> element_type * + { + return m_ptr; + } + + //! Check if the observer pointer is watching an object. + //! + //! @return True if the observer pointer is watching an object, false otherwise. + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_ptr != nullptr; + } + + //! Get the currently watched object. + //! + //! @return A reference to the currently watched object. + [[nodiscard]] constexpr auto operator*() const noexcept -> element_type & + { + throw_on_null(); + return *m_ptr; + } + + //! Get the currently watched object. + //! + //! @return A pointer to the currently watched object. + [[nodiscard]] constexpr auto operator->() const noexcept -> element_type * + { + throw_on_null(); + return m_ptr; + } + + //! Convert the observer pointer to a raw pointer. + //! + //! @return A pointer to the currently watched object. + constexpr explicit operator element_type *() const noexcept + { + return m_ptr; + } + + //! Compare the observer pointer with another observer pointer. + //! + //! @param other The other observer pointer to compare with. + //! @return The result of the comparison. + constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; + + private: + //! Throw an exception if the observer pointer is null. + //! + //! @throws std::runtime_error if the observer pointer is null. + constexpr auto throw_on_null() const noexcept -> void + { + if (m_ptr == nullptr) + { + os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); + } + } + + //! The raw pointer to the watched object. + ElementType * m_ptr; + }; + + //! Swap two observer pointers. + //! + //! @param lhs The first observer pointer to swap. + //! @param rhs The second observer pointer to swap. + template + constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void + { + lhs.swap(rhs); + } + + //! Create an observer pointer from a raw pointer. + //! + //! @param pointer The raw pointer to create an observer pointer from. + //! @return An observer pointer to the given raw pointer. + template + constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr + { + return observer_ptr{pointer}; + } + +} // namespace kstd + +#endif \ No newline at end of file -- cgit v1.2.3 From 15b882d0416bb83a18e5437480c08419b2035e1f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 31 Mar 2026 22:52:52 +0200 Subject: kstd: add some basic observer_ptr tests --- libs/kstd/CMakeLists.txt | 1 + libs/kstd/include/kstd/observer_ptr | 7 +- libs/kstd/tests/src/observer_ptr.cpp | 234 +++++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+), 3 deletions(-) create mode 100644 libs/kstd/tests/src/observer_ptr.cpp diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index ec0f441..240118e 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -44,6 +44,7 @@ else() add_executable("kstd_tests" "tests/src/flat_map.cpp" "tests/src/vector.cpp" + "tests/src/observer_ptr.cpp" "tests/src/os_panic.cpp" "tests/src/string.cpp" ) diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr index d3d24b4..97c9e5e 100644 --- a/libs/kstd/include/kstd/observer_ptr +++ b/libs/kstd/include/kstd/observer_ptr @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace kstd @@ -52,7 +53,7 @@ namespace kstd //! Reset the observer pointer. //! //! @param pointer The new object to watch. - constexpr auto reset(element_type * pointer) noexcept -> void + constexpr auto reset(element_type * pointer = nullptr) noexcept -> void { m_ptr = pointer; } @@ -84,7 +85,7 @@ namespace kstd //! Get the currently watched object. //! //! @return A reference to the currently watched object. - [[nodiscard]] constexpr auto operator*() const noexcept -> element_type & + [[nodiscard]] constexpr auto operator*() const noexcept -> std::add_lvalue_reference_t { throw_on_null(); return *m_ptr; @@ -126,7 +127,7 @@ namespace kstd } //! The raw pointer to the watched object. - ElementType * m_ptr; + ElementType * m_ptr{}; }; //! Swap two observer pointers. diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp new file mode 100644 index 0000000..2fe2226 --- /dev/null +++ b/libs/kstd/tests/src/observer_ptr.cpp @@ -0,0 +1,234 @@ +#include +#include + +#include + +#include + +SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto ptr = kstd::observer_ptr{}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a nullptr") + { + auto ptr = kstd::observer_ptr{nullptr}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a raw pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + THEN("the observer pointer is not null") + { + REQUIRE(ptr); + } + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + } + + WHEN("copy constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy constructing from an existing observer pointer with a compatible type") + { + struct A + { + }; + + struct B : A + { + }; + + auto value = B{}; + auto ptr = kstd::observer_ptr(&value); + auto copy = kstd::observer_ptr(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + } +} + +SCENARIO("Observer pointer modifiers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("releasing the observer pointer") + { + auto raw_ptr = ptr.release(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + + THEN("the returned pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("resetting the observer pointer to nullptr") + { + ptr.reset(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("resetting the observer pointer to a new object") + { + auto other_value = 2; + ptr.reset(&other_value); + + THEN("the observer pointer points to the new object") + { + REQUIRE(&*ptr == &other_value); + } + } + + WHEN("swapping it with another observer pointer") + { + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + ptr.swap(other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } + } +} + +SCENARIO("Observer pointer observers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + struct A + { + int value{}; + + constexpr auto operator<=>(A const & other) const noexcept = default; + }; + + auto value = A{1}; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("getting the raw pointer") + { + auto raw_ptr = ptr.get(); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("dereferencing the observer pointer") + { + auto dereferenced = *ptr; + + THEN("the dereferenced value is the correct value") + { + REQUIRE(dereferenced == value); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + ptr->value = 2; + + THEN("the value is updated") + { + REQUIRE(value.value == 2); + } + } + + WHEN("converting the observer pointer to a raw pointer") + { + auto raw_ptr = static_cast(ptr); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 5f084e49a27e73fdf9ca88f46121618d9fae399f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 06:53:41 +0200 Subject: kstd/tests: extend operation tracker --- libs/kstd/tests/include/kstd/tests/test_types.hpp | 71 ++++++++++--- libs/kstd/tests/src/vector.cpp | 119 +++++++++++++--------- 2 files changed, 128 insertions(+), 62 deletions(-) diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp index 6a06311..9207ee9 100644 --- a/libs/kstd/tests/include/kstd/tests/test_types.hpp +++ b/libs/kstd/tests/include/kstd/tests/test_types.hpp @@ -11,44 +11,43 @@ namespace kstd::tests //! A type tracking copy and move operations //! //! This type is designed to test move and copy semantics of standard library containers implemented in kstd. - struct move_tracker + struct special_member_tracker { //! A value indicating that the object was moved from. constexpr auto static moved_from_v = -1; - //! A simple value to be able to track the move-from state. - int value{}; - //! A flag to track if an instance of this type was either copy constructed or copy assigned. - bool was_copied{false}; - //! A flag to track if an instance of this type was either move constructed or move assigned. - bool was_moved{false}; + constexpr special_member_tracker() + : default_constructed_count{1} + {} //! Construct a new move tracker with the given value, if any. - constexpr move_tracker(int v = 0) + constexpr special_member_tracker(int v = 0) : value{v} + , value_constructed_count{1} {} //! Construct a new move tracker by copying an existing one. - constexpr move_tracker(move_tracker const & other) + constexpr special_member_tracker(special_member_tracker const & other) : value{other.value} - , was_copied{true} + , copy_constructed_count{1} {} //! Construct a new move tracker by moving from an existing one. - constexpr move_tracker(move_tracker && other) noexcept + constexpr special_member_tracker(special_member_tracker && other) noexcept : value{other.value} - , was_moved{true} + , move_constructed_count{1} { other.value = moved_from_v; } //! Copy assign a new move tracker from an existing one. - constexpr auto operator=(move_tracker const & other) -> move_tracker & + constexpr auto operator=(special_member_tracker const & other) -> special_member_tracker & { if (this != &other) { value = other.value; - was_copied = true; + ++copy_assigned_count; + ++other.copied_from_count; } return *this; } @@ -56,16 +55,56 @@ namespace kstd::tests //! Move assign a new move tracker from an existing one. //! //! This function ensures that the moved-from state is marked. - constexpr auto operator=(move_tracker && other) noexcept -> move_tracker & + constexpr auto operator=(special_member_tracker && other) noexcept -> special_member_tracker & { if (this != &other) { value = other.value; - was_moved = true; + ++move_assigned_count; other.value = moved_from_v; + ++other.moved_from_count; } return *this; } + + ~special_member_tracker() + { + ++destroyed_count; + } + + auto reset_counts() -> void + { + default_constructed_count = 0; + copy_constructed_count = 0; + move_constructed_count = 0; + value_constructed_count = 0; + copy_assigned_count = 0; + move_assigned_count = 0; + destroyed_count = 0; + copied_from_count = 0; + moved_from_count = 0; + } + + //! A simple value to be able to track the move-from state. + int value{}; + //! A counter to track how many times an instance of this type was default constructed. + std::size_t default_constructed_count{0}; + //! A counter to track how many times an instance of this type was copy constructed. + std::size_t copy_constructed_count{0}; + //! A counter to track how many times an instance of this type was move constructed. + std::size_t move_constructed_count{0}; + //! A counter to track how many times an instance of this type was value constructed. + std::size_t value_constructed_count{0}; + //! A counter to track how many times an instance of this type was copy assigned. + std::size_t copy_assigned_count{0}; + //! A counter to track how many times an instance of this type was move assigned. + std::size_t move_assigned_count{0}; + //! A counter to track how many times an instance of this type was destroyed. + std::size_t destroyed_count{0}; + //! A counter to track how many times an instance of this type was copied from another instance. + mutable std::size_t copied_from_count{0}; + //! A counter to track how many times an instance of this type was moved from another instance. + std::size_t moved_from_count{0}; }; //! A type that is not default constructible. diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index b7971f4..81bf32f 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -919,8 +919,8 @@ SCENARIO("Vector modifier move semantics", "[vector]") { GIVEN("An empty vector and a move tracker element") { - auto v = kstd::vector{}; - auto tracker = kstd::tests::move_tracker{42}; + auto v = kstd::vector{}; + auto tracker = kstd::tests::special_member_tracker{42}; WHEN("push_back is called with the move tracker") { @@ -929,8 +929,8 @@ SCENARIO("Vector modifier move semantics", "[vector]") THEN("the element is added and the size and capacity increase") { REQUIRE(v.size() == 1); - REQUIRE(v.back().was_moved); - REQUIRE_FALSE(v.back().was_copied); + REQUIRE(v.back().move_constructed_count == 1); + REQUIRE(v.back().copy_constructed_count == 0); REQUIRE(v.back().value == 42); } @@ -943,7 +943,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("An empty vector") { - auto v = kstd::vector{}; + auto v = kstd::vector{}; WHEN("emplace_back is called with constructor arguments") { @@ -952,8 +952,8 @@ SCENARIO("Vector modifier move semantics", "[vector]") THEN("the element is constructed directly, without moves or copies") { REQUIRE(v.size() == 1); - REQUIRE_FALSE(v.back().was_moved); - REQUIRE_FALSE(v.back().was_copied); + REQUIRE(v.back().move_constructed_count == 0); + REQUIRE(v.back().copy_constructed_count == 0); REQUIRE(v.back().value == 42); } } @@ -961,7 +961,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("A populated vector of move trackers") { - auto v = kstd::vector{}; + auto v = kstd::vector{}; v.reserve(10); v.emplace_back(10); v.emplace_back(20); @@ -969,41 +969,46 @@ SCENARIO("Vector modifier move semantics", "[vector]") WHEN("inserting an element in the middle with sufficient capacity") { - auto const tracker = kstd::tests::move_tracker{15}; + auto const tracker = kstd::tests::special_member_tracker{15}; v.insert(v.cbegin() + 1, tracker); THEN("the shifted elements are move-assigned and the new element is copy-assigned") { REQUIRE(v.size() == 4); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 15); - REQUIRE(v[1].was_copied); - REQUIRE_FALSE(v[1].was_moved); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 1); + REQUIRE(tracker.copied_from_count == 1); REQUIRE(v[2].value == 20); - REQUIRE(v[2].was_moved); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); REQUIRE(v[3].value == 30); - REQUIRE(v[3].was_moved); + REQUIRE(v[3].move_constructed_count == 1); } } WHEN("inserting an rvalue element in the middle with sufficient capacity") { - auto tracker = kstd::tests::move_tracker{15}; + auto tracker = kstd::tests::special_member_tracker{15}; v.insert(v.cbegin() + 1, std::move(tracker)); THEN("the shifted elements are move-assigned and the new element is move-assigned") { REQUIRE(v.size() == 4); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 15); - REQUIRE_FALSE(v[1].was_copied); - REQUIRE(v[1].was_moved); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + REQUIRE(tracker.moved_from_count == 1); REQUIRE(v[2].value == 20); - REQUIRE(v[2].was_moved); + REQUIRE(v[2].move_assigned_count == 1); REQUIRE(v[3].value == 30); - REQUIRE(v[3].was_moved); + REQUIRE(v[3].move_constructed_count == 1); } } @@ -1011,8 +1016,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") { for (auto & elem : v) { - elem.was_copied = false; - elem.was_moved = false; + elem.reset_counts(); } auto it = v.erase(v.cbegin() + 1); @@ -1022,12 +1026,15 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v.size() == 2); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); - REQUIRE_FALSE(v[0].was_copied); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 30); - REQUIRE(v[1].was_moved); - REQUIRE_FALSE(v[1].was_copied); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 1); REQUIRE(it == v.begin() + 1); } @@ -1037,8 +1044,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") { for (auto & elem : v) { - elem.was_copied = false; - elem.was_moved = false; + elem.reset_counts(); } auto it = v.erase(v.cend() - 1); @@ -1048,12 +1054,20 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v.size() == 2); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); - REQUIRE_FALSE(v[0].was_copied); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 20); - REQUIRE_FALSE(v[1].was_moved); - REQUIRE_FALSE(v[1].was_copied); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 0); + REQUIRE(v.data()[2].copied_from_count == 0); REQUIRE(it == v.end()); } @@ -1066,8 +1080,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") for (auto & elem : v) { - elem.was_copied = false; - elem.was_moved = false; + elem.reset_counts(); } auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); @@ -1077,16 +1090,30 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v.size() == 3); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); - REQUIRE_FALSE(v[0].was_copied); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 40); - REQUIRE(v[1].was_moved); - REQUIRE_FALSE(v[1].was_copied); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); REQUIRE(v[2].value == 50); - REQUIRE(v[2].was_moved); - REQUIRE_FALSE(v[2].was_copied); + REQUIRE(v[2].move_constructed_count == 0); + REQUIRE(v[2].copy_constructed_count == 0); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); + + REQUIRE(v.data()[3].destroyed_count == 1); + REQUIRE(v.data()[3].moved_from_count == 1); + REQUIRE(v.data()[3].copied_from_count == 0); + + REQUIRE(v.data()[4].destroyed_count == 1); + REQUIRE(v.data()[4].moved_from_count == 1); + REQUIRE(v.data()[4].copied_from_count == 0); REQUIRE(it == v.begin() + 1); } @@ -1417,8 +1444,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") GIVEN("An empty vector and a const lvalue tracker") { - auto v = kstd::vector{}; - auto const tracker = kstd::tests::move_tracker{42}; + auto v = kstd::vector{}; + auto const tracker = kstd::tests::special_member_tracker{42}; WHEN("push_back is called with the const lvalue") { @@ -1428,8 +1455,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") { REQUIRE(v.size() == 1); REQUIRE(v.back().value == 42); - REQUIRE(v.back().was_copied); - REQUIRE_FALSE(v.back().was_moved); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); } } @@ -1444,8 +1471,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") REQUIRE(v.size() == 1); REQUIRE(v.capacity() == current_capacity); REQUIRE(v.back().value == 42); - REQUIRE(v.back().was_copied); - REQUIRE_FALSE(v.back().was_moved); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); } } } -- cgit v1.2.3 From 724b9693897642497ca5feee65546dc670bed722 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 07:19:38 +0200 Subject: kstd/observer_ptr: extend test suite --- libs/kstd/include/kstd/observer_ptr | 8 +- libs/kstd/tests/src/observer_ptr.cpp | 165 ++++++++++++++++++++++++++++++----- 2 files changed, 149 insertions(+), 24 deletions(-) diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr index 97c9e5e..a328331 100644 --- a/libs/kstd/include/kstd/observer_ptr +++ b/libs/kstd/include/kstd/observer_ptr @@ -85,7 +85,7 @@ namespace kstd //! Get the currently watched object. //! //! @return A reference to the currently watched object. - [[nodiscard]] constexpr auto operator*() const noexcept -> std::add_lvalue_reference_t + [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t { throw_on_null(); return *m_ptr; @@ -94,7 +94,7 @@ namespace kstd //! Get the currently watched object. //! //! @return A pointer to the currently watched object. - [[nodiscard]] constexpr auto operator->() const noexcept -> element_type * + [[nodiscard]] constexpr auto operator->() const -> element_type * { throw_on_null(); return m_ptr; @@ -109,7 +109,7 @@ namespace kstd } //! Compare the observer pointer with another observer pointer. - //! + //!> //! @param other The other observer pointer to compare with. //! @return The result of the comparison. constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; @@ -118,7 +118,7 @@ namespace kstd //! Throw an exception if the observer pointer is null. //! //! @throws std::runtime_error if the observer pointer is null. - constexpr auto throw_on_null() const noexcept -> void + constexpr auto throw_on_null() const -> void { if (m_ptr == nullptr) { diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp index 2fe2226..5d94098 100644 --- a/libs/kstd/tests/src/observer_ptr.cpp +++ b/libs/kstd/tests/src/observer_ptr.cpp @@ -3,8 +3,28 @@ #include +#include +#include #include +namespace +{ + struct Base + { + }; + + struct Derived : Base + { + }; + + struct Element + { + int value{}; + + constexpr auto operator<=>(Element const &) const noexcept = default; + }; +} // namespace + SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") { GIVEN("An empty context") @@ -59,17 +79,9 @@ SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") WHEN("copy constructing from an existing observer pointer with a compatible type") { - struct A - { - }; - - struct B : A - { - }; - - auto value = B{}; - auto ptr = kstd::observer_ptr(&value); - auto copy = kstd::observer_ptr(ptr); + auto value = Derived{}; + auto ptr = kstd::observer_ptr(&value); + kstd::observer_ptr copy = ptr; THEN("the new observer pointer points to the same object as the other observer pointer") { @@ -112,6 +124,22 @@ SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") REQUIRE(&*copy == &value); } } + + WHEN("constructing an observer pointer using make_observer") + { + auto value = 1; + auto ptr = kstd::make_observer(&value); + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + + THEN("the observe pointer has the correct element type") + { + STATIC_REQUIRE(std::is_same_v>); + } + } } } @@ -174,6 +202,24 @@ SCENARIO("Observer pointer modifiers", "[observer_ptr]") REQUIRE(&*other_ptr == &value); } } + + WHEN("using namespace-level swap to swap it with another observer pointer") + { + using std::swap; + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + swap(ptr, other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } } } @@ -181,14 +227,7 @@ SCENARIO("Observer pointer observers", "[observer_ptr]") { GIVEN("A non-null observer pointer") { - struct A - { - int value{}; - - constexpr auto operator<=>(A const & other) const noexcept = default; - }; - - auto value = A{1}; + auto value = Element{1}; auto ptr = kstd::observer_ptr{&value}; WHEN("getting the raw pointer") @@ -223,12 +262,98 @@ SCENARIO("Observer pointer observers", "[observer_ptr]") WHEN("converting the observer pointer to a raw pointer") { - auto raw_ptr = static_cast(ptr); + auto raw_ptr = static_cast(ptr); THEN("the raw pointer points to the correct object") { REQUIRE(raw_ptr == &value); } } + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns true") + { + REQUIRE(static_cast(ptr)); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns false") + { + REQUIRE_FALSE(static_cast(ptr)); + } + } + + WHEN("dereferencing the observer pointer") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Observer pointer comparisons", "[observer_ptr]") +{ + GIVEN("Observer pointers to elements of an array") + { + int arr[] = {1, 2}; + auto ptr1 = kstd::observer_ptr{&arr[0]}; + auto ptr2 = kstd::observer_ptr{&arr[1]}; + + WHEN("comparing the same observer pointer") + { + THEN("they are equal") + { + REQUIRE(ptr1 == ptr1); + REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal); + } + } + + WHEN("comparing different observer pointers") + { + THEN("they are ordered correctly") + { + REQUIRE(ptr1 != ptr2); + REQUIRE(ptr1 < ptr2); + REQUIRE(ptr1 <= ptr2); + REQUIRE(ptr2 > ptr1); + REQUIRE(ptr2 >= ptr1); + REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less); + REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("comparing with another null observer pointer") + { + auto other_ptr = kstd::observer_ptr{}; + + THEN("they are equal") + { + REQUIRE(ptr == other_ptr); + REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal); + } + } } } \ No newline at end of file -- cgit v1.2.3 From a2ff4ace21699fe2be2e0401af78790c01f78d85 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 11:45:55 +0200 Subject: kstd: move observer_ptr to bits --- kapi/include/kapi/devices/bus.hpp | 1 - libs/kstd/include/kstd/bits/observer_ptr.hpp | 157 +++++++++++++++++++++++++++ libs/kstd/include/kstd/memory | 9 +- libs/kstd/include/kstd/observer_ptr | 155 -------------------------- libs/kstd/tests/src/observer_ptr.cpp | 2 +- 5 files changed, 163 insertions(+), 161 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/observer_ptr.hpp delete mode 100644 libs/kstd/include/kstd/observer_ptr diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index 0b25ac1..a8d7df8 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -4,7 +4,6 @@ #include "kapi/devices/device.hpp" #include -#include #include #include diff --git a/libs/kstd/include/kstd/bits/observer_ptr.hpp b/libs/kstd/include/kstd/bits/observer_ptr.hpp new file mode 100644 index 0000000..43ea409 --- /dev/null +++ b/libs/kstd/include/kstd/bits/observer_ptr.hpp @@ -0,0 +1,157 @@ +#ifndef KSTD_OBSERVER_PTR_HPP +#define KSTD_OBSERVER_PTR_HPP + +// IWYU pragma: private, include + +#include "kstd/os/error.hpp" + +#include +#include +#include +#include +#include + +namespace kstd +{ + + template + struct observer_ptr + { + //! The type of the element being pointed to. + using element_type = ElementType; + + //! Construct an empty observer pointer. + constexpr observer_ptr() noexcept = default; + + //! Construct an empty observer pointer from a null pointer. + constexpr observer_ptr(std::nullptr_t) noexcept {} + + //! Construct an observer pointer from a raw pointer. + constexpr explicit observer_ptr(element_type * pointer) + : m_ptr{pointer} + {} + + //! Construct an observer pointer from another observer pointer. + template + requires std::convertible_to + constexpr observer_ptr(observer_ptr other) noexcept + : m_ptr{other.get()} + {} + + //! Copy construct an observer pointer. + constexpr observer_ptr(observer_ptr const & other) noexcept = default; + + //! Move construct an observer pointer. + constexpr observer_ptr(observer_ptr && other) noexcept = default; + + //! Stop watching the the watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto release() noexcept -> element_type * + { + return std::exchange(m_ptr, nullptr); + } + + //! Reset the observer pointer. + //! + //! @param pointer The new object to watch. + constexpr auto reset(element_type * pointer = nullptr) noexcept -> void + { + m_ptr = pointer; + } + + //! Swap the observer pointer with another observer pointer. + //! + //! @param other The other observer pointer to swap with. + constexpr auto swap(observer_ptr & other) noexcept -> void + { + std::swap(m_ptr, other.m_ptr); + } + + //! Get the currently watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto get() const noexcept -> element_type * + { + return m_ptr; + } + + //! Check if the observer pointer is watching an object. + //! + //! @return True if the observer pointer is watching an object, false otherwise. + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_ptr != nullptr; + } + + //! Get the currently watched object. + //! + //! @return A reference to the currently watched object. + [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t + { + throw_on_null(); + return *m_ptr; + } + + //! Get the currently watched object. + //! + //! @return A pointer to the currently watched object. + [[nodiscard]] constexpr auto operator->() const -> element_type * + { + throw_on_null(); + return m_ptr; + } + + //! Convert the observer pointer to a raw pointer. + //! + //! @return A pointer to the currently watched object. + constexpr explicit operator element_type *() const noexcept + { + return m_ptr; + } + + //! Compare the observer pointer with another observer pointer. + //!> + //! @param other The other observer pointer to compare with. + //! @return The result of the comparison. + constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; + + private: + //! Throw an exception if the observer pointer is null. + //! + //! @throws std::runtime_error if the observer pointer is null. + constexpr auto throw_on_null() const -> void + { + if (m_ptr == nullptr) + { + os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); + } + } + + //! The raw pointer to the watched object. + ElementType * m_ptr{}; + }; + + //! Swap two observer pointers. + //! + //! @param lhs The first observer pointer to swap. + //! @param rhs The second observer pointer to swap. + template + constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void + { + lhs.swap(rhs); + } + + //! Create an observer pointer from a raw pointer. + //! + //! @param pointer The raw pointer to create an observer pointer from. + //! @return An observer pointer to the given raw pointer. + template + constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr + { + return observer_ptr{pointer}; + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/memory b/libs/kstd/include/kstd/memory index cab2fba..493f49a 100644 --- a/libs/kstd/include/kstd/memory +++ b/libs/kstd/include/kstd/memory @@ -1,7 +1,8 @@ -#ifndef KSTD_SHARED_POINTER_HPP -#define KSTD_SHARED_POINTER_HPP +#ifndef KSTD_MEMORY_HPP +#define KSTD_MEMORY_HPP -#include "kstd/bits/shared_ptr.hpp" // IWYU pragma: export -#include "kstd/bits/unique_ptr.hpp" // IWYU pragma: export +#include "kstd/bits/observer_ptr.hpp" // IWYU pragma: export +#include "kstd/bits/shared_ptr.hpp" // IWYU pragma: export +#include "kstd/bits/unique_ptr.hpp" // IWYU pragma: export #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr deleted file mode 100644 index a328331..0000000 --- a/libs/kstd/include/kstd/observer_ptr +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef KSTD_OBSERVER_PTR_HPP -#define KSTD_OBSERVER_PTR_HPP - -#include "kstd/os/error.hpp" - -#include -#include -#include -#include -#include - -namespace kstd -{ - - template - struct observer_ptr - { - //! The type of the element being pointed to. - using element_type = ElementType; - - //! Construct an empty observer pointer. - constexpr observer_ptr() noexcept = default; - - //! Construct an empty observer pointer from a null pointer. - constexpr observer_ptr(std::nullptr_t) noexcept {} - - //! Construct an observer pointer from a raw pointer. - constexpr explicit observer_ptr(element_type * pointer) - : m_ptr{pointer} - {} - - //! Construct an observer pointer from another observer pointer. - template - requires std::convertible_to - constexpr observer_ptr(observer_ptr other) noexcept - : m_ptr{other.get()} - {} - - //! Copy construct an observer pointer. - constexpr observer_ptr(observer_ptr const & other) noexcept = default; - - //! Move construct an observer pointer. - constexpr observer_ptr(observer_ptr && other) noexcept = default; - - //! Stop watching the the watched object. - //! - //! @return The currently watched object, or nullptr if no object is being watched. - [[nodiscard]] constexpr auto release() noexcept -> element_type * - { - return std::exchange(m_ptr, nullptr); - } - - //! Reset the observer pointer. - //! - //! @param pointer The new object to watch. - constexpr auto reset(element_type * pointer = nullptr) noexcept -> void - { - m_ptr = pointer; - } - - //! Swap the observer pointer with another observer pointer. - //! - //! @param other The other observer pointer to swap with. - constexpr auto swap(observer_ptr & other) noexcept -> void - { - std::swap(m_ptr, other.m_ptr); - } - - //! Get the currently watched object. - //! - //! @return The currently watched object, or nullptr if no object is being watched. - [[nodiscard]] constexpr auto get() const noexcept -> element_type * - { - return m_ptr; - } - - //! Check if the observer pointer is watching an object. - //! - //! @return True if the observer pointer is watching an object, false otherwise. - [[nodiscard]] constexpr explicit operator bool() const noexcept - { - return m_ptr != nullptr; - } - - //! Get the currently watched object. - //! - //! @return A reference to the currently watched object. - [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t - { - throw_on_null(); - return *m_ptr; - } - - //! Get the currently watched object. - //! - //! @return A pointer to the currently watched object. - [[nodiscard]] constexpr auto operator->() const -> element_type * - { - throw_on_null(); - return m_ptr; - } - - //! Convert the observer pointer to a raw pointer. - //! - //! @return A pointer to the currently watched object. - constexpr explicit operator element_type *() const noexcept - { - return m_ptr; - } - - //! Compare the observer pointer with another observer pointer. - //!> - //! @param other The other observer pointer to compare with. - //! @return The result of the comparison. - constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; - - private: - //! Throw an exception if the observer pointer is null. - //! - //! @throws std::runtime_error if the observer pointer is null. - constexpr auto throw_on_null() const -> void - { - if (m_ptr == nullptr) - { - os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); - } - } - - //! The raw pointer to the watched object. - ElementType * m_ptr{}; - }; - - //! Swap two observer pointers. - //! - //! @param lhs The first observer pointer to swap. - //! @param rhs The second observer pointer to swap. - template - constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void - { - lhs.swap(rhs); - } - - //! Create an observer pointer from a raw pointer. - //! - //! @param pointer The raw pointer to create an observer pointer from. - //! @return An observer pointer to the given raw pointer. - template - constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr - { - return observer_ptr{pointer}; - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp index 5d94098..006ebde 100644 --- a/libs/kstd/tests/src/observer_ptr.cpp +++ b/libs/kstd/tests/src/observer_ptr.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include -- cgit v1.2.3 From e7af7ceea2324dcf7d2222986c09d1c478ee4c7e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 13:22:28 +0200 Subject: kstd: make string formattable --- libs/kstd/include/kstd/string | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string index 075422e..4ce19ce 100644 --- a/libs/kstd/include/kstd/string +++ b/libs/kstd/include/kstd/string @@ -1,6 +1,10 @@ #ifndef KSTD_STRING_HPP #define KSTD_STRING_HPP +#include "kstd/bits/format/context.hpp" +#include "kstd/bits/format/formatter.hpp" +#include "kstd/bits/format/formatter/string_view.hpp" + #include #include #include @@ -343,6 +347,15 @@ namespace kstd return !(lhs == rhs); } + template<> + struct formatter : formatter + { + auto format(string const & str, format_context & context) const -> void + { + formatter::format(str.view(), context); + } + }; + } // namespace kstd #endif \ No newline at end of file -- cgit v1.2.3 From d0c532af74d8d486d734904fd330d5dae7f49754 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 13:36:01 +0200 Subject: kapi: add basic device subsystem --- kapi/include/kapi/devices.hpp | 46 ++++++++++++++++++++ kernel/CMakeLists.txt | 2 + kernel/include/kernel/devices/root_bus.hpp | 32 ++++++++++++++ kernel/kapi/devices.cpp | 63 ++++++++++++++++++++++++++++ kernel/src/devices/root_bus.cpp | 55 ++++++++++++++++++++++++ kernel/src/main.cpp | 4 ++ libs/kstd/include/kstd/bits/observer_ptr.hpp | 6 +++ 7 files changed, 208 insertions(+) create mode 100644 kapi/include/kapi/devices.hpp create mode 100644 kernel/include/kernel/devices/root_bus.hpp create mode 100644 kernel/kapi/devices.cpp create mode 100644 kernel/src/devices/root_bus.cpp diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp new file mode 100644 index 0000000..60a39bd --- /dev/null +++ b/kapi/include/kapi/devices.hpp @@ -0,0 +1,46 @@ +#ifndef TEACHOS_KAPI_DEVICES_HPP +#define TEACHOS_KAPI_DEVICES_HPP + +#include "kapi/devices/bus.hpp" // IWYU pragma: export +#include "kapi/devices/device.hpp" // IWYU pragma: export + +#include + +namespace kapi::devices +{ + + //! @addtogroup kernel-defined + //! @{ + + //! Initialize the kernel's device management subsystem. + auto init() -> void; + + //! Get the virtual system root bus. + //! + //! @warning This function will panic if the root bus has not been initialized. + //! + //! @return a reference to the root bus. + auto get_root_bus() -> bus &; + + //! Ask the kernel to allocate a new major number. + //! + //! @return a new, unused major number. + auto allocate_major_number() -> std::size_t; + + //! Register a new device with the kernel's device manager. + //! + //! @param device The device to register. + //! @return true if the device was registered successfully, false otherwise. + auto register_device(device & device) -> bool; + + //! Unregister a device from the kernel's device manager. + //! + //! @param device The device to unregister. + //! @return true if the device was unregistered successfully, false otherwise. + auto unregister_device(device & device) -> bool; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index f283588..9868eb9 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -3,6 +3,7 @@ add_library("kernel_objs" OBJECT "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/devices.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" @@ -17,6 +18,7 @@ add_library("kernel_objs" OBJECT "src/memory.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" + "src/devices/root_bus.cpp" "src/devices/storage/controller.cpp" "src/devices/storage/management.cpp" "src/devices/storage/ram_disk/controller.cpp" diff --git a/kernel/include/kernel/devices/root_bus.hpp b/kernel/include/kernel/devices/root_bus.hpp new file mode 100644 index 0000000..f7bfbfb --- /dev/null +++ b/kernel/include/kernel/devices/root_bus.hpp @@ -0,0 +1,32 @@ +#ifndef TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP +#define TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP + +#include "kapi/devices/bus.hpp" +#include "kapi/devices/device.hpp" + +#include +#include +#include + +namespace kernel::devices +{ + + struct root_bus final : kapi::devices::bus + { + root_bus(); + + auto add_child(kstd::unique_ptr child) -> void override; + + [[nodiscard]] auto children() const -> kstd::vector> const & override; + + auto init() -> bool override; + + private: + kstd::vector> m_children{}; + kstd::vector> m_observers{}; + bool m_initialized{}; + }; + +} // namespace kernel::devices + +#endif \ No newline at end of file diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp new file mode 100644 index 0000000..dc19ab4 --- /dev/null +++ b/kernel/kapi/devices.cpp @@ -0,0 +1,63 @@ +#include "kapi/devices.hpp" + +#include "kapi/system.hpp" + +#include "kernel/devices/root_bus.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace kapi::devices +{ + + namespace + { + auto constinit next_major_number = std::atomic_size_t{0}; + auto constinit root_bus = std::optional{}; + auto constinit device_tree = kstd::flat_map, kstd::observer_ptr>{}; + } // namespace + + auto init() -> void + { + auto static is_initialized = std::atomic_flag{}; + if (is_initialized.test_and_set()) + { + return; + } + + root_bus.emplace(); + root_bus->init(); + } + + auto get_root_bus() -> bus & + { + if (!root_bus.has_value()) + { + kapi::system::panic("[kernel:devices] Root bus not initialized!"); + } + return *root_bus; + } + + auto allocate_major_number() -> std::size_t + { + return next_major_number++; + } + + auto register_device(device & device) -> bool + { + return device_tree.emplace(std::pair{device.major(), device.minor()}, &device).second; + } + + auto unregister_device(device &) -> bool + { + kstd::println("[kernel:devices] TODO: implement device deregistration"); + return false; + } + +} // namespace kapi::devices \ No newline at end of file diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp new file mode 100644 index 0000000..a7f3c1a --- /dev/null +++ b/kernel/src/devices/root_bus.cpp @@ -0,0 +1,55 @@ +#include "kernel/devices/root_bus.hpp" + +#include "kapi/devices.hpp" +#include "kapi/devices/bus.hpp" +#include "kapi/devices/device.hpp" +#include "kapi/system.hpp" + +#include +#include +#include + +#include +#include + +namespace kernel::devices +{ + + root_bus::root_bus() + : kapi::devices::bus{kapi::devices::allocate_major_number(), 0, "system"} + {} + + auto root_bus::add_child(kstd::unique_ptr child) -> void + { + auto observer = m_observers.emplace_back(child.get()); + m_children.push_back(std::move(child)); + + if (m_initialized) + { + kstd::println("Initializing child device '{}'", observer->name()); + if (!observer->init()) + { + kapi::system::panic("[kernel:devices] Failed to initialize child device"); + } + } + } + + auto root_bus::children() const -> kstd::vector> const & + { + return m_observers; + } + + auto root_bus::init() -> bool + { + if (m_initialized) + { + kapi::system::panic("[kernel:devices] Root bus already initialized!"); + } + + return std::ranges::fold_left(m_children, true, [](bool acc, auto & child) -> bool { + kstd::println("[kernel:devices] Initializing child device '{}'", child->name()); + return acc && child->init(); + }); + } + +} // namespace kernel::devices \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 37b4c5b..9d6028d 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,6 +1,7 @@ #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" +#include "kapi/devices.hpp" #include "kapi/interrupts.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" @@ -179,6 +180,9 @@ auto main() -> int kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); + kapi::devices::init(); + kstd::println("[OS] System root bus initialized."); + kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); diff --git a/libs/kstd/include/kstd/bits/observer_ptr.hpp b/libs/kstd/include/kstd/bits/observer_ptr.hpp index 43ea409..1c5da15 100644 --- a/libs/kstd/include/kstd/bits/observer_ptr.hpp +++ b/libs/kstd/include/kstd/bits/observer_ptr.hpp @@ -44,6 +44,12 @@ namespace kstd //! Move construct an observer pointer. constexpr observer_ptr(observer_ptr && other) noexcept = default; + //! Copy assign an observer pointer. + constexpr auto operator=(observer_ptr const & other) noexcept -> observer_ptr & = default; + + //! Move assign an observer pointer. + constexpr auto operator=(observer_ptr && other) noexcept -> observer_ptr & = default; + //! Stop watching the the watched object. //! //! @return The currently watched object, or nullptr if no object is being watched. -- cgit v1.2.3 From b84c4c9d8c90f3d3fd5a60de282278912fad2f04 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 13:59:27 +0200 Subject: x86_64/devices: implement ISA bus stub --- arch/x86_64/CMakeLists.txt | 4 +++ arch/x86_64/include/arch/bus/isa.hpp | 31 +++++++++++++++++ arch/x86_64/kapi/devices.cpp | 22 ++++++++++++ arch/x86_64/src/bus/isa.cpp | 56 ++++++++++++++++++++++++++++++ kapi/include/kapi/devices.hpp | 8 +++++ kernel/include/kernel/devices/root_bus.hpp | 4 ++- kernel/src/devices/root_bus.cpp | 4 +-- kernel/src/main.cpp | 3 ++ libs/kstd/include/kstd/bits/unique_ptr.hpp | 11 ++++++ 9 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 arch/x86_64/include/arch/bus/isa.hpp create mode 100644 arch/x86_64/kapi/devices.cpp create mode 100644 arch/x86_64/src/bus/isa.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 4427e4c..21dceef 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources("x86_64" PRIVATE "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/devices.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" @@ -24,6 +25,9 @@ target_sources("x86_64" PRIVATE "src/cpu/interrupts.cpp" "src/cpu/interrupt_stubs.S" + # Bus Initialization + "src/bus/isa.cpp" + # Low-level bootstrap "src/boot/boot32.S" "src/boot/entry64.s" diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp new file mode 100644 index 0000000..41dda93 --- /dev/null +++ b/arch/x86_64/include/arch/bus/isa.hpp @@ -0,0 +1,31 @@ +#ifndef TEACHOS_X86_64_BUS_ISA_HPP +#define TEACHOS_X86_64_BUS_ISA_HPP + +#include "kapi/devices/bus.hpp" +#include "kapi/devices/device.hpp" + +#include +#include + +namespace arch::bus +{ + + struct isa final : public kapi::devices::bus + { + isa(); + + auto add_child(kstd::unique_ptr child) -> void override; + + [[nodiscard]] auto children() const -> kstd::vector> const & override; + + auto init() -> bool override; + + private: + kstd::vector> m_devices{}; + kstd::vector> m_observers{}; + bool m_initialized{}; + }; + +} // namespace arch::bus + +#endif // TEACHOS_X86_64_BUS_ISA_HPP diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp new file mode 100644 index 0000000..25185d6 --- /dev/null +++ b/arch/x86_64/kapi/devices.cpp @@ -0,0 +1,22 @@ +#include "kapi/devices.hpp" + +#include "arch/bus/isa.hpp" + +#include +#include + +#include + +namespace kapi::devices +{ + + auto init_platform_devices() -> void + { + kstd::println("[x86_64:devices] Initializing ISA bus..."); + auto isa_bus = kstd::make_unique(); + + auto & root_bus = get_root_bus(); + root_bus.add_child(std::move(isa_bus)); + } + +} // namespace kapi::devices \ No newline at end of file diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp new file mode 100644 index 0000000..3fe4f6f --- /dev/null +++ b/arch/x86_64/src/bus/isa.cpp @@ -0,0 +1,56 @@ +#include "arch/bus/isa.hpp" + +#include "kapi/devices.hpp" +#include "kapi/devices/device.hpp" +#include "kapi/system.hpp" + +#include +#include +#include + +#include +#include + +namespace arch::bus +{ + + isa::isa() + : kapi::devices::bus(kapi::devices::allocate_major_number(), 0, "isa") + {} + + auto isa::add_child(kstd::unique_ptr child) -> void + { + auto observer = m_observers.emplace_back(child.get()); + m_devices.push_back(std::move(child)); + + if (m_initialized) + { + kstd::println("[bus:{}} Initializing child device '{}'", name(), observer->name()); + if (!observer->init()) + { + kapi::system::panic("[x86_64:devices] Failed to initialize child device"); + } + } + } + + auto isa::children() const -> kstd::vector> const & + { + return m_observers; + } + + auto isa::init() -> bool + { + if (m_initialized) + { + kapi::system::panic("[x86_64:devices] ISA bus already initialized!"); + } + + m_initialized = std::ranges::fold_left(m_devices, true, [](bool acc, auto & child) -> bool { + kstd::println("[x86_64:devices] Initializing child device '{}'", child->name()); + return acc && child->init(); + }); + + return m_initialized; + } + +} // namespace arch::bus \ No newline at end of file diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp index 60a39bd..2028a64 100644 --- a/kapi/include/kapi/devices.hpp +++ b/kapi/include/kapi/devices.hpp @@ -41,6 +41,14 @@ namespace kapi::devices //! @} + //! @addtogroup platform-defined + //! @{ + + //! Initialize the platform's device tree. + auto init_platform_devices() -> void; + + //! @} + } // namespace kapi::devices #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/root_bus.hpp b/kernel/include/kernel/devices/root_bus.hpp index f7bfbfb..d92914d 100644 --- a/kernel/include/kernel/devices/root_bus.hpp +++ b/kernel/include/kernel/devices/root_bus.hpp @@ -8,6 +8,8 @@ #include #include +#include + namespace kernel::devices { @@ -24,7 +26,7 @@ namespace kernel::devices private: kstd::vector> m_children{}; kstd::vector> m_observers{}; - bool m_initialized{}; + std::atomic_flag m_initialized{}; }; } // namespace kernel::devices diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp index a7f3c1a..75b5b80 100644 --- a/kernel/src/devices/root_bus.cpp +++ b/kernel/src/devices/root_bus.cpp @@ -24,7 +24,7 @@ namespace kernel::devices auto observer = m_observers.emplace_back(child.get()); m_children.push_back(std::move(child)); - if (m_initialized) + if (m_initialized.test()) { kstd::println("Initializing child device '{}'", observer->name()); if (!observer->init()) @@ -41,7 +41,7 @@ namespace kernel::devices auto root_bus::init() -> bool { - if (m_initialized) + if (m_initialized.test_and_set()) { kapi::system::panic("[kernel:devices] Root bus already initialized!"); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 9d6028d..eaaf87f 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -183,6 +183,9 @@ auto main() -> int kapi::devices::init(); kstd::println("[OS] System root bus initialized."); + kapi::devices::init_platform_devices(); + kstd::println("[OS] Platform devices initialized."); + kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); diff --git a/libs/kstd/include/kstd/bits/unique_ptr.hpp b/libs/kstd/include/kstd/bits/unique_ptr.hpp index e0870b1..f50335c 100644 --- a/libs/kstd/include/kstd/bits/unique_ptr.hpp +++ b/libs/kstd/include/kstd/bits/unique_ptr.hpp @@ -16,6 +16,9 @@ namespace kstd template struct unique_ptr { + template + friend struct unique_ptr; + /** * @brief Constructor. * @@ -40,6 +43,14 @@ namespace kstd */ unique_ptr(unique_ptr const &) = delete; + template + requires(std::is_convertible_v) + unique_ptr(unique_ptr && other) noexcept + : pointer(other.pointer) + { + other.pointer = nullptr; + } + /** * @brief Deleted copy assignment operator to enforce unique ownership. */ -- cgit v1.2.3 From 66ffd2ad8c793c4eea1527848fe4772e42595718 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 14:24:52 +0200 Subject: kapi: extract common bus code --- arch/x86_64/include/arch/bus/isa.hpp | 15 -------- arch/x86_64/src/bus/isa.cpp | 44 --------------------- kapi/include/kapi/devices/bus.hpp | 61 ++++++++++++++++++++++++++++-- kernel/include/kernel/devices/root_bus.hpp | 18 --------- kernel/kapi/devices.cpp | 4 +- kernel/src/devices/root_bus.cpp | 43 --------------------- libs/kstd/include/kstd/bits/unique_ptr.hpp | 22 ++++------- 7 files changed, 66 insertions(+), 141 deletions(-) diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp index 41dda93..bd92b2e 100644 --- a/arch/x86_64/include/arch/bus/isa.hpp +++ b/arch/x86_64/include/arch/bus/isa.hpp @@ -2,10 +2,6 @@ #define TEACHOS_X86_64_BUS_ISA_HPP #include "kapi/devices/bus.hpp" -#include "kapi/devices/device.hpp" - -#include -#include namespace arch::bus { @@ -13,17 +9,6 @@ namespace arch::bus struct isa final : public kapi::devices::bus { isa(); - - auto add_child(kstd::unique_ptr child) -> void override; - - [[nodiscard]] auto children() const -> kstd::vector> const & override; - - auto init() -> bool override; - - private: - kstd::vector> m_devices{}; - kstd::vector> m_observers{}; - bool m_initialized{}; }; } // namespace arch::bus diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp index 3fe4f6f..2ad4d21 100644 --- a/arch/x86_64/src/bus/isa.cpp +++ b/arch/x86_64/src/bus/isa.cpp @@ -1,15 +1,6 @@ #include "arch/bus/isa.hpp" #include "kapi/devices.hpp" -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" - -#include -#include -#include - -#include -#include namespace arch::bus { @@ -18,39 +9,4 @@ namespace arch::bus : kapi::devices::bus(kapi::devices::allocate_major_number(), 0, "isa") {} - auto isa::add_child(kstd::unique_ptr child) -> void - { - auto observer = m_observers.emplace_back(child.get()); - m_devices.push_back(std::move(child)); - - if (m_initialized) - { - kstd::println("[bus:{}} Initializing child device '{}'", name(), observer->name()); - if (!observer->init()) - { - kapi::system::panic("[x86_64:devices] Failed to initialize child device"); - } - } - } - - auto isa::children() const -> kstd::vector> const & - { - return m_observers; - } - - auto isa::init() -> bool - { - if (m_initialized) - { - kapi::system::panic("[x86_64:devices] ISA bus already initialized!"); - } - - m_initialized = std::ranges::fold_left(m_devices, true, [](bool acc, auto & child) -> bool { - kstd::println("[x86_64:devices] Initializing child device '{}'", child->name()); - return acc && child->init(); - }); - - return m_initialized; - } - } // namespace arch::bus \ No newline at end of file diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index a8d7df8..ccaf0f2 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -2,12 +2,17 @@ #define TEACHOS_KAPI_DEVICES_BUS_HPP #include "kapi/devices/device.hpp" +#include "kapi/system.hpp" #include +#include #include #include +#include +#include #include +#include namespace kapi::devices { @@ -23,17 +28,65 @@ namespace kapi::devices : device(major, minor, name) {} + //! Initialize the bus and all of its children. + //! + //! @return true iff. the bus and all of its children are healthy, false otherwise. + auto init() -> bool final + { + if (m_initialized.test_and_set()) + { + return true; + } + + if (!probe()) + { + return false; + } + + return std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { + kstd::println("[kAPI:BUS] Initializing child device {}@{}", child->name(), name()); + return acc && child->init(); + }); + } + //! Attach a child device to this bus. //! //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. //! //! @param child The child device to attach. - virtual auto add_child(kstd::unique_ptr child) -> void = 0; + auto add_child(kstd::unique_ptr child) -> void + { + auto observer = m_observers.emplace_back(child.get()); + m_devices.push_back(std::move(child)); - //! Get a list of all child devices attached to this bus. + if (m_initialized.test()) + { + kstd::println("[kAPI:BUS] Initializing child device {}@{}", observer->name(), name()); + if (!observer->init()) + { + kapi::system::panic("[kAPI:BUS] Failed to initialize child device"); + } + } + } + + [[nodiscard]] auto children() const -> kstd::vector> const & + { + return m_observers; + } + + protected: + //! Probe the bus hardware state. //! - //! @return A reference to list of child devices of this bus. - [[nodiscard]] virtual auto children() const -> kstd::vector> const & = 0; + //! @return true iff. the bus hardware is healthy, false otherwise. + auto virtual probe() -> bool + { + return true; + } + + private: + kstd::vector> m_devices; + kstd::vector> m_observers; + std::atomic_flag m_initialized{}; }; } // namespace kapi::devices diff --git a/kernel/include/kernel/devices/root_bus.hpp b/kernel/include/kernel/devices/root_bus.hpp index d92914d..660b715 100644 --- a/kernel/include/kernel/devices/root_bus.hpp +++ b/kernel/include/kernel/devices/root_bus.hpp @@ -2,13 +2,6 @@ #define TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP #include "kapi/devices/bus.hpp" -#include "kapi/devices/device.hpp" - -#include -#include -#include - -#include namespace kernel::devices { @@ -16,17 +9,6 @@ namespace kernel::devices struct root_bus final : kapi::devices::bus { root_bus(); - - auto add_child(kstd::unique_ptr child) -> void override; - - [[nodiscard]] auto children() const -> kstd::vector> const & override; - - auto init() -> bool override; - - private: - kstd::vector> m_children{}; - kstd::vector> m_observers{}; - std::atomic_flag m_initialized{}; }; } // namespace kernel::devices diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index dc19ab4..dbf5e68 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -39,7 +39,7 @@ namespace kapi::devices { if (!root_bus.has_value()) { - kapi::system::panic("[kernel:devices] Root bus not initialized!"); + kapi::system::panic("[OS:DEV] Root bus not initialized!"); } return *root_bus; } @@ -56,7 +56,7 @@ namespace kapi::devices auto unregister_device(device &) -> bool { - kstd::println("[kernel:devices] TODO: implement device deregistration"); + kstd::println("[OS:DEV] TODO: implement device deregistration"); return false; } diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp index 75b5b80..d3ba23f 100644 --- a/kernel/src/devices/root_bus.cpp +++ b/kernel/src/devices/root_bus.cpp @@ -1,16 +1,6 @@ #include "kernel/devices/root_bus.hpp" #include "kapi/devices.hpp" -#include "kapi/devices/bus.hpp" -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" - -#include -#include -#include - -#include -#include namespace kernel::devices { @@ -19,37 +9,4 @@ namespace kernel::devices : kapi::devices::bus{kapi::devices::allocate_major_number(), 0, "system"} {} - auto root_bus::add_child(kstd::unique_ptr child) -> void - { - auto observer = m_observers.emplace_back(child.get()); - m_children.push_back(std::move(child)); - - if (m_initialized.test()) - { - kstd::println("Initializing child device '{}'", observer->name()); - if (!observer->init()) - { - kapi::system::panic("[kernel:devices] Failed to initialize child device"); - } - } - } - - auto root_bus::children() const -> kstd::vector> const & - { - return m_observers; - } - - auto root_bus::init() -> bool - { - if (m_initialized.test_and_set()) - { - kapi::system::panic("[kernel:devices] Root bus already initialized!"); - } - - return std::ranges::fold_left(m_children, true, [](bool acc, auto & child) -> bool { - kstd::println("[kernel:devices] Initializing child device '{}'", child->name()); - return acc && child->init(); - }); - } - } // namespace kernel::devices \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/unique_ptr.hpp b/libs/kstd/include/kstd/bits/unique_ptr.hpp index f50335c..3d803b4 100644 --- a/libs/kstd/include/kstd/bits/unique_ptr.hpp +++ b/libs/kstd/include/kstd/bits/unique_ptr.hpp @@ -46,10 +46,8 @@ namespace kstd template requires(std::is_convertible_v) unique_ptr(unique_ptr && other) noexcept - : pointer(other.pointer) - { - other.pointer = nullptr; - } + : pointer{std::exchange(other.pointer, nullptr)} + {} /** * @brief Deleted copy assignment operator to enforce unique ownership. @@ -62,10 +60,8 @@ namespace kstd * @param other Unique pointer to move from. */ unique_ptr(unique_ptr && other) noexcept - : pointer(other.pointer) - { - other.pointer = nullptr; - } + : pointer{std::exchange(other.pointer, nullptr)} + {} /** * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()). @@ -78,8 +74,7 @@ namespace kstd if (this != &other) { delete pointer; - pointer = other.pointer; - other.pointer = nullptr; + pointer = std::exchange(other.pointer, nullptr); } return *this; } @@ -134,9 +129,7 @@ namespace kstd */ auto release() -> T * { - T * temp = pointer; - pointer = nullptr; - return temp; + return std::exchange(pointer, nullptr); } /** @@ -150,8 +143,7 @@ namespace kstd */ auto reset(T * ptr = nullptr) -> void { - delete pointer; - pointer = ptr; + delete std::exchange(pointer, ptr); } /** -- cgit v1.2.3 From ab4c59912c526d515e6e72188c08a7f92e5573e8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:07:54 +0200 Subject: x86_64: implement legacy PIT driver --- arch/x86_64/CMakeLists.txt | 3 ++ arch/x86_64/include/arch/devices/legacy_pit.hpp | 28 +++++++++++++ arch/x86_64/kapi/devices.cpp | 11 +++++ arch/x86_64/src/cpu/initialization.cpp | 2 +- arch/x86_64/src/devices/legacy_pit.cpp | 54 +++++++++++++++++++++++++ kapi/include/kapi/devices.hpp | 24 ++--------- kapi/include/kapi/devices/bus.hpp | 2 + kapi/include/kapi/devices/manager.hpp | 37 +++++++++++++++++ kernel/kapi/devices.cpp | 6 ++- kernel/src/main.cpp | 4 +- 10 files changed, 146 insertions(+), 25 deletions(-) create mode 100644 arch/x86_64/include/arch/devices/legacy_pit.hpp create mode 100644 arch/x86_64/src/devices/legacy_pit.cpp create mode 100644 kapi/include/kapi/devices/manager.hpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 21dceef..83cae0b 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -37,6 +37,9 @@ target_sources("x86_64" PRIVATE # Debug interfaces "src/debug/qemu_output.cpp" + # Devices + "src/devices/legacy_pit.cpp" + # Memory management "src/memory/kernel_mapper.cpp" "src/memory/higher_half_mapper.cpp" diff --git a/arch/x86_64/include/arch/devices/legacy_pit.hpp b/arch/x86_64/include/arch/devices/legacy_pit.hpp new file mode 100644 index 0000000..d28e4d6 --- /dev/null +++ b/arch/x86_64/include/arch/devices/legacy_pit.hpp @@ -0,0 +1,28 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP + +#include "kapi/devices/device.hpp" +#include "kapi/interrupts.hpp" + +#include + +namespace arch::devices +{ + + struct legacy_pit : kapi::devices::device, kapi::interrupts::handler + { + explicit legacy_pit(std::uint32_t frequency_in_hz); + + auto init() -> bool override; + + auto handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status override; + + private: + std::uint32_t m_irq_number{}; + std::uint32_t m_frequency_in_hz{}; + std::uint64_t m_ticks{}; + }; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index 25185d6..7aa7090 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -1,20 +1,31 @@ #include "kapi/devices.hpp" #include "arch/bus/isa.hpp" +#include "arch/devices/legacy_pit.hpp" #include #include +#include #include namespace kapi::devices { + namespace + { + constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; + } + auto init_platform_devices() -> void { kstd::println("[x86_64:devices] Initializing ISA bus..."); + auto isa_bus = kstd::make_unique(); + auto pit = kstd::make_unique(pit_frequency_in_hz); + isa_bus->add_child(std::move(pit)); + auto & root_bus = get_root_bus(); root_bus.add_child(std::move(isa_bus)); } diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp index 878fa07..b808c76 100644 --- a/arch/x86_64/src/cpu/initialization.cpp +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -139,7 +139,7 @@ namespace arch::cpu constexpr auto pic_cascade_address = std::uint8_t{0x04}; constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; - constexpr auto pic_master_mask = std::uint8_t{0x01}; + constexpr auto pic_master_mask = std::uint8_t{0x00}; constexpr auto pic_slave_mask = std::uint8_t{0x00}; pic_master_control_port::write(pic_init_command); diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp new file mode 100644 index 0000000..f2fb70e --- /dev/null +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -0,0 +1,54 @@ +#include "arch/devices/legacy_pit.hpp" + +#include "kapi/devices.hpp" +#include "kapi/devices/device.hpp" +#include "kapi/interrupts.hpp" + +#include "arch/device_io/port_io.hpp" + +#include + +namespace arch::devices +{ + + namespace + { + using command_port = io::port<0x43, std::uint8_t, io::port_write>; + using channel_0_port = io::port<0x40, std::uint8_t, io::port_write>; + using channel_1_port = io::port<0x41, std::uint8_t, io::port_write>; + using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; + } // namespace + + legacy_pit::legacy_pit(std::uint32_t frequency_in_hz) + : kapi::devices::device{kapi::devices::allocate_major_number(), 0, "legacy_pit"} + , m_irq_number{0} + , m_frequency_in_hz{frequency_in_hz} + {} + + auto legacy_pit::init() -> bool + { + constexpr auto base_frequency = 1'193'182u; + auto divisor = static_cast(base_frequency / m_frequency_in_hz); + + kapi::interrupts::register_handler(m_irq_number, *this); + + command_port::write(0x36); // NOLINT + channel_0_port::write(divisor & 0xff); // NOLINT + channel_0_port::write(divisor >> 8 & 0xff); // NOLINT + + return true; + } + + auto legacy_pit::handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status + { + if (irq_number != m_irq_number) + { + return kapi::interrupts::status::unhandled; + } + + ++m_ticks; + + return kapi::interrupts::status::handled; + } + +} // namespace arch::devices \ No newline at end of file diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp index 2028a64..5c01b2f 100644 --- a/kapi/include/kapi/devices.hpp +++ b/kapi/include/kapi/devices.hpp @@ -1,10 +1,9 @@ #ifndef TEACHOS_KAPI_DEVICES_HPP #define TEACHOS_KAPI_DEVICES_HPP -#include "kapi/devices/bus.hpp" // IWYU pragma: export -#include "kapi/devices/device.hpp" // IWYU pragma: export - -#include +#include "kapi/devices/bus.hpp" // IWYU pragma: export +#include "kapi/devices/device.hpp" // IWYU pragma: export +#include "kapi/devices/manager.hpp" // IWYU pragma: export namespace kapi::devices { @@ -22,23 +21,6 @@ namespace kapi::devices //! @return a reference to the root bus. auto get_root_bus() -> bus &; - //! Ask the kernel to allocate a new major number. - //! - //! @return a new, unused major number. - auto allocate_major_number() -> std::size_t; - - //! Register a new device with the kernel's device manager. - //! - //! @param device The device to register. - //! @return true if the device was registered successfully, false otherwise. - auto register_device(device & device) -> bool; - - //! Unregister a device from the kernel's device manager. - //! - //! @param device The device to unregister. - //! @return true if the device was unregistered successfully, false otherwise. - auto unregister_device(device & device) -> bool; - //! @} //! @addtogroup platform-defined diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index ccaf0f2..a5457e1 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -2,6 +2,7 @@ #define TEACHOS_KAPI_DEVICES_BUS_HPP #include "kapi/devices/device.hpp" +#include "kapi/devices/manager.hpp" #include "kapi/system.hpp" #include @@ -58,6 +59,7 @@ namespace kapi::devices { auto observer = m_observers.emplace_back(child.get()); m_devices.push_back(std::move(child)); + kapi::devices::register_device(*observer); if (m_initialized.test()) { diff --git a/kapi/include/kapi/devices/manager.hpp b/kapi/include/kapi/devices/manager.hpp new file mode 100644 index 0000000..56cd678 --- /dev/null +++ b/kapi/include/kapi/devices/manager.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_KAPI_DEVICES_MANAGER_HPP +#define TEACHOS_KAPI_DEVICES_MANAGER_HPP + +// IWYU pragma: private, include "kapi/devices.hpp" + +#include "kapi/devices/device.hpp" + +#include + +namespace kapi::devices +{ + + //! @addtogroup kernel-defined + //! @{ + + //! Ask the kernel to allocate a new major number. + //! + //! @return a new, unused major number. + auto allocate_major_number() -> std::size_t; + + //! Register a new device with the kernel's device manager. + //! + //! @param device The device to register. + //! @return true if the device was registered successfully, false otherwise. + auto register_device(device & device) -> bool; + + //! Unregister a device from the kernel's device manager. + //! + //! @param device The device to unregister. + //! @return true if the device was unregistered successfully, false otherwise. + auto unregister_device(device & device) -> bool; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index dbf5e68..c0b738e 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -31,8 +31,9 @@ namespace kapi::devices return; } - root_bus.emplace(); - root_bus->init(); + auto & bus = root_bus.emplace(); + register_device(bus); + bus.init(); } auto get_root_bus() -> bus & @@ -51,6 +52,7 @@ namespace kapi::devices auto register_device(device & device) -> bool { + kstd::println("[OS:DEV] Registering device {}@{}:{}", device.name(), device.major(), device.minor()); return device_tree.emplace(std::pair{device.major(), device.minor()}, &device).second; } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index eaaf87f..2eaa2d8 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -173,7 +173,6 @@ auto main() -> int kstd::println("[OS] IO subsystem initialized."); kapi::cpu::init(); - kapi::interrupts::enable(); kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); @@ -186,6 +185,9 @@ auto main() -> int kapi::devices::init_platform_devices(); kstd::println("[OS] Platform devices initialized."); + kapi::interrupts::enable(); + kstd::println("[OS] Interrupts enabled."); + kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); -- cgit v1.2.3 From 3e80b6baa8f9666a9dd3cd4531bc68a3de4fee92 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:18:05 +0200 Subject: kapi: allow for device searches --- kapi/include/kapi/devices/manager.hpp | 16 ++++++++++++++++ kernel/kapi/devices.cpp | 22 ++++++++++++++++++++++ libs/kstd/include/kstd/bits/flat_map.hpp | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/kapi/include/kapi/devices/manager.hpp b/kapi/include/kapi/devices/manager.hpp index 56cd678..7817fbc 100644 --- a/kapi/include/kapi/devices/manager.hpp +++ b/kapi/include/kapi/devices/manager.hpp @@ -5,7 +5,10 @@ #include "kapi/devices/device.hpp" +#include + #include +#include namespace kapi::devices { @@ -30,6 +33,19 @@ namespace kapi::devices //! @return true if the device was unregistered successfully, false otherwise. auto unregister_device(device & device) -> bool; + //! Find a device by its major and minor numbers. + //! + //! @param major the major number of the device. + //! @param minor the minor number of the device. + //! @return a pointer to the device iff. the device was found, nullptr otherwise. + auto find_device(std::size_t major, std::size_t minor) -> kstd::observer_ptr; + + //! Find a device by its name. + //! + //! @param name the name of the device. + //! @return a pointer to the device iff. the device was found, nullptr otherwise. + auto find_device(std::string_view name) -> kstd::observer_ptr; + //! @} } // namespace kapi::devices diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index c0b738e..7d35778 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace kapi::devices @@ -62,4 +63,25 @@ namespace kapi::devices return false; } + auto find_device(std::size_t major, std::size_t minor) -> kstd::observer_ptr + { + if (device_tree.contains(std::pair{major, minor})) + { + return device_tree.at(std::pair{major, minor}); + } + return nullptr; + } + + auto find_device(std::string_view name) -> kstd::observer_ptr + { + for (auto const & [key, value] : device_tree) + { + if (value->name() == name) + { + return value; + } + } + return nullptr; + } + } // namespace kapi::devices \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/flat_map.hpp b/libs/kstd/include/kstd/bits/flat_map.hpp index 9455549..fe46203 100644 --- a/libs/kstd/include/kstd/bits/flat_map.hpp +++ b/libs/kstd/include/kstd/bits/flat_map.hpp @@ -45,7 +45,7 @@ namespace kstd::bits template requires(Index >= 0 && Index <= 1) - constexpr auto get() const noexcept -> decltype(auto) + [[nodiscard]] constexpr auto get() const noexcept -> decltype(auto) { if constexpr (Index == 0) { -- cgit v1.2.3 From 33b43603936ed0108d67853727a17d6b3740b445 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:43:34 +0200 Subject: kapi/bus: ensure all devices get initialized --- kapi/include/kapi/devices/bus.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index a5457e1..ee774b7 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -46,7 +46,7 @@ namespace kapi::devices return std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { kstd::println("[kAPI:BUS] Initializing child device {}@{}", child->name(), name()); - return acc && child->init(); + return child->init() && acc; }); } -- cgit v1.2.3 From 21489576381d827871e7cdf060929c5d7f3d4e9f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:49:14 +0200 Subject: devices: don't automatically allocate major numbers in ctors --- arch/x86_64/include/arch/bus/isa.hpp | 4 +++- arch/x86_64/include/arch/devices/legacy_pit.hpp | 3 ++- arch/x86_64/kapi/devices.cpp | 6 ++++-- arch/x86_64/src/bus/isa.cpp | 6 ++++-- arch/x86_64/src/devices/legacy_pit.cpp | 5 +++-- kernel/kapi/devices.cpp | 2 +- kernel/src/devices/root_bus.cpp | 2 +- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp index bd92b2e..5deed25 100644 --- a/arch/x86_64/include/arch/bus/isa.hpp +++ b/arch/x86_64/include/arch/bus/isa.hpp @@ -3,12 +3,14 @@ #include "kapi/devices/bus.hpp" +#include + namespace arch::bus { struct isa final : public kapi::devices::bus { - isa(); + isa(std::size_t major); }; } // namespace arch::bus diff --git a/arch/x86_64/include/arch/devices/legacy_pit.hpp b/arch/x86_64/include/arch/devices/legacy_pit.hpp index d28e4d6..de742ae 100644 --- a/arch/x86_64/include/arch/devices/legacy_pit.hpp +++ b/arch/x86_64/include/arch/devices/legacy_pit.hpp @@ -4,6 +4,7 @@ #include "kapi/devices/device.hpp" #include "kapi/interrupts.hpp" +#include #include namespace arch::devices @@ -11,7 +12,7 @@ namespace arch::devices struct legacy_pit : kapi::devices::device, kapi::interrupts::handler { - explicit legacy_pit(std::uint32_t frequency_in_hz); + legacy_pit(std::size_t major, std::uint32_t frequency_in_hz); auto init() -> bool override; diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index 7aa7090..b15503d 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -21,9 +21,11 @@ namespace kapi::devices { kstd::println("[x86_64:devices] Initializing ISA bus..."); - auto isa_bus = kstd::make_unique(); + auto isa_major_number = kapi::devices::allocate_major_number(); + auto isa_bus = kstd::make_unique(isa_major_number); - auto pit = kstd::make_unique(pit_frequency_in_hz); + auto pit_major_number = kapi::devices::allocate_major_number(); + auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); isa_bus->add_child(std::move(pit)); auto & root_bus = get_root_bus(); diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp index 2ad4d21..ff4ad71 100644 --- a/arch/x86_64/src/bus/isa.cpp +++ b/arch/x86_64/src/bus/isa.cpp @@ -2,11 +2,13 @@ #include "kapi/devices.hpp" +#include + namespace arch::bus { - isa::isa() - : kapi::devices::bus(kapi::devices::allocate_major_number(), 0, "isa") + isa::isa(std::size_t major) + : kapi::devices::bus{major, 0, "isa"} {} } // namespace arch::bus \ No newline at end of file diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp index f2fb70e..970f538 100644 --- a/arch/x86_64/src/devices/legacy_pit.cpp +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -6,6 +6,7 @@ #include "arch/device_io/port_io.hpp" +#include #include namespace arch::devices @@ -19,8 +20,8 @@ namespace arch::devices using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; } // namespace - legacy_pit::legacy_pit(std::uint32_t frequency_in_hz) - : kapi::devices::device{kapi::devices::allocate_major_number(), 0, "legacy_pit"} + legacy_pit::legacy_pit(std::size_t major, std::uint32_t frequency_in_hz) + : kapi::devices::device{major, 0, "legacy_pit"} , m_irq_number{0} , m_frequency_in_hz{frequency_in_hz} {} diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 7d35778..031f2c9 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -19,7 +19,7 @@ namespace kapi::devices namespace { - auto constinit next_major_number = std::atomic_size_t{0}; + auto constinit next_major_number = std::atomic_size_t{1}; auto constinit root_bus = std::optional{}; auto constinit device_tree = kstd::flat_map, kstd::observer_ptr>{}; } // namespace diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp index d3ba23f..43a35bf 100644 --- a/kernel/src/devices/root_bus.cpp +++ b/kernel/src/devices/root_bus.cpp @@ -6,7 +6,7 @@ namespace kernel::devices { root_bus::root_bus() - : kapi::devices::bus{kapi::devices::allocate_major_number(), 0, "system"} + : kapi::devices::bus{0, 0, "system"} {} } // namespace kernel::devices \ No newline at end of file -- cgit v1.2.3 From c5afb5c1ce1c084c840dbb58d73af6fe2b235ec7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:55:47 +0200 Subject: x86_64: ensure PIT is not overwhelmed on config --- .clang-tidy | 2 +- arch/x86_64/include/arch/device_io/port_io.hpp | 5 +++++ arch/x86_64/src/devices/legacy_pit.cpp | 13 +++++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 8fa3943..61ae9c9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -60,7 +60,7 @@ CheckOptions: modernize-use-std-print.ReplacementPrintlnFunction: 'kstd::println' modernize-use-std-print.PrintHeader: 'kstd/print' modernize-use-trailing-return-type.TransformLambdas: none - readability-magic-numbers.IgnoredIntegerValues: '1;2;3;4;5;6;7;10' + readability-magic-numbers.IgnoredIntegerValues: '1;2;3;4;5;6;7;10;255' readability-magic-numbers.IgnorePowersOf2IntegerValues: true readability-magic-numbers.IgnoreBitFieldsWidths: true readability-magic-numbers.IgnoreTypeAliases: true diff --git a/arch/x86_64/include/arch/device_io/port_io.hpp b/arch/x86_64/include/arch/device_io/port_io.hpp index 70773dd..4c8d66a 100644 --- a/arch/x86_64/include/arch/device_io/port_io.hpp +++ b/arch/x86_64/include/arch/device_io/port_io.hpp @@ -102,6 +102,11 @@ namespace arch::io : std::string_view{"eax"}; }; + auto inline wait() -> void + { + port<0x80, std::uint8_t, port_write>::write(0); + } + } // namespace arch::io #endif \ No newline at end of file diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp index 970f538..a8df3c3 100644 --- a/arch/x86_64/src/devices/legacy_pit.cpp +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -18,6 +18,9 @@ namespace arch::devices using channel_0_port = io::port<0x40, std::uint8_t, io::port_write>; using channel_1_port = io::port<0x41, std::uint8_t, io::port_write>; using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; + + constexpr auto base_frequency = 1'193'182u; + constexpr auto square_wave_mode = 0x36; } // namespace legacy_pit::legacy_pit(std::size_t major, std::uint32_t frequency_in_hz) @@ -28,14 +31,16 @@ namespace arch::devices auto legacy_pit::init() -> bool { - constexpr auto base_frequency = 1'193'182u; auto divisor = static_cast(base_frequency / m_frequency_in_hz); kapi::interrupts::register_handler(m_irq_number, *this); - command_port::write(0x36); // NOLINT - channel_0_port::write(divisor & 0xff); // NOLINT - channel_0_port::write(divisor >> 8 & 0xff); // NOLINT + command_port::write(square_wave_mode); + io::wait(); + channel_0_port::write(divisor & 0xff); + io::wait(); + channel_0_port::write(divisor >> 8 & 0xff); + io::wait(); return true; } -- cgit v1.2.3