@@ -164,6 +164,9 @@ pub mod mctp {
164164 type ResultEnumType : for < ' buf > mctp_rs:: MctpMessageTrait < ' buf , Header = Self :: HeaderType >
165165 + RelayResponse < Self :: ServiceIdType , Self :: HeaderType > ;
166166
167+ /// Returns a reference to the notification `Listener`.
168+ fn notification_listener ( & self ) -> & crate :: relay:: notifications:: Listener < ' _ > ;
169+
167170 /// Process the provided request and yield a result.
168171 fn process_request < ' a > (
169172 & ' a self ,
@@ -449,18 +452,21 @@ pub mod mctp {
449452
450453
451454 pub struct $relay_type_name<' hw> {
455+ listener: $crate:: relay:: notifications:: Listener <' hw>,
452456 $(
453457 [ <$service_name: snake _handler>] : & ' hw $service_handler_type,
454458 ) +
455459 }
456460
457461 impl <' hw> $relay_type_name<' hw> {
458462 pub fn new(
463+ listener: $crate:: relay:: notifications:: Listener <' hw>,
459464 $(
460465 [ <$service_name: snake _handler>] : & ' hw $service_handler_type,
461466 ) +
462467 ) -> Self {
463468 Self {
469+ listener,
464470 $(
465471 [ <$service_name: snake _handler>] ,
466472 ) +
@@ -474,6 +480,10 @@ pub mod mctp {
474480 type RequestEnumType = HostRequest ;
475481 type ResultEnumType = HostResult ;
476482
483+ fn notification_listener( & self ) -> & $crate:: relay:: notifications:: Listener <' _> {
484+ & self . listener
485+ }
486+
477487 fn process_request<' a>(
478488 & ' a self ,
479489 message: HostRequest ,
@@ -501,3 +511,129 @@ pub mod mctp {
501511
502512 pub use impl_odp_mctp_relay_handler;
503513}
514+
515+ /// Relay service notification support.
516+ pub mod notifications {
517+ use crate :: GlobalRawMutex ;
518+ use core:: sync:: atomic:: { AtomicBool , Ordering } ;
519+ use embassy_sync:: channel;
520+
521+ /// Notification error.
522+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
523+ #[ cfg_attr( feature = "defmt" , derive( defmt:: Format ) ) ]
524+ pub enum Error {
525+ /// A notification is currently being processed.
526+ Busy ,
527+ /// A [`Listener`] has already been instantiated.
528+ ListenerInstantiated ,
529+ }
530+
531+ /// Notifier.
532+ ///
533+ /// Used by services to send notifications to the relay service.
534+ pub struct Notifier < ' ch > {
535+ id : u8 ,
536+ sender : channel:: Sender < ' ch , GlobalRawMutex , u8 , 1 > ,
537+ }
538+
539+ impl < ' ch > Notifier < ' ch > {
540+ /// Notify the relay service that the service holding this [`Notifier`] needs attention from the host.
541+ ///
542+ /// This will wait if a different notification is currently being processed.
543+ pub async fn notify ( & self ) {
544+ self . sender . send ( self . id ) . await
545+ }
546+
547+ /// Try to notify the relay service that the service holding this [`Notifier`] needs attention from the host.
548+ ///
549+ /// # Errors
550+ ///
551+ /// Returns [`Error::Busy`] if a different notification is currently being processed.
552+ pub fn try_notify ( & self ) -> Result < ( ) , Error > {
553+ self . sender . try_send ( self . id ) . map_err ( |_| Error :: Busy )
554+ }
555+ }
556+
557+ /// Listener.
558+ pub struct Listener < ' ch > {
559+ receiver : channel:: Receiver < ' ch , GlobalRawMutex , u8 , 1 > ,
560+ }
561+
562+ impl < ' ch > Listener < ' ch > {
563+ /// Wait for a notification from any service, then returns the id associated with that notification.
564+ pub async fn listen ( & self ) -> u8 {
565+ self . receiver . receive ( ) . await
566+ }
567+
568+ /// Try to listen for a notification from any service.
569+ ///
570+ /// Returns [`None`] if no notification is currently available.
571+ pub fn try_listen ( & self ) -> Option < u8 > {
572+ self . receiver . try_receive ( ) . ok ( )
573+ }
574+ }
575+
576+ /// Notification handler.
577+ pub struct NotificationHandler {
578+ // The channel size is fixed to 1 (and not exposed as a configurable generic) so that
579+ // it does not need to be exposed to consumers of `Notifier` and `Listener`.
580+ //
581+ // This is reasonable because notifications are expected to be handled quickly,
582+ // and it is unlikely that multiple services will need to notify the host simultaneously.
583+ //
584+ // In the event that they do, the channel still provides backpressure so no notifications
585+ // will be lost and the expected latency is minimal.
586+ channel : channel:: Channel < GlobalRawMutex , u8 , 1 > ,
587+ listener_instantiated : AtomicBool ,
588+ }
589+
590+ impl Default for NotificationHandler {
591+ fn default ( ) -> Self {
592+ Self :: new ( )
593+ }
594+ }
595+
596+ impl NotificationHandler {
597+ /// Create a new [`NotificationHandler`] instance.
598+ ///
599+ /// This handler allows as many [`Notifier`]s to be created as needed,
600+ /// but only one [`Listener`] may be created.
601+ pub fn new ( ) -> Self {
602+ Self {
603+ channel : channel:: Channel :: new ( ) ,
604+ listener_instantiated : AtomicBool :: new ( false ) ,
605+ }
606+ }
607+
608+ /// Create a new [`Notifier`] instance with given id.
609+ ///
610+ /// This [`Notifier`] is then typically passed to services upon instantiation.
611+ ///
612+ /// The meaning of the id is platform specific and is opaque to services,
613+ /// but expectation is that the relay service understands how to interpret the id.
614+ ///
615+ /// Thus, the caller should ensure the given id matches what the relay service expects.
616+ pub fn new_notifier ( & self , id : u8 ) -> Notifier < ' _ > {
617+ Notifier {
618+ id,
619+ sender : self . channel . sender ( ) ,
620+ }
621+ }
622+
623+ /// Create a new [`Listener`] instance.
624+ ///
625+ /// # Errors
626+ ///
627+ /// Only one [`Listener`] may exist.
628+ /// Attempting to create another will return [`Error::ListenerInstantiated`].
629+ pub fn new_listener ( & self ) -> Result < Listener < ' _ > , Error > {
630+ if !self . listener_instantiated . swap ( true , Ordering :: Relaxed ) {
631+ Ok ( Listener {
632+ receiver : self . channel . receiver ( ) ,
633+ } )
634+ } else {
635+ Err ( Error :: ListenerInstantiated )
636+ }
637+ }
638+ }
639+ }
0 commit comments