Skip to content

[RFC] Relay service event handling#856

Draft
kurtjd wants to merge 1 commit into
OpenDevicePartnership:v0.2.0from
kurtjd:go-mux-yourself
Draft

[RFC] Relay service event handling#856
kurtjd wants to merge 1 commit into
OpenDevicePartnership:v0.2.0from
kurtjd:go-mux-yourself

Conversation

@kurtjd
Copy link
Copy Markdown
Member

@kurtjd kurtjd commented May 20, 2026

So relay services need to be able to listen for events from all relayable services to then decide which are worth notifying to the host.

This was challenging, especially since I wanted to make use of the generic Sender/Receiver traits introduced by Robert. Basically what I decided on was to introduce two new Receiver types: a MapReceiver and a MuxReceiver.

The MapReceiver can map events to another type, and the MuxReceiver can combine multiple receivers into a common event type. E.g. for services:

Thermal serivce -> ThermalEvent
Battery service -> BatteryEvent
etc

We need a common ServiceEvent type that might look like:

enum ServiceEvent {
Thermal(ThermalEvent)
Battery(BatteryEvent)
}

so a MapReceiver would map a Receiver to a Receiver like:
MapReciever::new(Receiver<ThermalEvent>, ServiceEvent::Thermal)

and a battery event like:
MapReciever::new(Receiver<BatteryEvent>, ServiceEvent::BatteryEvent)

and the MuxReceiver under the hood uses MapReceiver to mux together these events using with chains:

MuxReceiver::new().with(Receiver<ThermalEvent>, ServiceEvent::Thermal).with(Receiver<BatteryEvent>, ServiceEvent::Battery)

That internally selects over all its receivers and produces a flattened ServiceEvent type.

This seems like the best way to combine N generic Receivers into one.

However, still don't have a perfect solution for how to use this within relay services. I updated the mctp relay handler macro to generate a ServiceEvent enum based on the passed in service handlers, and it also generates a function that builds the MuxReceiver using with chains.

My original idea was this macro'd MuxReceiver for ServiceEvent can be passed to a relay service just like we pass a relay handler (so the relay service can wait for any event from a relayable serivce, then decide how to notify the host). But, the problem is how does the relay service know what the events are, since they are constructed via macro?

For example, the uart service can't do:

match self.relay_events.wait_next() {
    ServiceEvent(Thermal(e)) => ...
}

because it doesn't have knowledge of the constructed ServiceEvent type. So need to think this part through some more.

@kurtjd kurtjd self-assigned this May 20, 2026
@RobertZ2011
Copy link
Copy Markdown
Contributor

My original idea was this macro'd MuxReceiver for ServiceEvent can be passed to a relay service just like we pass a relay handler (so the relay service can wait for any event from a relayable serivce, then decide how to notify the host). But, the problem is how does the relay service know what the events are, since they are constructed via macro?

For example, the uart service can't do:

match self.relay_events.wait_next() {
    ServiceEvent(Thermal(e)) => ...
}

because it doesn't have knowledge of the constructed ServiceEvent type. So need to think this part through some more.

To make sure I understand, the UART service would be responsible for listening for events and sending them to a host over UART? And the question is to how to make the service generic over any possible event type? I don't think we can be generic in the sense that we don't have to provide service-specific code. Having a single Serialize trait wouldn't work because the actual bytes sent to the host is an implementation detail. But we could have separate espi::Serialize and uart::Serialize traits. Then we could also use enum_dispatch to create the top-level service event because we'd have a shared trait.

@kurtjd
Copy link
Copy Markdown
Member Author

kurtjd commented May 21, 2026

My original idea was this macro'd MuxReceiver for ServiceEvent can be passed to a relay service just like we pass a relay handler (so the relay service can wait for any event from a relayable serivce, then decide how to notify the host). But, the problem is how does the relay service know what the events are, since they are constructed via macro?
For example, the uart service can't do:

match self.relay_events.wait_next() {
    ServiceEvent(Thermal(e)) => ...
}

because it doesn't have knowledge of the constructed ServiceEvent type. So need to think this part through some more.

To make sure I understand, the UART service would be responsible for listening for events and sending them to a host over UART? And the question is to how to make the service generic over any possible event type? I don't think we can be generic in the sense that we don't have to provide service-specific code. Having a single Serialize trait wouldn't work because the actual bytes sent to the host is an implementation detail. But we could have separate espi::Serialize and uart::Serialize traits. Then we could also use enum_dispatch to create the top-level service event because we'd have a shared trait.

In a generic sense, any relay service is responsible for listening for events from all the relayable services and is responsible for deciding which events are considered "notifiable" and then how exactly to properly notify the host of that event.

Some relay services would just use a "doorbell" form of notification (so no payload, just for example asserting a gpio line that the host agrees on means "I need attention"). Some might decide to notify via payloads (for example, the mctp uart service could just send a payload directly via TX). The HID services also handle notifications in some specific way.

I'm just trying to figure out how to best pass the knowledge of this constructed ServiceEvent enum to relayable services. In my very first notification attempts I just assumed always "doorbell" style notifications so the relay handler macro would just map events to u8s and pass that u8 to the relay service, but this doesn't scale especially with the HID stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants