Skip to main content

hdmi_hal_async/scdc/
mod.rs

1/// Async raw register access to the SCDC register map over DDC/I²C.
2///
3/// This is the async counterpart to `hdmi_hal::scdc::ScdcTransport`. The trait
4/// contract is identical; only the method signatures gain `async`.
5///
6/// SCDC is a bidirectional channel used for FRL link training initiation, scrambling
7/// control, and CED (Character Error Detection) reporting. Any code that needs to touch
8/// the SCDC register map does so through this trait.
9///
10/// The trait operates at the raw register level: a one-byte address and a one-byte
11/// value. Typed register wrappers, named constants, and multi-register sequences belong
12/// in the protocol crate, not here.
13pub trait ScdcTransport {
14    /// Error type returned by transport operations.
15    type Error;
16
17    /// Read a single byte from the given SCDC register address.
18    async fn read(&self, reg: u8) -> Result<u8, Self::Error>;
19
20    /// Write a single byte to the given SCDC register address.
21    async fn write(&mut self, reg: u8, value: u8) -> Result<(), Self::Error>;
22
23    /// Read consecutive SCDC registers, starting at `reg`, into `buf`.
24    ///
25    /// The default implementation calls [`read`](Self::read) once per byte, stopping at
26    /// the first error. Transports that can read several bytes in one DDC/I²C
27    /// transaction should override it, so that related registers (such as the status
28    /// flags or the character error counters) are read together rather than one at a
29    /// time while the sink may be updating them. A read past register `0xFF` is not
30    /// meaningful; the default implementation wraps the address to `0x00`.
31    async fn read_block(&self, reg: u8, buf: &mut [u8]) -> Result<(), Self::Error> {
32        let mut addr = reg;
33        for byte in buf {
34            *byte = self.read(addr).await?;
35            addr = addr.wrapping_add(1);
36        }
37        Ok(())
38    }
39}
40
41/// [`ScdcTransport`] with `Send` futures, for code that spawns link training on a
42/// multi-threaded executor.
43///
44/// `ScdcTransport`'s `async fn`s promise nothing about their futures, so code generic over
45/// `T: ScdcTransport` cannot spawn a future that calls them, however `T` is bounded. This
46/// trait has the same methods and contract, each returning a future that is `Send`, and
47/// errors that are `Send` too: a spawned task returns them. Implementations still write
48/// `async fn`; the compiler checks that each future is `Send`.
49///
50/// The transport is `Sync` as well as `Send`: [`read`](Self::read) takes `&self`, and a
51/// future holding `&self` is only `Send` when `Self` is `Sync`. A transport that keeps
52/// interior state for reads uses a `Sync` type for it (an atomic or a lock rather than a
53/// `Cell`).
54///
55/// Every `SendScdcTransport` is an `ScdcTransport`, through a blanket implementation, so
56/// it works with everything written against `ScdcTransport` as well (a `read_block`
57/// override included); a type implements one of the two (implementing both conflicts with
58/// the blanket implementation). A `SendScdcTransport` type has both traits' methods, so
59/// with both traits in scope a method call on it is ambiguous: import only the trait the
60/// code is bounded by, or call `SendScdcTransport::read(…)`.
61pub trait SendScdcTransport: Send + Sync {
62    /// Error type returned by transport operations.
63    type Error: Send;
64
65    /// See [`ScdcTransport::read`].
66    fn read(&self, reg: u8) -> impl Future<Output = Result<u8, Self::Error>> + Send;
67
68    /// See [`ScdcTransport::write`].
69    fn write(&mut self, reg: u8, value: u8)
70    -> impl Future<Output = Result<(), Self::Error>> + Send;
71
72    /// See [`ScdcTransport::read_block`]; the default implementation is the same, one
73    /// [`read`](Self::read) per byte.
74    fn read_block(
75        &self,
76        reg: u8,
77        buf: &mut [u8],
78    ) -> impl Future<Output = Result<(), Self::Error>> + Send {
79        async move {
80            let mut addr = reg;
81            for byte in buf {
82                *byte = self.read(addr).await?;
83                addr = addr.wrapping_add(1);
84            }
85            Ok(())
86        }
87    }
88}
89
90impl<T: SendScdcTransport> ScdcTransport for T {
91    type Error = <T as SendScdcTransport>::Error;
92
93    fn read(&self, reg: u8) -> impl Future<Output = Result<u8, Self::Error>> {
94        <T as SendScdcTransport>::read(self, reg)
95    }
96
97    fn write(&mut self, reg: u8, value: u8) -> impl Future<Output = Result<(), Self::Error>> {
98        <T as SendScdcTransport>::write(self, reg, value)
99    }
100
101    fn read_block(&self, reg: u8, buf: &mut [u8]) -> impl Future<Output = Result<(), Self::Error>> {
102        <T as SendScdcTransport>::read_block(self, reg, buf)
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use core::cell::Cell;
110    use core::sync::atomic::{AtomicU32, Ordering};
111
112    struct MockTransport {
113        registers: [u8; 256],
114        reads: Cell<u32>,
115        fail_at: Option<u8>,
116    }
117
118    impl MockTransport {
119        fn new() -> Self {
120            Self {
121                registers: [0u8; 256],
122                reads: Cell::new(0),
123                fail_at: None,
124            }
125        }
126
127        /// A transport whose every register holds its own address.
128        fn filled() -> Self {
129            let mut t = Self::new();
130            for (i, r) in t.registers.iter_mut().enumerate() {
131                *r = i as u8;
132            }
133            t
134        }
135    }
136
137    impl ScdcTransport for MockTransport {
138        type Error = u8;
139
140        async fn read(&self, reg: u8) -> Result<u8, Self::Error> {
141            if self.fail_at == Some(reg) {
142                return Err(reg);
143            }
144            self.reads.set(self.reads.get() + 1);
145            Ok(self.registers[reg as usize])
146        }
147
148        async fn write(&mut self, reg: u8, value: u8) -> Result<(), Self::Error> {
149            self.registers[reg as usize] = value;
150            Ok(())
151        }
152    }
153
154    #[pollster::test]
155    async fn write_then_read_returns_written_value() {
156        let mut t = MockTransport::new();
157        t.write(0x20, 0xAB).await.unwrap();
158        assert_eq!(t.read(0x20).await.unwrap(), 0xAB);
159    }
160
161    #[pollster::test]
162    async fn unwritten_register_reads_zero() {
163        let t = MockTransport::new();
164        assert_eq!(t.read(0x31).await.unwrap(), 0x00);
165    }
166
167    #[pollster::test]
168    async fn write_is_register_specific() {
169        let mut t = MockTransport::new();
170        t.write(0x01, 0xFF).await.unwrap();
171        assert_eq!(t.read(0x00).await.unwrap(), 0x00);
172        assert_eq!(t.read(0x02).await.unwrap(), 0x00);
173    }
174
175    #[pollster::test]
176    async fn write_overwrites_previous_value() {
177        let mut t = MockTransport::new();
178        t.write(0x20, 0x01).await.unwrap();
179        t.write(0x20, 0x02).await.unwrap();
180        assert_eq!(t.read(0x20).await.unwrap(), 0x02);
181    }
182
183    #[pollster::test]
184    async fn read_block_reads_consecutive_registers() {
185        let t = MockTransport::filled();
186        let mut buf = [0u8; 3];
187        t.read_block(0x40, &mut buf).await.unwrap();
188        assert_eq!(buf, [0x40, 0x41, 0x42]);
189        assert_eq!(t.reads.get(), 3);
190    }
191
192    #[pollster::test]
193    async fn read_block_empty_buffer_reads_nothing() {
194        let t = MockTransport::filled();
195        t.read_block(0x40, &mut []).await.unwrap();
196        assert_eq!(t.reads.get(), 0);
197    }
198
199    #[pollster::test]
200    async fn read_block_stops_at_first_error() {
201        let mut t = MockTransport::filled();
202        t.fail_at = Some(0x41);
203        let mut buf = [0u8; 3];
204        assert_eq!(t.read_block(0x40, &mut buf).await, Err(0x41));
205        assert_eq!(buf, [0x40, 0x00, 0x00]);
206        assert_eq!(t.reads.get(), 1);
207    }
208
209    #[pollster::test]
210    async fn read_block_wraps_past_0xff() {
211        let t = MockTransport::filled();
212        let mut buf = [0u8; 2];
213        t.read_block(0xFF, &mut buf).await.unwrap();
214        assert_eq!(buf, [0xFF, 0x00]);
215    }
216
217    #[pollster::test]
218    async fn write_then_read_block() {
219        let mut t = MockTransport::new();
220        t.write(0x20, 0xAB).await.unwrap();
221        let mut buf = [0u8; 1];
222        t.read_block(0x20, &mut buf).await.unwrap();
223        assert_eq!(buf, [0xAB]);
224    }
225
226    /// Implements only the `Send` twin, with the default `read_block`. Counts reads in an
227    /// atomic, since the twin needs `Sync`.
228    struct SendMockTransport {
229        registers: [u8; 256],
230        reads: AtomicU32,
231        fail_at: Option<u8>,
232    }
233
234    impl SendMockTransport {
235        fn filled() -> Self {
236            let mut registers = [0u8; 256];
237            for (i, r) in registers.iter_mut().enumerate() {
238                *r = i as u8;
239            }
240            Self {
241                registers,
242                reads: AtomicU32::new(0),
243                fail_at: None,
244            }
245        }
246
247        fn reads(&self) -> u32 {
248            self.reads.load(Ordering::Relaxed)
249        }
250    }
251
252    impl SendScdcTransport for SendMockTransport {
253        type Error = u8;
254
255        async fn read(&self, reg: u8) -> Result<u8, Self::Error> {
256            if self.fail_at == Some(reg) {
257                return Err(reg);
258            }
259            self.reads.fetch_add(1, Ordering::Relaxed);
260            Ok(self.registers[reg as usize])
261        }
262
263        async fn write(&mut self, reg: u8, value: u8) -> Result<(), Self::Error> {
264            self.registers[reg as usize] = value;
265            Ok(())
266        }
267    }
268
269    /// Implements only the `Send` twin and overrides `read_block`, as a transport that
270    /// reads a block in one transaction does.
271    struct BlockReadingTransport;
272
273    impl SendScdcTransport for BlockReadingTransport {
274        type Error = ();
275
276        async fn read(&self, _reg: u8) -> Result<u8, Self::Error> {
277            Err(())
278        }
279
280        async fn write(&mut self, _reg: u8, _value: u8) -> Result<(), Self::Error> {
281            Err(())
282        }
283
284        async fn read_block(&self, reg: u8, buf: &mut [u8]) -> Result<(), Self::Error> {
285            buf.fill(reg);
286            Ok(())
287        }
288    }
289
290    /// Writes, then reads back one register and a block, through `ScdcTransport`, as code
291    /// written against the plain trait does.
292    async fn round_trip_plain<T: ScdcTransport>(
293        t: &mut T,
294        buf: &mut [u8],
295    ) -> (
296        Result<(), T::Error>,
297        Result<u8, T::Error>,
298        Result<(), T::Error>,
299    ) {
300        let written = t.write(0x20, 0xAB).await;
301        let read = t.read(0x20).await;
302        let block = t.read_block(0x20, buf).await;
303        (written, read, block)
304    }
305
306    /// The compiler proves this `Send` bound for every `T: SendScdcTransport`, not only
307    /// for the mock; with `ScdcTransport` in its place it would not compile.
308    fn spawnable<'a, T: SendScdcTransport>(
309        t: &'a T,
310        buf: &'a mut [u8],
311    ) -> impl Future<Output = Result<(), T::Error>> + Send + 'a {
312        SendScdcTransport::read_block(t, 0x40, buf)
313    }
314
315    fn assert_send<T: Send>(value: T) -> T {
316        value
317    }
318
319    #[pollster::test]
320    async fn send_twin_is_usable_as_scdc_transport() {
321        let mut t = SendMockTransport::filled();
322        let mut buf = [0u8; 2];
323        let (written, read, block) = round_trip_plain(&mut t, &mut buf).await;
324        assert_eq!((written, read, block), (Ok(()), Ok(0xAB), Ok(())));
325        assert_eq!(buf, [0xAB, 0x21]);
326        assert_eq!(t.reads(), 3);
327    }
328
329    #[pollster::test]
330    async fn send_twin_futures_are_send_in_generic_code() {
331        let t = SendMockTransport::filled();
332        let mut buf = [0u8; 3];
333        assert_send(spawnable(&t, &mut buf)).await.unwrap();
334        assert_eq!(buf, [0x40, 0x41, 0x42]);
335    }
336
337    #[pollster::test]
338    async fn send_twin_default_read_block_stops_at_first_error() {
339        let mut t = SendMockTransport::filled();
340        t.fail_at = Some(0x41);
341        let mut buf = [0u8; 3];
342        assert_eq!(
343            SendScdcTransport::read_block(&t, 0x40, &mut buf).await,
344            Err(0x41)
345        );
346        assert_eq!(buf, [0x40, 0x00, 0x00]);
347        assert_eq!(t.reads(), 1);
348    }
349
350    #[pollster::test]
351    async fn send_twin_default_read_block_wraps_past_0xff() {
352        let t = SendMockTransport::filled();
353        let mut buf = [0u8; 2];
354        SendScdcTransport::read_block(&t, 0xFF, &mut buf)
355            .await
356            .unwrap();
357        assert_eq!(buf, [0xFF, 0x00]);
358    }
359
360    #[pollster::test]
361    async fn send_twin_read_block_override_is_used_through_scdc_transport() {
362        let mut t = BlockReadingTransport;
363        let mut buf = [0u8; 2];
364        let (written, read, block) = round_trip_plain(&mut t, &mut buf).await;
365        assert_eq!((written, read, block), (Err(()), Err(()), Ok(())));
366        assert_eq!(buf, [0x20, 0x20]);
367    }
368}