diff options
Diffstat (limited to 'kapi')
| -rw-r--r-- | kapi/kapi/devices/interface_registry.hpp | 145 |
1 files changed, 140 insertions, 5 deletions
diff --git a/kapi/kapi/devices/interface_registry.hpp b/kapi/kapi/devices/interface_registry.hpp index 25a8b90f..e0e70654 100644 --- a/kapi/kapi/devices/interface_registry.hpp +++ b/kapi/kapi/devices/interface_registry.hpp @@ -18,10 +18,42 @@ namespace kapi::devices { + //! @addtogroup kapi-devices-kernel-defined + //! @{ + + struct interface_registry_observer; + + //! The device capability interface registry. + //! + //! The purpose of the interface registry is to allow system components, for + //! example drivers, to plush interfaces/capabilities for devices. An example + //! for such a capability are block devices. A driver for a given type of block + //! devices may choose too publish a the "block" capability on a bound device. + //! Other subsystems may then query if a given device exposes the "block" + //! capability or get all devices implementing that capability. + //! + //! @note The device registry holds only non-owning references to the published + //! devices. If a device dies after publishing, without being unpublished, the + //! registry accessors will prevent a dead handle from being exported. + //! + //! @see kernel::drivers::storage::ram_disk + //! @see kernel::filesystem::device_inode + //! @see kernel::filesystem::device_number_registry struct interface_registry { + //! A registry entry. + //! + //! Registry entries are used to record published interfaces on devices. struct entry { + //! Create a new registry entry, associating a device with a name, + //! interface, and interface implementation. + //! + //! @param device The device to record the published interface for. + //! @param name The stable name to associate with this interface record. + //! @param interface The id of the implemented interface. + //! @param implementation A pointer to the actual implementation of the + //! interface for the given device. constexpr entry(kstd::shared_ptr<struct device> device, kstd::string name, interface_id interface, void * implementation) : m_device{device} @@ -30,36 +62,65 @@ namespace kapi::devices , m_implementation{implementation} {} + //! Get a handle to the device of this entry. + //! + //! @return A non-null pointer to the device if it is still alive, a null + //! pointer otherwise. [[nodiscard]] constexpr auto device() const noexcept -> kstd::shared_ptr<device> { return m_device.lock(); } + //! Get the name of this entry. + //! + //! @return The name associated with this entry. [[nodiscard]] constexpr auto name() const noexcept -> kstd::string const & { return m_name; } + //! Get the interface id associated with this entry. + //! + //! @return The interface id of this entry. [[nodiscard]] constexpr auto interface() const noexcept -> interface_id { return m_interface; } + //! Get the interface implementation associated with this entry. + //! + //! @return An untyped pointer to the interface implementation. [[nodiscard]] constexpr auto implementation() const noexcept -> void * { return m_implementation; } + //! Get the interface implementation associated with this entry. + //! + //! @return A typed pointer to the implementation if the interface type + //! matches, nullptr otherwise. template<typename Interface> [[nodiscard]] constexpr auto as() noexcept -> Interface * { - return static_cast<Interface *>(m_implementation); + if (m_interface == Interface::id) + { + return static_cast<Interface *>(m_implementation); + } + return nullptr; } + //! Get the interface implementation associated with this entry. + //! + //! @return A typed pointer to the implementation if the interface type + //! matches, nullptr otherwise. template<typename Interface> [[nodiscard]] constexpr auto as() const noexcept -> Interface const * { - return static_cast<Interface const *>(m_implementation); + if (m_interface == Interface::id) + { + return static_cast<Interface const *>(m_implementation); + } + return nullptr; } private: @@ -69,10 +130,19 @@ namespace kapi::devices void * m_implementation; }; + //! Construct an empty interface registry. interface_registry() = default; + //! Initialize the system global singleton. + //! + //! @warning This function will panic if the global singleton has already been + //! initialized. auto static init() -> void; + //! Get the system global singleton. + //! + //! @warning This function will panic if the global singleton has not been + //! initialized. auto static get() -> interface_registry &; //! Publish the fact that a given device implements a given interface. @@ -107,14 +177,20 @@ namespace kapi::devices //! //! @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_id interface) -> void; + auto withdraw(device const & device, interface_id interface) -> void; + + //! Withdraw every interface previously published for a given device. + //! + //! @param device The device to withdraw all interfaces for. + auto withdraw_all_for(device const & device) -> void; //! Get all devices implementing a given interface. //! //! @param interface The interface for which to get the implementing devices. [[nodiscard]] auto all(interface_id interface) const -> kstd::vector<entry>; - //! Attempt to resolve a given device, by name, to a given interface implementation + //! Attempt to resolve a given device, by name, to a given interface + //! implementation //! //! @param interface The interface to look for. //! @param name The stable name of the device to look for. @@ -126,7 +202,8 @@ namespace kapi::devices //! @param device The device to look for. [[nodiscard]] auto resolve(interface_id interface, device & device) -> void *; - //! Attempt to resolve a given device, by name, to a given interface implementation + //! Attempt to resolve a given device, by name, to a given interface + //! implementation //! //! @tparam Interface The interface to look for. //! @param name The stable name of the device to look for. @@ -146,6 +223,31 @@ namespace kapi::devices return static_cast<Interface *>(resolve(Interface::id, device)); } + //! Subscribe to interface publish/withdraw notifications. + //! + //! Observers subscribed through this function are held through non-owning + //! pointers. It is safe of an observer subscribed through this function to be + //! destroyed without being unsubscribed. + //! + //! @param observer The observer to subscribe. + auto subscribe(kstd::weak_ptr<interface_registry_observer> observer) -> void; + + //! Subscribe to interface publish/withdraw notifications. + //! + //! @warning The registry has no way of detecting liveness of observers + //! subscribed through this function. This function is intended to be used + //! solely with long-lived observers. If an observer subscribed through this + //! function is destroyed before being unregistered, the behavior of any + //! triggered notifications becomes undefined. + //! + //! @param observer The observer to subscribe. + auto subscribe(interface_registry_observer & observer) -> void; + + //! Cancel a subscription previously made for a given observer. + //! + //! @param observer The observer to unsubscribe. + auto unsubscribe(interface_registry_observer & observer) -> void; + private: //! Publish the fact that a given device implements a given interface. //! @@ -156,9 +258,42 @@ namespace kapi::devices auto do_publish(kstd::shared_ptr<device> device, kstd::string name, interface_id interface, void * implementation) -> kstd::result<void>; + auto do_notify_published(interface_id interface, entry const & published) -> void; + + auto do_notify_withdrawn(interface_id interface, device const & device) -> void; + kstd::vector<entry> m_entries; + kstd::vector<kstd::weak_ptr<interface_registry_observer>> m_observers; + kstd::vector<interface_registry_observer *> m_static_observers; }; + //! @} + + //! @addtogroup kapi-devices + //! @{ + + //! The interface for types interested in observing interface transaction on an interface registry. + struct interface_registry_observer + { + //! Enable correct destruction through base pointers. + virtual ~interface_registry_observer() = default; + + //! Called after an interface has been published for a device. + //! + //! @param interface The interface id of the published interface. + //! @param published The published entry. + virtual auto on_interface_published(interface_id interface, interface_registry::entry const & published) + -> void = 0; + + //! Called after an interface has been withdrawn. + //! + //! @param interface The interface that was withdrawn. + //! @param device The device for which the interface was withdrawn. + virtual auto on_interface_withdrawn(interface_id interface, device & device) -> void = 0; + }; + + //! @} + } // namespace kapi::devices #endif |
