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}