Skip to main content

pamoja_radios/sx1302/
usb.rs

1//! A concentrator on a USB card, driven through its bridge.
2//!
3//! [`bridge`] builds the messages; this sends them down a port and reads
4//! what comes back. The port is anything that reads and writes bytes, so a test hands over
5//! a script and a gateway hands over the serial device the card enumerates as.
6//!
7//! The concentrator driver wants an SPI device and a reset pin. Over USB both are the bridge,
8//! so [`BridgeSpi`] and [`BridgePin`] each hold a share of one [`Bridge`] and turn the
9//! driver's transfers and pin writes into messages. The driver does not change: every frame
10//! it builds goes out inside a message exactly as it would have gone out on the wire.
11//!
12//! One message carries one transfer, which costs a round trip per register. The bridge can
13//! carry many at once, and a driver that gathers its writes into a [`Bulk`] and sends them
14//! through [`Bridge::bulk`] saves those trips; the firmware load is where that matters.
15
16use std::io::{self, Read, Write};
17use std::sync::{Arc, Mutex, PoisonError};
18use std::time::Duration;
19
20use embedded_hal::digital::{self, OutputPin};
21use embedded_hal::spi::{self, Operation, SpiDevice};
22
23use super::bridge::{
24    self, BridgeError, Bulk, Header, Identity, Replies, Status, ACK, GPIO_PORT, HEADER_LEN,
25    MAX_MESSAGE, ORDER_ERROR, ORDER_PING, ORDER_RESET, ORDER_SPI, ORDER_STATUS, ORDER_WRITE_GPIO,
26    PIN_POWER, PIN_RADIO_RESET, PIN_RESET, TARGET_CONCENTRATOR, TARGET_LISTENER,
27};
28
29/// What went wrong talking to the bridge.
30#[derive(Debug)]
31pub enum UsbError {
32    /// The port failed.
33    Io(io::Error),
34    /// The bridge answered something it should not have.
35    Bridge(BridgeError),
36    /// The bridge answered with more than there was room for.
37    TooLong {
38        /// How many bytes it announced.
39        size: usize,
40        /// How many there was room for.
41        room: usize,
42    },
43    /// A frame the driver built is longer than one message carries.
44    FrameTooLong {
45        /// How long it is.
46        len: usize,
47    },
48    /// The bridge could not read the order at all.
49    Rejected,
50    /// Another holder of the bridge panicked while holding it.
51    Poisoned,
52}
53
54impl core::fmt::Display for UsbError {
55    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56        match self {
57            UsbError::Io(error) => write!(f, "the port failed: {error}"),
58            UsbError::Bridge(error) => error.fmt(f),
59            UsbError::TooLong { size, room } => {
60                write!(
61                    f,
62                    "the bridge answered {size} bytes and there was room for {room}"
63                )
64            }
65            UsbError::FrameTooLong { len } => {
66                write!(
67                    f,
68                    "a {len} byte frame does not fit one message to the bridge"
69                )
70            }
71            UsbError::Rejected => f.write_str("the bridge could not read the order"),
72            UsbError::Poisoned => f.write_str("the bridge was left locked by a panic"),
73        }
74    }
75}
76
77impl std::error::Error for UsbError {}
78
79impl From<io::Error> for UsbError {
80    fn from(error: io::Error) -> UsbError {
81        UsbError::Io(error)
82    }
83}
84
85impl From<BridgeError> for UsbError {
86    fn from(error: BridgeError) -> UsbError {
87        UsbError::Bridge(error)
88    }
89}
90
91impl<T> From<PoisonError<T>> for UsbError {
92    fn from(_: PoisonError<T>) -> UsbError {
93        UsbError::Poisoned
94    }
95}
96
97impl spi::Error for UsbError {
98    fn kind(&self) -> spi::ErrorKind {
99        spi::ErrorKind::Other
100    }
101}
102
103impl digital::Error for UsbError {
104    fn kind(&self) -> digital::ErrorKind {
105        digital::ErrorKind::Other
106    }
107}
108
109/// The bridge on a USB card, reached over a port.
110pub struct Bridge<P> {
111    port: P,
112    next_id: u8,
113}
114
115impl<P: Read + Write> Bridge<P> {
116    /// Wraps a port. Nothing is sent until the bridge is asked something.
117    ///
118    /// # Arguments
119    ///
120    /// * `port` - the serial device the card enumerates as, opened raw.
121    ///
122    /// # Returns
123    ///
124    /// The bridge.
125    pub fn new(port: P) -> Bridge<P> {
126        Bridge { port, next_id: 0 }
127    }
128
129    /// Asks the bridge who it is.
130    ///
131    /// # Returns
132    ///
133    /// Its unique identifier and firmware version.
134    ///
135    /// # Errors
136    ///
137    /// When the port fails or the bridge answers something else.
138    pub fn ping(&mut self) -> Result<Identity, UsbError> {
139        self.send(ORDER_PING, &[])?;
140        let mut answer = [0u8; bridge::PING_LEN];
141        let got = self.receive(ORDER_PING, &mut answer)?;
142        Ok(Identity::parse(&answer[..got])?)
143    }
144
145    /// Asks the bridge for its clock and temperature.
146    ///
147    /// # Returns
148    ///
149    /// The status.
150    ///
151    /// # Errors
152    ///
153    /// When the port fails or the bridge answers something else.
154    pub fn status(&mut self) -> Result<Status, UsbError> {
155        self.send(ORDER_STATUS, &[])?;
156        let mut answer = [0u8; bridge::STATUS_LEN];
157        let got = self.receive(ORDER_STATUS, &mut answer)?;
158        Ok(Status::parse(&answer[..got])?)
159    }
160
161    /// Drives one of the card's pins.
162    ///
163    /// # Arguments
164    ///
165    /// * `pin` - which pin, such as [`PIN_RESET`].
166    /// * `high` - whether to drive it high.
167    ///
168    /// # Errors
169    ///
170    /// When the port fails or the bridge refuses.
171    pub fn write_gpio(&mut self, pin: u8, high: bool) -> Result<(), UsbError> {
172        self.send(ORDER_WRITE_GPIO, &bridge::write_gpio(GPIO_PORT, pin, high))?;
173        let mut answer = [0u8; 1];
174        let got = self.receive(ORDER_WRITE_GPIO, &mut answer)?;
175        Ok(bridge::took(&answer[..got])?)
176    }
177
178    /// Resets the card.
179    ///
180    /// # Errors
181    ///
182    /// When the port fails or the bridge refuses.
183    pub fn reset_card(&mut self) -> Result<(), UsbError> {
184        self.send(ORDER_RESET, &bridge::reset())?;
185        let mut answer = [0u8; 1];
186        let got = self.receive(ORDER_RESET, &mut answer)?;
187        Ok(bridge::took(&answer[..got])?)
188    }
189
190    /// Brings the card up the way the reference does before it is used.
191    ///
192    /// The supply is switched on, the concentrator's reset is pulsed, then the radio beside
193    /// it has its own reset pulsed, which is active low.
194    ///
195    /// # Errors
196    ///
197    /// When any of those pin writes fails.
198    pub fn power_up(&mut self) -> Result<(), UsbError> {
199        self.write_gpio(PIN_POWER, true)?;
200        self.write_gpio(PIN_RESET, true)?;
201        self.write_gpio(PIN_RESET, false)?;
202        self.write_gpio(PIN_RADIO_RESET, false)?;
203        self.write_gpio(PIN_RADIO_RESET, true)
204    }
205
206    /// Sends a bulk of SPI transfers and reads back what the chip clocked.
207    ///
208    /// # Arguments
209    ///
210    /// * `bulk` - the entries.
211    /// * `into` - where the answer goes, which needs as much room as the entries took plus
212    ///   nothing, since the answer echoes each entry at the same length.
213    ///
214    /// # Returns
215    ///
216    /// How many bytes of answer were read, which [`Replies`] then walks.
217    ///
218    /// # Errors
219    ///
220    /// When the port fails, the bridge answers another order, or the answer does not fit.
221    pub fn bulk(&mut self, bulk: &Bulk, into: &mut [u8]) -> Result<usize, UsbError> {
222        self.send(ORDER_SPI, bulk.as_bytes())?;
223        self.receive(ORDER_SPI, into)
224    }
225
226    // Writes a header and a payload.
227    fn send(&mut self, order: u8, payload: &[u8]) -> Result<(), UsbError> {
228        if payload.len() > MAX_MESSAGE {
229            return Err(UsbError::FrameTooLong { len: payload.len() });
230        }
231        let header = Header {
232            id: self.next_id,
233            size: payload.len() as u16,
234            order,
235        };
236        self.next_id = self.next_id.wrapping_add(1);
237        self.port.write_all(&header.to_bytes())?;
238        if !payload.is_empty() {
239            self.port.write_all(payload)?;
240        }
241        self.port.flush()?;
242        Ok(())
243    }
244
245    // Reads a header, checks it answers the order, then reads its payload.
246    fn receive(&mut self, order: u8, into: &mut [u8]) -> Result<usize, UsbError> {
247        let mut raw = [0u8; HEADER_LEN];
248        self.port.read_exact(&mut raw)?;
249        let header = Header::from_bytes(raw);
250        if header.order == ORDER_ERROR {
251            return Err(UsbError::Rejected);
252        }
253        if !header.answers(order) {
254            return Err(BridgeError::WrongAnswer {
255                asked: order,
256                answered: header.order.wrapping_sub(ACK),
257            }
258            .into());
259        }
260        let size = usize::from(header.size);
261        if size > into.len() {
262            return Err(UsbError::TooLong {
263                size,
264                room: into.len(),
265            });
266        }
267        self.port.read_exact(&mut into[..size])?;
268        Ok(size)
269    }
270}
271
272/// One bridge, held by everything that needs it.
273///
274/// The concentrator driver takes its SPI device and its reset pin as two values it owns, and
275/// over USB both are the same bridge. This is how they share it.
276pub struct Shared<P>(Arc<Mutex<Bridge<P>>>);
277
278impl<P> Clone for Shared<P> {
279    fn clone(&self) -> Shared<P> {
280        Shared(Arc::clone(&self.0))
281    }
282}
283
284impl<P: Read + Write> Shared<P> {
285    /// Shares a bridge.
286    ///
287    /// # Arguments
288    ///
289    /// * `bridge` - the bridge to share.
290    ///
291    /// # Returns
292    ///
293    /// A handle, which clones cheaply.
294    pub fn new(bridge: Bridge<P>) -> Shared<P> {
295        Shared(Arc::new(Mutex::new(bridge)))
296    }
297
298    /// The SPI device the concentrator answers on.
299    ///
300    /// # Returns
301    ///
302    /// A device the driver takes, carrying every frame to the concentrator and the front ends
303    /// behind it.
304    pub fn concentrator(&self) -> BridgeSpi<P> {
305        BridgeSpi {
306            bridge: self.clone(),
307            target: TARGET_CONCENTRATOR,
308        }
309    }
310
311    /// The SPI device the radio beside the concentrator answers on.
312    ///
313    /// # Returns
314    ///
315    /// A device carrying every frame to that radio, which is what a carrier check drives.
316    pub fn listener(&self) -> BridgeSpi<P> {
317        BridgeSpi {
318            bridge: self.clone(),
319            target: TARGET_LISTENER,
320        }
321    }
322
323    /// One of the card's pins, as a pin the driver drives.
324    ///
325    /// # Arguments
326    ///
327    /// * `pin` - which one, such as [`PIN_RESET`].
328    ///
329    /// # Returns
330    ///
331    /// The pin.
332    pub fn pin(&self, pin: u8) -> BridgePin<P> {
333        BridgePin {
334            bridge: self.clone(),
335            pin,
336        }
337    }
338
339    /// Does something with the bridge itself, holding it for the duration.
340    ///
341    /// # Arguments
342    ///
343    /// * `with` - what to do.
344    ///
345    /// # Returns
346    ///
347    /// Whatever it returned.
348    ///
349    /// # Errors
350    ///
351    /// [`UsbError::Poisoned`] when another holder panicked, and whatever `with` returned.
352    pub fn with<T>(
353        &self,
354        with: impl FnOnce(&mut Bridge<P>) -> Result<T, UsbError>,
355    ) -> Result<T, UsbError> {
356        let mut bridge = self.0.lock()?;
357        with(&mut bridge)
358    }
359}
360
361/// An SPI device that carries each transfer to the bridge.
362pub struct BridgeSpi<P> {
363    bridge: Shared<P>,
364    target: u8,
365}
366
367impl<P> spi::ErrorType for BridgeSpi<P> {
368    type Error = UsbError;
369}
370
371impl<P: Read + Write> SpiDevice<u8> for BridgeSpi<P> {
372    // One transaction is one time the chip select is held, so it is one raw frame: what every
373    // operation clocks out, in order, with zeros where the host only listens. The bridge
374    // clocks that frame and echoes what came back, and each operation that listens takes its
375    // own slice of the echo.
376    fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), UsbError> {
377        let mut frame = Vec::new();
378        for operation in operations.iter() {
379            match operation {
380                Operation::Write(bytes) | Operation::Transfer(_, bytes) => {
381                    frame.extend_from_slice(bytes);
382                }
383                Operation::Read(buffer) => frame.resize(frame.len() + buffer.len(), 0),
384                Operation::TransferInPlace(buffer) => frame.extend_from_slice(buffer),
385                Operation::DelayNs(_) => {}
386            }
387        }
388        if frame.len() + 5 > bridge::BULK_CHUNK {
389            return Err(UsbError::FrameTooLong { len: frame.len() });
390        }
391
392        let mut bulk = Bulk::new();
393        bulk.transfer(self.target, &frame)?;
394        let mut answer = vec![0u8; frame.len() + 5];
395        let got = self.bridge.with(|bridge| bridge.bulk(&bulk, &mut answer))?;
396
397        let echoed = Replies::new(&answer[..got])
398            .next()
399            .ok_or(BridgeError::Short { got, needs: 5 })??
400            .frame;
401        if echoed.len() < frame.len() {
402            return Err(BridgeError::Short {
403                got: echoed.len(),
404                needs: frame.len(),
405            }
406            .into());
407        }
408
409        let mut at = 0;
410        for operation in operations.iter_mut() {
411            match operation {
412                Operation::Write(bytes) => at += bytes.len(),
413                Operation::Transfer(read, write) => {
414                    let take = read.len().min(write.len());
415                    read[..take].copy_from_slice(&echoed[at..at + take]);
416                    at += write.len();
417                }
418                Operation::Read(buffer) => {
419                    buffer.copy_from_slice(&echoed[at..at + buffer.len()]);
420                    at += buffer.len();
421                }
422                Operation::TransferInPlace(buffer) => {
423                    buffer.copy_from_slice(&echoed[at..at + buffer.len()]);
424                    at += buffer.len();
425                }
426                Operation::DelayNs(ns) => std::thread::sleep(Duration::from_nanos(u64::from(*ns))),
427            }
428        }
429        Ok(())
430    }
431}
432
433/// One of the card's pins, driven through the bridge.
434pub struct BridgePin<P> {
435    bridge: Shared<P>,
436    pin: u8,
437}
438
439impl<P> digital::ErrorType for BridgePin<P> {
440    type Error = UsbError;
441}
442
443impl<P: Read + Write> OutputPin for BridgePin<P> {
444    fn set_low(&mut self) -> Result<(), UsbError> {
445        let pin = self.pin;
446        self.bridge.with(|bridge| bridge.write_gpio(pin, false))
447    }
448
449    fn set_high(&mut self) -> Result<(), UsbError> {
450        let pin = self.pin;
451        self.bridge.with(|bridge| bridge.write_gpio(pin, true))
452    }
453}
454
455#[cfg(test)]
456mod tests {
457    use super::*;
458    use crate::sx1302::spi as frame;
459    use std::collections::VecDeque;
460
461    /// A port that records what is written and answers from a script.
462    struct FakePort {
463        written: Vec<u8>,
464        answers: VecDeque<u8>,
465    }
466
467    impl FakePort {
468        fn answering(answers: &[&[u8]]) -> FakePort {
469            FakePort {
470                written: Vec::new(),
471                answers: answers.iter().flat_map(|a| a.iter().copied()).collect(),
472            }
473        }
474    }
475
476    impl Read for FakePort {
477        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
478            let mut n = 0;
479            while n < buf.len() {
480                match self.answers.pop_front() {
481                    Some(byte) => {
482                        buf[n] = byte;
483                        n += 1;
484                    }
485                    None => break,
486                }
487            }
488            Ok(n)
489        }
490    }
491
492    impl Write for FakePort {
493        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
494            self.written.extend_from_slice(buf);
495            Ok(buf.len())
496        }
497        fn flush(&mut self) -> io::Result<()> {
498            Ok(())
499        }
500    }
501
502    fn answer(order: u8, payload: &[u8]) -> Vec<u8> {
503        let mut out = Header {
504            id: 0,
505            size: payload.len() as u16,
506            order: order + ACK,
507        }
508        .to_bytes()
509        .to_vec();
510        out.extend_from_slice(payload);
511        out
512    }
513
514    #[test]
515    fn a_ping_goes_out_as_an_empty_message_and_the_identity_comes_back() {
516        let mut ping = [0u8; bridge::PING_LEN];
517        ping[12..].copy_from_slice(b"V01.00.00");
518        let mut bridge = Bridge::new(FakePort::answering(&[&answer(ORDER_PING, &ping)]));
519
520        let identity = bridge.ping().expect("the bridge answers");
521        assert!(identity.matches_firmware());
522        assert_eq!(bridge.port.written, [0, 0, 0, ORDER_PING], "no payload");
523    }
524
525    #[test]
526    fn a_register_read_through_the_driver_path_finds_the_chip_id() {
527        // The reference reads the version register; the bridge echoes the frame with the
528        // chip's answer in the last byte.
529        let out = frame::read(frame::TARGET_CONCENTRATOR, 0x5600);
530        let mut echoed = out.to_vec();
531        *echoed.last_mut().unwrap() = 0x60;
532        let mut reply = vec![0u8, 0x01, 0, 0, echoed.len() as u8];
533        reply.extend_from_slice(&echoed);
534
535        let shared = Shared::new(Bridge::new(FakePort::answering(&[&answer(
536            ORDER_SPI, &reply,
537        )])));
538        let mut spi = shared.concentrator();
539
540        let mut back = [0u8; frame::READ_LEN];
541        spi.transfer(&mut back, &out).expect("the transfer runs");
542        assert_eq!(frame::read_value(&back), Some(0x60));
543
544        // What went down the port: a header for one bulk order, then one entry carrying the
545        // frame the driver built, byte for byte.
546        let written = shared.with(|b| Ok(b.port.written.clone())).unwrap();
547        assert_eq!(&written[..4], &[0, 0, (5 + out.len()) as u8, ORDER_SPI]);
548        assert_eq!(
549            &written[4..9],
550            &[0, 0x01, TARGET_CONCENTRATOR, 0, out.len() as u8]
551        );
552        assert_eq!(&written[9..], &out);
553    }
554
555    #[test]
556    fn a_burst_read_is_one_frame_and_the_read_takes_its_own_slice_of_the_echo() {
557        let header = frame::burst_read_header(frame::TARGET_CONCENTRATOR, 0x1000);
558        let data = [0x11u8, 0x22, 0x33];
559        let mut echoed = header.to_vec();
560        echoed.extend_from_slice(&data);
561        let mut reply = vec![0u8, 0x01, 0, 0, echoed.len() as u8];
562        reply.extend_from_slice(&echoed);
563
564        let shared = Shared::new(Bridge::new(FakePort::answering(&[&answer(
565            ORDER_SPI, &reply,
566        )])));
567        let mut spi = shared.concentrator();
568
569        let mut buffer = [0u8; 3];
570        spi.transaction(&mut [Operation::Write(&header), Operation::Read(&mut buffer)])
571            .expect("the transfer runs");
572        assert_eq!(buffer, data);
573    }
574
575    #[test]
576    fn the_second_radio_goes_out_under_its_own_target() {
577        let out = [0xc0u8, 0x00];
578        let mut reply = vec![0u8, 0x01, 0, 0, 2];
579        reply.extend_from_slice(&[0xc0, 0x22]);
580        let shared = Shared::new(Bridge::new(FakePort::answering(&[&answer(
581            ORDER_SPI, &reply,
582        )])));
583        let mut spi = shared.listener();
584
585        let mut back = [0u8; 2];
586        spi.transfer(&mut back, &out).unwrap();
587        let written = shared.with(|b| Ok(b.port.written.clone())).unwrap();
588        assert_eq!(written[6], TARGET_LISTENER);
589    }
590
591    #[test]
592    fn a_pin_write_is_a_port_a_pin_and_a_level_and_a_refusal_is_an_error() {
593        let shared = Shared::new(Bridge::new(FakePort::answering(&[
594            &answer(ORDER_WRITE_GPIO, &[0]),
595            &answer(ORDER_WRITE_GPIO, &[1]),
596        ])));
597        let mut reset = shared.pin(PIN_RESET);
598
599        reset.set_high().expect("the bridge took it");
600        let written = shared.with(|b| Ok(b.port.written.clone())).unwrap();
601        assert_eq!(&written[4..], &[GPIO_PORT, PIN_RESET, 1]);
602
603        assert!(matches!(
604            reset.set_low(),
605            Err(UsbError::Bridge(BridgeError::Refused { status: 1 }))
606        ));
607    }
608
609    #[test]
610    fn powering_up_pulses_the_pins_in_the_order_the_reference_does() {
611        let ok = answer(ORDER_WRITE_GPIO, &[0]);
612        let mut bridge = Bridge::new(FakePort::answering(&[&ok, &ok, &ok, &ok, &ok]));
613        bridge.power_up().expect("every write takes");
614
615        let pins: Vec<[u8; 3]> = bridge
616            .port
617            .written
618            .chunks(7)
619            .map(|message| [message[4], message[5], message[6]])
620            .collect();
621        assert_eq!(
622            pins,
623            [
624                [0, PIN_POWER, 1],
625                [0, PIN_RESET, 1],
626                [0, PIN_RESET, 0],
627                [0, PIN_RADIO_RESET, 0],
628                [0, PIN_RADIO_RESET, 1],
629            ]
630        );
631    }
632
633    #[test]
634    fn an_answer_to_a_different_order_is_refused() {
635        let mut bridge = Bridge::new(FakePort::answering(&[&answer(ORDER_PING, &[0u8; 21])]));
636        assert!(matches!(
637            bridge.status(),
638            Err(UsbError::Bridge(BridgeError::WrongAnswer {
639                asked: ORDER_STATUS,
640                answered: ORDER_PING
641            }))
642        ));
643
644        let mut bridge = Bridge::new(FakePort::answering(&[&[0u8, 0, 0, ORDER_ERROR]]));
645        assert!(matches!(bridge.ping(), Err(UsbError::Rejected)));
646    }
647
648    #[test]
649    fn message_identifiers_count_up_so_a_trace_can_be_followed() {
650        let ok = answer(ORDER_WRITE_GPIO, &[0]);
651        let mut bridge = Bridge::new(FakePort::answering(&[&ok, &ok, &ok]));
652        for _ in 0..3 {
653            bridge.write_gpio(PIN_POWER, true).unwrap();
654        }
655        let ids: Vec<u8> = bridge.port.written.chunks(7).map(|m| m[0]).collect();
656        assert_eq!(ids, [0, 1, 2]);
657    }
658}