From a6166befa7421be233ece97e184139870bbc0af7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Jul 2026 14:16:33 +0200 Subject: kapi: introduce device interfaces --- kapi/kapi/devices.hpp | 40 ++++++++- kapi/kapi/devices/device.hpp | 23 +++++ kapi/kapi/devices/interface.hpp | 31 +++++++ kapi/kapi/devices/interface_registry.hpp | 150 +++++++++++++++++++++++++++++++ kapi/kapi/test_support/devices.hpp | 10 +++ 5 files changed, 250 insertions(+), 4 deletions(-) create mode 100644 kapi/kapi/devices/interface.hpp create mode 100644 kapi/kapi/devices/interface_registry.hpp create mode 100644 kapi/kapi/test_support/devices.hpp (limited to 'kapi') diff --git a/kapi/kapi/devices.hpp b/kapi/kapi/devices.hpp index b597aa8c..64820f7c 100644 --- a/kapi/kapi/devices.hpp +++ b/kapi/kapi/devices.hpp @@ -1,10 +1,18 @@ #ifndef TEACHOS_KAPI_DEVICES_HPP #define TEACHOS_KAPI_DEVICES_HPP -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#include +#include +#include + +#include namespace kapi::devices { @@ -22,6 +30,30 @@ namespace kapi::devices //! @return a reference to the root bus. auto get_root_bus() -> bus &; + //! Publish the fact that a given device implements a given interface. + //! + //! @tparam Interface The interface type implemented by the given device. + //! @param device The device to publish the interface for. + //! @param name A stable name for the device. + template + auto publish_interface(kstd::shared_ptr device, kstd::string name) -> kstd::result + { + return interface_registry::get().publish(device, std::move(name)); + } + + //! Publish the fact that a given device implements a given interface. + //! + //! @tparam Interface The interface type implemented by the given device. + //! @param device The device to publish the interface for. + //! @param name A stable name for the device. + //! @param implementation The implementation of the interface for the device. + template + auto publish_interface(kstd::shared_ptr device, kstd::string name, Interface * implementation) + -> kstd::result + { + return interface_registry::get().publish(device, std::move(name), implementation); + } + //! @} //! @addtogroup kapi-devices-platform-defined diff --git a/kapi/kapi/devices/device.hpp b/kapi/kapi/devices/device.hpp index 104d7b8e..e99dd5f8 100644 --- a/kapi/kapi/devices/device.hpp +++ b/kapi/kapi/devices/device.hpp @@ -3,6 +3,8 @@ // IWYU pragma: private, include +#include + #include #include @@ -38,6 +40,24 @@ namespace kapi::devices */ virtual auto init() -> bool = 0; + template + [[nodiscard]] constexpr auto as() -> InterfaceType * + { + return static_cast(query_interface(InterfaceType::id)); + } + + template + [[nodiscard]] constexpr auto as() const noexcept -> InterfaceType const * + { + return static_cast(const_cast(this)->query_interface(InterfaceType::id)); + } + + template + [[nodiscard]] constexpr auto is_a() const noexcept -> bool + { + return const_cast(this)->query_interface(InterfaceType::id) != nullptr; + } + /** * @brief Returns the major number of the device. * @return Device major number. @@ -62,6 +82,9 @@ namespace kapi::devices */ [[nodiscard]] virtual auto is_block_device() const -> bool; + protected: + auto virtual query_interface(interface interface) -> void *; + private: friend struct bus; diff --git a/kapi/kapi/devices/interface.hpp b/kapi/kapi/devices/interface.hpp new file mode 100644 index 00000000..f5198691 --- /dev/null +++ b/kapi/kapi/devices/interface.hpp @@ -0,0 +1,31 @@ +#ifndef TEACHOS_KAPI_DEVICES_INTERFACE_HPP +#define TEACHOS_KAPI_DEVICES_INTERFACE_HPP + +// IWYU pragma: private, include + +#include + +namespace kapi::devices +{ + + //! A tag to mark that a given devices provides a specific device capability interface. + struct interface + { + //! Construct a new interface with a given name. + constexpr explicit interface(std::string_view name) + : m_name{name} + {} + + //! Get the name of this interface. + [[nodiscard]] constexpr auto name() const noexcept -> std::string_view const &; + + //! Check if this interface is equal to another one. + constexpr auto operator==(interface const &) const noexcept -> bool = default; + + private: + std::string_view m_name; + }; + +} // namespace kapi::devices + +#endif diff --git a/kapi/kapi/devices/interface_registry.hpp b/kapi/kapi/devices/interface_registry.hpp new file mode 100644 index 00000000..9fad91e6 --- /dev/null +++ b/kapi/kapi/devices/interface_registry.hpp @@ -0,0 +1,150 @@ +#ifndef TEACHOS_KAPI_DEVICES_INTERFACE_REGISTRY_HPP +#define TEACHOS_KAPI_DEVICES_INTERFACE_REGISTRY_HPP + +// IWYU pragma: private, include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace kapi::devices +{ + + struct interface_registry + { + struct entry + { + constexpr entry(kstd::shared_ptr device, kstd::string name, interface interface, + void * implementation) + : m_device{device} + , m_name{name} + , m_interface{interface} + , m_implementation{implementation} + {} + + [[nodiscard]] constexpr auto device() const noexcept -> kstd::weak_ptr + { + return m_device; + } + + [[nodiscard]] constexpr auto name() const noexcept -> kstd::string const & + { + return m_name; + } + + [[nodiscard]] constexpr auto interface() const noexcept -> interface + { + return m_interface; + } + + [[nodiscard]] constexpr auto implementation() const noexcept -> void * + { + return m_implementation; + } + + template + [[nodiscard]] constexpr auto as() noexcept -> Interface * + { + return static_cast(m_implementation); + } + + template + [[nodiscard]] constexpr auto as() const noexcept -> Interface const * + { + return static_cast(m_implementation); + } + + private: + kstd::weak_ptr m_device; + kstd::string m_name; + struct interface m_interface; + void * m_implementation; + }; + + interface_registry() = default; + + auto static init() -> void; + + auto static get() -> interface_registry &; + + //! Publish the fact that a given device implements a given interface. + //! + //! @tparam Interface The interface type implemented by the given device. + //! @param device The device to publish the interface for. + //! @param name A stable name for the device. + template + auto publish(kstd::shared_ptr device, kstd::string name) -> kstd::result + { + if (!device) + { + return kstd::failure(make_error_code(kstd::errc::invalid_argument)); + } + + return do_publish(device, std::move(name), Interface::id, device->as()); + } + + //! Publish the fact that a given device implements a given interface. + //! + //! @tparam Interface The interface type implemented by the given device. + //! @param device The device to publish the interface for. + //! @param name A stable name for the device. + //! @param implementation The implementation of the interface for the device. + template + auto publish(kstd::shared_ptr device, kstd::string name, Interface * implementation) -> kstd::result + { + return do_publish(device, std::move(name), Interface::id, implementation); + } + + //! Withdraw a previously published inteface for a given device. + //! + //! @param device The device to withdraw the interface for. + //! @param interface The interface to withdraw for the given device. + auto unpublish(device const & device, interface interface) -> void; + + //! Get all devices implementing a given interface. + //! + //! @param interface The interface for which to get the implementing devices. + [[nodiscard]] auto all(interface interface) const -> kstd::vector; + + //! Find an entry for a given interface on a device with the given name. + //! + //! @param interface The interface to look for. + //! @param name The stable name of the device to look for. + //! @return An entry representing the published interface if it exists, an error otherwise. + [[nodiscard]] auto find(interface interface, std::string_view name) const -> kstd::result; + + //! Find an entry for a given interface on a device with the given name. + //! + //! @tparam Interface The interface type to look for. + //! @param name The stable name of the device to look for. + //! @return An entry representing the published interface if it exists, an error otherwise. + template + [[nodiscard]] auto find(std::string_view name) const -> kstd::result + { + return find(Interface::id, name); + } + + private: + //! Publish the fact that a given device implements a given interface. + //! + //! @param device The device to publish the interface for. + //! @param name A stable name for the device. + //! @param interface The capability interface to be published for the device. + //! @param implementation The implementation of the interface for the device. + auto do_publish(kstd::shared_ptr device, kstd::string name, interface interface, void * implementation) + -> kstd::result; + + kstd::vector m_entries; + }; + +} // namespace kapi::devices + +#endif diff --git a/kapi/kapi/test_support/devices.hpp b/kapi/kapi/test_support/devices.hpp new file mode 100644 index 00000000..ab0911fc --- /dev/null +++ b/kapi/kapi/test_support/devices.hpp @@ -0,0 +1,10 @@ +#ifndef KAPI_TEST_SUPPORT_DEVICES_HPP +#define KAPI_TEST_SUPPORT_DEVICES_HPP + +namespace kapi::test_support::devices +{ + auto deinit() -> void; + auto deinit_interface_registry() -> void; +} // namespace kapi::test_support::devices + +#endif -- cgit v1.2.3