umsh_ulcp_runtime/
i2c.rs

1//! The bus half of `CMD_I2C_TRANSFER` and `CMD_I2C_SCAN`, over any
2//! `embedded-hal-async` bus.
3//!
4//! [`transfer_with`] and [`scan_with`] are the part that is the same on
5//! every board: turning the staged operation list into HAL operations
6//! that read straight into the result buffer, walking a scan range, and
7//! naming what went wrong in the protocol's terms. [`guarded_transfer`]
8//! and [`guarded_scan`] add what a board with a shared bus needs around
9//! them—a bounded wait for the bus mutex, a deadline on the transaction,
10//! and a [`Reservation`] through which the board's own multi-transaction
11//! procedures keep a host out of a peripheral mid-procedure. A board with
12//! an exclusively owned bus can call the bare functions instead.
13
14use core::sync::atomic::{AtomicU8, Ordering};
15
16use embassy_sync::blocking_mutex::raw::RawMutex;
17use embassy_sync::mutex::Mutex;
18use embassy_time::{Duration, with_timeout};
19use embedded_hal_async::i2c::{Error as _, ErrorKind, I2c, NoAcknowledgeSource, Operation};
20use umsh_ulcp::Status;
21use umsh_ulcp::i2c::{Op, ScanRequest, TransferRequest, TransferShape};
22use umsh_ulcp_device::{I2C_DATA_MAX, I2C_MAX_OPS};
23
24/// Perform `request` on `bus`, writing the concatenated read data into
25/// `out` and returning its length.
26///
27/// The operations are handed to the HAL as one transaction, so the wire
28/// semantics—repeated START on a change of direction, no restart between
29/// consecutive operations of the same direction, one STOP—are the HAL's
30/// `transaction` contract. Consecutive reads are coalesced before carving
31/// `out`. Write data is packed into scratch space so consecutive writes
32/// also reach the HAL as one operation, without an extra START/address.
33pub async fn transfer_with<I: I2c>(
34    bus: &mut I,
35    request: TransferRequest<'_>,
36    out: &mut [u8],
37) -> Result<usize, Status> {
38    let mut writes = [0u8; I2C_DATA_MAX];
39    let mut written = 0;
40    for op in request.ops() {
41        if let Op::Write(data) = op.map_err(|error| error.status())? {
42            let end = written + data.len();
43            writes
44                .get_mut(written..end)
45                .ok_or(Status::INVALID_ARGUMENT)?
46                .copy_from_slice(data);
47            written = end;
48        }
49    }
50    let mut write_rest = &writes[..written];
51    let mut operations: heapless::Vec<Operation<'_>, I2C_MAX_OPS> = heapless::Vec::new();
52    let mut rest = out;
53    let mut read_len = 0usize;
54    let mut ops = request.ops().peekable();
55    while let Some(op) = ops.next() {
56        let operation = match op.map_err(|error| error.status())? {
57            Op::Write(data) => {
58                let mut len = data.len();
59                while let Some(Ok(Op::Write(next))) = ops.peek() {
60                    len += next.len();
61                    ops.next();
62                }
63                let (head, tail) = write_rest.split_at(len);
64                write_rest = tail;
65                Operation::Write(head)
66            }
67            Op::Read(mut len) => {
68                while let Some(Ok(Op::Read(next))) = ops.peek() {
69                    len = len.checked_add(*next).ok_or(Status::NOMEM)?;
70                    ops.next();
71                }
72                if rest.len() < len {
73                    return Err(Status::NOMEM);
74                }
75                let (head, tail) = core::mem::take(&mut rest).split_at_mut(len);
76                rest = tail;
77                read_len += len;
78                Operation::Read(head)
79            }
80        };
81        operations
82            .push(operation)
83            .map_err(|_| Status::INVALID_ARGUMENT)?;
84    }
85    bus.transaction(request.addr, &mut operations)
86        .await
87        .map_err(|error| status_for(error.kind()))?;
88    Ok(read_len)
89}
90
91/// Probe every address in `request`'s range with a one-octet read,
92/// writing the addresses that acknowledged into `out` in ascending order
93/// and returning how many.
94///
95/// An address that does not acknowledge is absent; any other failure
96/// ends the scan, because a bus that has just reported an error is not
97/// one whose silence means anything.
98pub async fn scan_with<I: I2c>(
99    bus: &mut I,
100    request: ScanRequest,
101    out: &mut [u8],
102) -> Result<usize, Status> {
103    let mut found = 0usize;
104    for addr in request.first..=request.last {
105        let mut probe = [0u8; 1];
106        match bus.read(addr, &mut probe).await {
107            Ok(()) => {
108                let slot = out.get_mut(found).ok_or(Status::NOMEM)?;
109                *slot = addr;
110                found += 1;
111            }
112            Err(error) => match error.kind() {
113                ErrorKind::NoAcknowledge(_) => {}
114                other => return Err(status_for(other)),
115            },
116        }
117    }
118    Ok(found)
119}
120
121/// The status a HAL error kind answers with.
122///
123/// An unacknowledged address is nobody home; an unacknowledged data
124/// octet is somebody home who refused it; everything else is the bus
125/// itself misbehaving, which is what a host sees whether the cause was
126/// arbitration, an overrun, or the board's own deadline.
127pub const fn status_for(kind: ErrorKind) -> Status {
128    match kind {
129        ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address) => Status::NO_DEVICE,
130        ErrorKind::NoAcknowledge(_) => Status::NACK,
131        _ => Status::BUS_ERROR,
132    }
133}
134
135/// The peripheral one of the board's own multi-transaction procedures
136/// currently holds, if any.
137///
138/// The bus mutex serializes transactions, not procedures: a gauge unseal,
139/// read, and reseal are three transactions, and a host transfer to the
140/// gauge between them would land inside the procedure. A procedure
141/// publishes its peripheral's address here for its duration through
142/// [`hold`](Self::hold), and the guarded functions answer `STATUS_BUSY`
143/// to anything addressed to it, or to a scan whose range covers it. Give each
144/// independently running procedure its own reservation and pass their array
145/// to guarded access. A single reservation must never have overlapping holds.
146pub struct Reservation(AtomicU8);
147
148impl Reservation {
149    const NONE: u8 = 0xFF;
150
151    pub const fn new() -> Self {
152        Self(AtomicU8::new(Self::NONE))
153    }
154
155    /// Publish `addr` as held until the returned guard drops. Every exit
156    /// from the procedure, error paths included, releases it.
157    #[must_use = "the reservation lasts as long as the guard"]
158    pub fn hold(&self, addr: u8) -> Held<'_> {
159        self.0.store(addr, Ordering::Release);
160        Held(self)
161    }
162
163    /// Whether `addr` is held.
164    pub fn holds(&self, addr: u8) -> bool {
165        let held = self.0.load(Ordering::Acquire);
166        held != Self::NONE && held == addr
167    }
168
169    /// Whether a scan of `range` would probe the held address.
170    pub fn covers(&self, range: &ScanRequest) -> bool {
171        let held = self.0.load(Ordering::Acquire);
172        held != Self::NONE && range.covers(held)
173    }
174}
175
176impl Default for Reservation {
177    fn default() -> Self {
178        Self::new()
179    }
180}
181
182/// A [`Reservation`] in force; dropping it releases the peripheral.
183pub struct Held<'a>(&'a Reservation);
184
185impl Drop for Held<'_> {
186    fn drop(&mut self) {
187        self.0.0.store(Reservation::NONE, Ordering::Release);
188    }
189}
190
191/// Independent board procedures may reserve different devices concurrently.
192pub trait Reservations {
193    fn holds(&self, addr: u8) -> bool;
194    fn covers(&self, range: &ScanRequest) -> bool;
195}
196
197impl Reservations for Reservation {
198    fn holds(&self, addr: u8) -> bool {
199        Reservation::holds(self, addr)
200    }
201    fn covers(&self, range: &ScanRequest) -> bool {
202        Reservation::covers(self, range)
203    }
204}
205
206impl<const N: usize> Reservations for [&Reservation; N] {
207    fn holds(&self, addr: u8) -> bool {
208        self.iter().any(|r| r.holds(addr))
209    }
210    fn covers(&self, range: &ScanRequest) -> bool {
211        self.iter().any(|r| r.covers(range))
212    }
213}
214
215/// The floor under every transaction deadline: clock stretching and the
216/// controller's own bookkeeping cost more than the octets do on a short
217/// transfer.
218const DEADLINE_FLOOR: Duration = Duration::from_millis(100);
219
220/// How long a transaction moving `octets` on a bus clocked at `speed_khz`
221/// may take before it is declared stuck.
222///
223/// Ten times the nominal wire time on top of the floor: generous enough
224/// that a peripheral stretching the clock is not mistaken for a stuck
225/// bus, and still well under the host's response timeout.
226pub const fn deadline_for(octets: usize, speed_khz: u16) -> Duration {
227    // Nine clocks per octet (eight bits and an acknowledge), in
228    // microseconds at `speed_khz` kilohertz.
229    let speed_khz = if speed_khz == 0 { 1 } else { speed_khz as u64 };
230    let nominal_us = (octets as u64 * 9 * 1000) / speed_khz;
231    Duration::from_micros(DEADLINE_FLOOR.as_micros() + 10 * nominal_us)
232}
233
234/// The deadline for a transfer of `shape`: its data plus one address
235/// octet per operation.
236pub const fn transfer_deadline(shape: TransferShape, speed_khz: u16) -> Duration {
237    deadline_for(shape.data_len + shape.ops + 1, speed_khz)
238}
239
240/// The deadline for a whole scan: an address and a probe octet per
241/// address in the range.
242pub const fn scan_deadline(request: &ScanRequest, speed_khz: u16) -> Duration {
243    deadline_for(request.len() * 2, speed_khz)
244}
245
246/// Acquire `bus` within `lock_timeout`, refuse the transfer if
247/// `reserved` holds its address, and perform it within `deadline`.
248///
249/// The reservation is checked *after* the bus is acquired. Checked
250/// before, a transfer could pass while the peripheral is free, wait for
251/// the mutex while a procedure begins, and then run inside it.
252/// A wait for the bus that outlasts `lock_timeout` is `STATUS_BUSY`; a
253/// transaction that outlasts `deadline` is `STATUS_BUS_ERROR`, and
254/// dropping the HAL's future is how the transaction is abandoned—a HAL
255/// whose drop does not reset the controller is not one to use here.
256pub async fn guarded_transfer<M: RawMutex, I: I2c>(
257    bus: &Mutex<M, I>,
258    reserved: &impl Reservations,
259    lock_timeout: Duration,
260    deadline: Duration,
261    request: TransferRequest<'_>,
262    out: &mut [u8],
263) -> Result<usize, Status> {
264    let mut bus = with_timeout(lock_timeout, bus.lock())
265        .await
266        .map_err(|_| Status::BUSY)?;
267    if reserved.holds(request.addr) {
268        return Err(Status::BUSY);
269    }
270    with_timeout(deadline, transfer_with(&mut *bus, request, out))
271        .await
272        .map_err(|_| Status::BUS_ERROR)?
273}
274
275/// The scan counterpart of [`guarded_transfer`]: a range that covers a
276/// held address is refused whole rather than silently skipping it, so
277/// the host learns to try again instead of drawing a wrong inventory.
278pub async fn guarded_scan<M: RawMutex, I: I2c>(
279    bus: &Mutex<M, I>,
280    reserved: &impl Reservations,
281    lock_timeout: Duration,
282    deadline: Duration,
283    request: ScanRequest,
284    out: &mut [u8],
285) -> Result<usize, Status> {
286    let mut bus = with_timeout(lock_timeout, bus.lock())
287        .await
288        .map_err(|_| Status::BUSY)?;
289    if reserved.covers(&request) {
290        return Err(Status::BUSY);
291    }
292    with_timeout(deadline, scan_with(&mut *bus, request, out))
293        .await
294        .map_err(|_| Status::BUS_ERROR)?
295}
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300    use core::pin::pin;
301    use core::task::{Context, Poll, Waker};
302    use embassy_futures::block_on;
303    use embassy_sync::blocking_mutex::raw::NoopRawMutex;
304    use embedded_hal_async::i2c::ErrorType;
305    use std::vec::Vec;
306
307    const LOCK_TIMEOUT: Duration = Duration::from_millis(30);
308    const DEADLINE: Duration = Duration::from_millis(30);
309    /// Long enough that no test waits it out by accident.
310    const NEVER: Duration = Duration::from_secs(60);
311
312    /// One transaction the fake bus expects: the address, the operations
313    /// as (`is_read`, octets written or to be returned), and the outcome.
314    struct Expected {
315        addr: u8,
316        ops: Vec<(bool, Vec<u8>)>,
317        result: Result<(), ErrorKind>,
318    }
319
320    #[derive(Default)]
321    struct Bus {
322        expected: std::collections::VecDeque<Expected>,
323        transactions: usize,
324    }
325
326    impl ErrorType for Bus {
327        type Error = ErrorKind;
328    }
329
330    impl I2c for Bus {
331        async fn transaction(
332            &mut self,
333            address: u8,
334            operations: &mut [Operation<'_>],
335        ) -> Result<(), Self::Error> {
336            self.transactions += 1;
337            let expected = self.expected.pop_front().expect("unexpected transaction");
338            assert_eq!(address, expected.addr);
339            assert_eq!(operations.len(), expected.ops.len(), "operation count");
340            for (operation, (is_read, octets)) in operations.iter_mut().zip(&expected.ops) {
341                match operation {
342                    Operation::Write(data) => {
343                        assert!(!is_read, "expected a read");
344                        assert_eq!(*data, octets.as_slice());
345                    }
346                    Operation::Read(buffer) => {
347                        assert!(*is_read, "expected a write");
348                        assert_eq!(buffer.len(), octets.len());
349                        buffer.copy_from_slice(octets);
350                    }
351                }
352            }
353            expected.result
354        }
355    }
356
357    fn request(bus: u8, addr: u8, ops: &[Op<'_>]) -> Vec<u8> {
358        let mut buf = [0u8; 300];
359        let len = umsh_ulcp::i2c::encode_transfer(&mut buf, 0, bus, addr, ops).unwrap();
360        // Strip the frame header; the session hands the glue a parsed
361        // request.
362        buf[2..len].to_vec()
363    }
364
365    #[test]
366    fn reads_are_carved_out_of_the_result_in_order() {
367        let mut bus = Bus::default();
368        bus.expected.push_back(Expected {
369            addr: 0x55,
370            ops: vec![
371                (false, vec![0x08]),
372                (true, vec![0x34, 0x12]),
373                (false, vec![0x0A]),
374                (true, vec![0xEF, 0xBE, 0xAD]),
375            ],
376            result: Ok(()),
377        });
378        let payload = request(
379            0,
380            0x55,
381            &[
382                Op::Write(&[0x08]),
383                Op::Read(2),
384                Op::Write(&[0x0A]),
385                Op::Read(3),
386            ],
387        );
388        let request = TransferRequest::parse(&payload).unwrap();
389        let mut out = [0u8; 8];
390        let len = block_on(transfer_with(&mut bus, request, &mut out)).unwrap();
391        assert_eq!(&out[..len], &[0x34, 0x12, 0xEF, 0xBE, 0xAD]);
392        assert_eq!(bus.transactions, 1);
393    }
394
395    #[test]
396    fn consecutive_reads_and_writes_are_merged_without_crossing_direction_changes() {
397        let cases = [
398            (
399                vec![Op::Read(2), Op::Read(2)],
400                vec![(true, vec![1, 2, 3, 4])],
401                vec![1, 2, 3, 4],
402            ),
403            (
404                vec![Op::Write(&[8]), Op::Write(&[9, 10])],
405                vec![(false, vec![8, 9, 10])],
406                vec![],
407            ),
408            (
409                vec![
410                    Op::Read(1),
411                    Op::Read(2),
412                    Op::Write(&[8]),
413                    Op::Write(&[9, 10]),
414                    Op::Read(2),
415                    Op::Read(1),
416                ],
417                vec![
418                    (true, vec![1, 2, 3]),
419                    (false, vec![8, 9, 10]),
420                    (true, vec![4, 5, 6]),
421                ],
422                vec![1, 2, 3, 4, 5, 6],
423            ),
424        ];
425        for (ops, expected, read) in cases {
426            let mut bus = Bus::default();
427            bus.expected.push_back(Expected {
428                addr: 0x55,
429                ops: expected,
430                result: Ok(()),
431            });
432            let payload = request(0, 0x55, &ops);
433            let request = TransferRequest::parse(&payload).unwrap();
434            let mut out = [0xFF; 8];
435            let len = block_on(transfer_with(&mut bus, request, &mut out)).unwrap();
436            assert_eq!(&out[..len], read);
437            assert!(out[len..].iter().all(|byte| *byte == 0xFF));
438            assert_eq!(bus.transactions, 1);
439        }
440    }
441
442    #[test]
443    fn merged_writes_fill_the_entire_transfer_limit() {
444        let first = [0xA5; 127];
445        let second = [0x5A; 128];
446        let expected = [first.as_slice(), second.as_slice()].concat();
447        let mut bus = Bus::default();
448        bus.expected.push_back(Expected {
449            addr: 0x55,
450            ops: vec![(false, expected)],
451            result: Ok(()),
452        });
453        let payload = request(0, 0x55, &[Op::Write(&first), Op::Write(&second)]);
454        let request = TransferRequest::parse(&payload).unwrap();
455        assert_eq!(block_on(transfer_with(&mut bus, request, &mut [])), Ok(0));
456        assert_eq!(bus.transactions, 1);
457    }
458
459    #[test]
460    fn merged_reads_check_the_combined_output_capacity_before_the_bus() {
461        let mut bus = Bus::default();
462        let payload = request(0, 0x55, &[Op::Read(2), Op::Read(3)]);
463        let request = TransferRequest::parse(&payload).unwrap();
464        let mut out = [0u8; 4];
465        assert_eq!(
466            block_on(transfer_with(&mut bus, request, &mut out)),
467            Err(Status::NOMEM)
468        );
469        assert_eq!(bus.transactions, 0);
470    }
471
472    #[test]
473    fn a_write_only_transfer_reads_nothing() {
474        let mut bus = Bus::default();
475        bus.expected.push_back(Expected {
476            addr: 0x6B,
477            ops: vec![(false, vec![0x00, 0x14])],
478            result: Ok(()),
479        });
480        let payload = request(0, 0x6B, &[Op::Write(&[0x00, 0x14])]);
481        let request = TransferRequest::parse(&payload).unwrap();
482        let mut out = [0u8; 4];
483        assert_eq!(
484            block_on(transfer_with(&mut bus, request, &mut out)).unwrap(),
485            0
486        );
487    }
488
489    #[test]
490    fn bus_errors_become_the_statuses_the_table_names() {
491        let cases = [
492            (
493                ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address),
494                Status::NO_DEVICE,
495            ),
496            (
497                ErrorKind::NoAcknowledge(NoAcknowledgeSource::Data),
498                Status::NACK,
499            ),
500            (
501                ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown),
502                Status::NACK,
503            ),
504            (ErrorKind::Bus, Status::BUS_ERROR),
505            (ErrorKind::ArbitrationLoss, Status::BUS_ERROR),
506            (ErrorKind::Overrun, Status::BUS_ERROR),
507            (ErrorKind::Other, Status::BUS_ERROR),
508        ];
509        for (kind, status) in cases {
510            assert_eq!(status_for(kind), status, "{kind:?}");
511            let mut bus = Bus::default();
512            bus.expected.push_back(Expected {
513                addr: 0x50,
514                ops: vec![(true, vec![0])],
515                result: Err(kind),
516            });
517            let payload = request(0, 0x50, &[Op::Read(1)]);
518            let request = TransferRequest::parse(&payload).unwrap();
519            let mut out = [0u8; 4];
520            assert_eq!(
521                block_on(transfer_with(&mut bus, request, &mut out)),
522                Err(status)
523            );
524        }
525    }
526
527    #[test]
528    fn a_result_buffer_too_small_is_refused_before_the_bus() {
529        let mut bus = Bus::default();
530        let payload = request(0, 0x50, &[Op::Read(5)]);
531        let request = TransferRequest::parse(&payload).unwrap();
532        let mut out = [0u8; 4];
533        assert_eq!(
534            block_on(transfer_with(&mut bus, request, &mut out)),
535            Err(Status::NOMEM)
536        );
537        assert_eq!(bus.transactions, 0);
538    }
539
540    #[test]
541    fn a_scan_reports_who_acknowledged_and_skips_who_did_not() {
542        let mut bus = Bus::default();
543        for addr in 0x50..=0x53u8 {
544            bus.expected.push_back(Expected {
545                addr,
546                ops: vec![(true, vec![0])],
547                result: if addr % 2 == 0 {
548                    Ok(())
549                } else {
550                    Err(ErrorKind::NoAcknowledge(NoAcknowledgeSource::Address))
551                },
552            });
553        }
554        let mut out = [0u8; 8];
555        let request = ScanRequest {
556            bus: 0,
557            first: 0x50,
558            last: 0x53,
559        };
560        let len = block_on(scan_with(&mut bus, request, &mut out)).unwrap();
561        assert_eq!(&out[..len], &[0x50, 0x52]);
562        assert_eq!(bus.transactions, 4);
563    }
564
565    #[test]
566    fn a_scan_stops_at_the_first_bus_error() {
567        let mut bus = Bus::default();
568        bus.expected.push_back(Expected {
569            addr: 0x50,
570            ops: vec![(true, vec![0])],
571            result: Ok(()),
572        });
573        bus.expected.push_back(Expected {
574            addr: 0x51,
575            ops: vec![(true, vec![0])],
576            result: Err(ErrorKind::ArbitrationLoss),
577        });
578        let mut out = [0u8; 8];
579        let request = ScanRequest {
580            bus: 0,
581            first: 0x50,
582            last: 0x5F,
583        };
584        assert_eq!(
585            block_on(scan_with(&mut bus, request, &mut out)),
586            Err(Status::BUS_ERROR)
587        );
588        assert_eq!(bus.transactions, 2);
589    }
590    /// A bus whose every transaction hangs: what a stuck peripheral or a
591    /// held-low SCL looks like to the HAL.
592    struct Stuck;
593
594    impl ErrorType for Stuck {
595        type Error = ErrorKind;
596    }
597
598    impl I2c for Stuck {
599        async fn transaction(
600            &mut self,
601            _address: u8,
602            _operations: &mut [Operation<'_>],
603        ) -> Result<(), Self::Error> {
604            core::future::pending().await
605        }
606    }
607
608    fn poll_once<F: core::future::Future>(fut: core::pin::Pin<&mut F>) -> Poll<F::Output> {
609        fut.poll(&mut Context::from_waker(Waker::noop()))
610    }
611
612    fn successful_probe(addr: u8) -> Expected {
613        Expected {
614            addr,
615            ops: vec![(true, vec![0])],
616            result: Ok(()),
617        }
618    }
619
620    #[test]
621    fn independent_procedures_reserve_without_replacing_the_gauge() {
622        let bus: Mutex<NoopRawMutex, Bus> = Mutex::new(Bus::default());
623        let gauge = Reservation::new();
624        let codec = Reservation::new();
625        let haptic = Reservation::new();
626        let reservations = [&gauge, &codec, &haptic];
627        let _gauge = gauge.hold(0x55);
628        let codec_guard = codec.hold(0x18);
629        let _haptic = haptic.hold(0x5a);
630        for addr in [0x55, 0x18, 0x5a] {
631            let payload = request(0, addr, &[Op::Write(&[0])]);
632            let req = TransferRequest::parse(&payload).unwrap();
633            assert_eq!(
634                block_on(guarded_transfer(
635                    &bus,
636                    &reservations,
637                    NEVER,
638                    NEVER,
639                    req,
640                    &mut []
641                )),
642                Err(Status::BUSY)
643            );
644        }
645        drop(codec_guard);
646        assert!(!reservations.holds(0x18));
647        assert!(reservations.holds(0x55));
648        assert!(reservations.holds(0x5a));
649        assert!(!reservations.holds(0x34));
650    }
651
652    /// The race the reservation exists for, in the order that defeats a
653    /// check-before-lock implementation: the transfer is already parked
654    /// on the bus mutex when the procedure publishes its reservation.
655    #[test]
656    fn a_transfer_parked_on_the_bus_sees_a_reservation_published_meanwhile() {
657        let bus: Mutex<NoopRawMutex, Bus> = Mutex::new(Bus::default());
658        let reserved = Reservation::new();
659        let codec = Reservation::new();
660        let _codec = codec.hold(0x18);
661        let reservations = [&reserved, &codec];
662        let payload = request(0, 0x55, &[Op::Write(&[0x00]), Op::Read(2)]);
663        let transfer = TransferRequest::parse(&payload).unwrap();
664        let mut out = [0u8; 4];
665
666        // (1) Something else holds the bus, while only the unrelated codec is reserved.
667        let holder = block_on(bus.lock());
668        let held = {
669            // (2) The transfer starts and parks on the mutex.
670            let mut fut = pin!(guarded_transfer(
671                &bus,
672                &reservations,
673                NEVER,
674                NEVER,
675                transfer,
676                &mut out
677            ));
678            assert!(poll_once(fut.as_mut()).is_pending(), "waiting for the bus");
679            // (3) The procedure begins, then the bus frees up.
680            let held = reserved.hold(0x55);
681            drop(holder);
682            // (4) The transfer is refused and never touched the bus.
683            assert_eq!(block_on(fut.as_mut()), Err(Status::BUSY));
684            held
685        };
686        assert_eq!(block_on(bus.lock()).transactions, 0);
687        // (5) The procedure ends and the same transfer goes through.
688        drop(held);
689        block_on(bus.lock()).expected.push_back(Expected {
690            addr: 0x55,
691            ops: vec![(false, vec![0x00]), (true, vec![0xAA, 0xBB])],
692            result: Ok(()),
693        });
694        let len = block_on(guarded_transfer(
695            &bus,
696            &reservations,
697            NEVER,
698            NEVER,
699            transfer,
700            &mut out,
701        ))
702        .unwrap();
703        assert_eq!(&out[..len], &[0xAA, 0xBB]);
704    }
705
706    #[test]
707    fn a_scan_parked_on_the_bus_is_refused_whole_when_its_range_covers_the_reservation() {
708        let bus: Mutex<NoopRawMutex, Bus> = Mutex::new(Bus::default());
709        let reserved = Reservation::new();
710        let codec = Reservation::new();
711        let _codec = codec.hold(0x18);
712        let reservations = [&reserved, &codec];
713        let scan = ScanRequest {
714            bus: 0,
715            first: 0x50,
716            last: 0x57,
717        };
718        let mut out = [0u8; 8];
719
720        let holder = block_on(bus.lock());
721        let held = {
722            let mut fut = pin!(guarded_scan(
723                &bus,
724                &reservations,
725                NEVER,
726                NEVER,
727                scan,
728                &mut out
729            ));
730            assert!(poll_once(fut.as_mut()).is_pending());
731            let held = reserved.hold(0x55);
732            drop(holder);
733            assert_eq!(block_on(fut.as_mut()), Err(Status::BUSY));
734            held
735        };
736        assert_eq!(
737            block_on(bus.lock()).transactions,
738            0,
739            "no address was probed"
740        );
741
742        drop(held);
743        for addr in 0x50..=0x57u8 {
744            block_on(bus.lock())
745                .expected
746                .push_back(successful_probe(addr));
747        }
748        let len = block_on(guarded_scan(
749            &bus,
750            &reservations,
751            NEVER,
752            NEVER,
753            scan,
754            &mut out,
755        ))
756        .unwrap();
757        assert_eq!(len, 8);
758    }
759
760    #[test]
761    fn a_reservation_elsewhere_on_the_bus_does_not_get_in_the_way() {
762        let bus: Mutex<NoopRawMutex, Bus> = Mutex::new(Bus::default());
763        let reserved = Reservation::new();
764        let _held = reserved.hold(0x55);
765        block_on(bus.lock()).expected.push_back(Expected {
766            addr: 0x6B,
767            ops: vec![(false, vec![0x00, 0x14])],
768            result: Ok(()),
769        });
770        let payload = request(0, 0x6B, &[Op::Write(&[0x00, 0x14])]);
771        let transfer = TransferRequest::parse(&payload).unwrap();
772        let mut out = [0u8; 8];
773        assert_eq!(
774            block_on(guarded_transfer(
775                &bus, &reserved, NEVER, NEVER, transfer, &mut out
776            )),
777            Ok(0)
778        );
779        // A scan that stops short of the reserved address is fine too.
780        let scan = ScanRequest {
781            bus: 0,
782            first: 0x50,
783            last: 0x54,
784        };
785        for addr in 0x50..=0x54u8 {
786            block_on(bus.lock())
787                .expected
788                .push_back(successful_probe(addr));
789        }
790        assert_eq!(
791            block_on(guarded_scan(&bus, &reserved, NEVER, NEVER, scan, &mut out)),
792            Ok(5)
793        );
794    }
795
796    #[test]
797    fn the_guard_releases_the_reservation_on_every_exit() {
798        let reserved = Reservation::new();
799        {
800            let _held = reserved.hold(0x55);
801            assert!(reserved.holds(0x55));
802            assert!(!reserved.holds(0x54));
803        }
804        assert!(!reserved.holds(0x55));
805        // Nothing held: the sentinel is never mistaken for an address.
806        assert!(!reserved.holds(0xFF));
807        assert!(!reserved.covers(&ScanRequest {
808            bus: 0,
809            first: 0x00,
810            last: 0x7F,
811        }));
812    }
813
814    #[test]
815    fn a_bus_that_stays_busy_is_reported_as_busy_not_as_broken() {
816        let bus: Mutex<NoopRawMutex, Bus> = Mutex::new(Bus::default());
817        let reserved = Reservation::new();
818        let _holder = block_on(bus.lock());
819        let payload = request(0, 0x50, &[Op::Read(1)]);
820        let transfer = TransferRequest::parse(&payload).unwrap();
821        let mut out = [0u8; 4];
822        assert_eq!(
823            block_on(guarded_transfer(
824                &bus,
825                &reserved,
826                LOCK_TIMEOUT,
827                NEVER,
828                transfer,
829                &mut out
830            )),
831            Err(Status::BUSY)
832        );
833    }
834
835    #[test]
836    fn a_transaction_that_never_completes_is_a_bus_error_and_frees_the_bus() {
837        let bus: Mutex<NoopRawMutex, Stuck> = Mutex::new(Stuck);
838        let reserved = Reservation::new();
839        let payload = request(0, 0x50, &[Op::Read(1)]);
840        let transfer = TransferRequest::parse(&payload).unwrap();
841        let mut out = [0u8; 4];
842        assert_eq!(
843            block_on(guarded_transfer(
844                &bus, &reserved, NEVER, DEADLINE, transfer, &mut out
845            )),
846            Err(Status::BUS_ERROR)
847        );
848        let scan = ScanRequest::new(0);
849        assert_eq!(
850            block_on(guarded_scan(
851                &bus, &reserved, NEVER, DEADLINE, scan, &mut out
852            )),
853            Err(Status::BUS_ERROR)
854        );
855        // The abandoned transaction released the mutex with it.
856        assert!(bus.try_lock().is_ok());
857    }
858
859    #[test]
860    fn deadlines_scale_with_the_wire_time_above_a_floor() {
861        let short = transfer_deadline(
862            TransferShape {
863                ops: 2,
864                data_len: 3,
865                read_len: 2,
866            },
867            400,
868        );
869        assert!(short >= DEADLINE_FLOOR && short < Duration::from_millis(110));
870        let long = transfer_deadline(
871            TransferShape {
872                ops: 16,
873                data_len: 255,
874                read_len: 0,
875            },
876            100,
877        );
878        // 272 octets at 100 kHz is about 24 ms on the wire; ten times
879        // that plus the floor.
880        assert!(long > Duration::from_millis(300) && long < Duration::from_millis(400));
881        let scan = scan_deadline(&ScanRequest::new(0), 400);
882        assert!(scan > DEADLINE_FLOOR && scan < Duration::from_millis(200));
883        // A zero speed is nonsense, but must not divide by it.
884        let _ = deadline_for(10, 0);
885    }
886}