Skip to main content

plumbob_async/
client.rs

1use crate::{
2    CedCounters, ClearableUpdateFlags, FrlConfig, LtpRequests, SourceTestConfig, UpdateFlags,
3};
4
5/// Async typed SCDC interface required by the FRL training state machine.
6///
7/// This is the async counterpart to [`plumbob::ScdcClient`]: the same methods with the
8/// same meaning, each an `async fn`. See that trait for what implementations must
9/// guarantee, including where the poll interval belongs (inside the polled methods; an
10/// async implementation can await a timer there) and how to stop training early ("Stopping
11/// early"): for a deadline or a cancel signal, return one error of your own from a polled
12/// read and let every later call through. Training then ends through LTS:L, and `train`
13/// returns [`TrainingError::Scdc`](crate::TrainingError::Scdc) with that error; unlike a
14/// dropped `train` future, this leaves both ends in TMDS (unless
15/// `exit_to_tmds_on_error` is off or the sink sets `FLT_no_timeout`). Never fail a read
16/// made after LTS:L has begun (after a `write_frl_config` with `NotSupported`), until a
17/// `write_frl_config` with an FRL rate starts training again. `culvert-async` implements
18/// it for its `Scdc<T>`.
19pub trait ScdcClient {
20    /// Error type returned by SCDC operations.
21    type Error;
22
23    /// Read `FLT_ready` (`Status_Flags_0` bit 6).
24    ///
25    /// Async counterpart to [`plumbob::ScdcClient::read_flt_ready`].
26    async fn read_flt_ready(&mut self) -> Result<bool, Self::Error>;
27
28    /// Read the `Update_0` flags the state machine uses.
29    ///
30    /// Async counterpart to [`plumbob::ScdcClient::read_update_flags`].
31    async fn read_update_flags(&mut self) -> Result<UpdateFlags, Self::Error>;
32
33    /// Clear the given `Update_0` flags (write 1 to clear).
34    ///
35    /// Async counterpart to [`plumbob::ScdcClient::clear_update_flags`].
36    async fn clear_update_flags(&mut self, flags: ClearableUpdateFlags) -> Result<(), Self::Error>;
37
38    /// Read the per-lane link training requests from `Status_Flags_1/2`.
39    ///
40    /// Async counterpart to [`plumbob::ScdcClient::read_ltp_requests`].
41    async fn read_ltp_requests(&mut self) -> Result<LtpRequests, Self::Error>;
42
43    /// Read `Source_Test_Configuration` (0x35).
44    ///
45    /// Async counterpart to [`plumbob::ScdcClient::read_source_test_config`].
46    async fn read_source_test_config(&mut self) -> Result<SourceTestConfig, Self::Error>;
47
48    /// Write `Config_0` with read requests disabled and `FLT_no_retrain` clear.
49    ///
50    /// Async counterpart to [`plumbob::ScdcClient::write_config_0_defaults`].
51    async fn write_config_0_defaults(&mut self) -> Result<(), Self::Error>;
52
53    /// Write `Config_1`: the FRL rate and FFE levels.
54    ///
55    /// Async counterpart to [`plumbob::ScdcClient::write_frl_config`].
56    async fn write_frl_config(&mut self, config: FrlConfig) -> Result<(), Self::Error>;
57
58    /// Read the per-lane character error counters, for diagnostics.
59    ///
60    /// Async counterpart to [`plumbob::ScdcClient::read_ced`].
61    async fn read_ced(&mut self) -> Result<CedCounters, Self::Error>;
62}
63
64/// [`ScdcClient`] with `Send` futures, for code that spawns link training on a
65/// multi-threaded executor.
66///
67/// `ScdcClient`'s `async fn`s promise nothing about their futures, so code generic over
68/// `C: ScdcClient` cannot spawn a future that calls them, however `C` is bounded. This
69/// trait has the same methods and contract, each returning a future that is `Send`, and
70/// errors that are `Send` too: a spawned task returns them. Implementations still write
71/// `async fn`; the compiler checks that each future is `Send`. With a `SendScdcClient` and
72/// an `hdmi_hal_async::phy::SendHdmiPhy`, the futures of `FrlTrainer`'s training methods
73/// and `exit_to_tmds` are `Send` in generic code too (`train_with_events` given a `Send`
74/// callback).
75///
76/// Bound generic code by `SendScdcClient` alone. Adding `+ ScdcClient`, which it implies
77/// anyway, makes the compiler use that bound instead of the blanket implementation, and
78/// the futures are no longer known to be `Send`; the same holds for `SendHdmiPhy` and
79/// `+ HdmiPhy`.
80///
81/// Every `SendScdcClient` is an `ScdcClient`, through a blanket implementation, so it
82/// works with everything written against `ScdcClient` as well; a type implements one of
83/// the two (implementing both conflicts with the blanket implementation). A
84/// `SendScdcClient` type has both traits' methods, so with both traits in scope a method
85/// call on it is ambiguous: import only the trait the code is bounded by, or call
86/// `SendScdcClient::read_flt_ready(…)`.
87pub trait SendScdcClient: Send {
88    /// Error type returned by SCDC operations.
89    type Error: Send;
90
91    /// See [`ScdcClient::read_flt_ready`].
92    fn read_flt_ready(&mut self) -> impl Future<Output = Result<bool, Self::Error>> + Send;
93
94    /// See [`ScdcClient::read_update_flags`].
95    fn read_update_flags(
96        &mut self,
97    ) -> impl Future<Output = Result<UpdateFlags, Self::Error>> + Send;
98
99    /// See [`ScdcClient::clear_update_flags`].
100    fn clear_update_flags(
101        &mut self,
102        flags: ClearableUpdateFlags,
103    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
104
105    /// See [`ScdcClient::read_ltp_requests`].
106    fn read_ltp_requests(
107        &mut self,
108    ) -> impl Future<Output = Result<LtpRequests, Self::Error>> + Send;
109
110    /// See [`ScdcClient::read_source_test_config`].
111    fn read_source_test_config(
112        &mut self,
113    ) -> impl Future<Output = Result<SourceTestConfig, Self::Error>> + Send;
114
115    /// See [`ScdcClient::write_config_0_defaults`].
116    fn write_config_0_defaults(&mut self) -> impl Future<Output = Result<(), Self::Error>> + Send;
117
118    /// See [`ScdcClient::write_frl_config`].
119    fn write_frl_config(
120        &mut self,
121        config: FrlConfig,
122    ) -> impl Future<Output = Result<(), Self::Error>> + Send;
123
124    /// See [`ScdcClient::read_ced`].
125    fn read_ced(&mut self) -> impl Future<Output = Result<CedCounters, Self::Error>> + Send;
126}
127
128impl<T: SendScdcClient> ScdcClient for T {
129    type Error = <T as SendScdcClient>::Error;
130
131    fn read_flt_ready(&mut self) -> impl Future<Output = Result<bool, Self::Error>> {
132        <T as SendScdcClient>::read_flt_ready(self)
133    }
134
135    fn read_update_flags(&mut self) -> impl Future<Output = Result<UpdateFlags, Self::Error>> {
136        <T as SendScdcClient>::read_update_flags(self)
137    }
138
139    fn clear_update_flags(
140        &mut self,
141        flags: ClearableUpdateFlags,
142    ) -> impl Future<Output = Result<(), Self::Error>> {
143        <T as SendScdcClient>::clear_update_flags(self, flags)
144    }
145
146    fn read_ltp_requests(&mut self) -> impl Future<Output = Result<LtpRequests, Self::Error>> {
147        <T as SendScdcClient>::read_ltp_requests(self)
148    }
149
150    fn read_source_test_config(
151        &mut self,
152    ) -> impl Future<Output = Result<SourceTestConfig, Self::Error>> {
153        <T as SendScdcClient>::read_source_test_config(self)
154    }
155
156    fn write_config_0_defaults(&mut self) -> impl Future<Output = Result<(), Self::Error>> {
157        <T as SendScdcClient>::write_config_0_defaults(self)
158    }
159
160    fn write_frl_config(
161        &mut self,
162        config: FrlConfig,
163    ) -> impl Future<Output = Result<(), Self::Error>> {
164        <T as SendScdcClient>::write_frl_config(self, config)
165    }
166
167    fn read_ced(&mut self) -> impl Future<Output = Result<CedCounters, Self::Error>> {
168        <T as SendScdcClient>::read_ced(self)
169    }
170}