wasefire_board_api/usb/
serial.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! USB serial interface.
16
17use usb_device::UsbError;
18use usb_device::class_prelude::UsbBus;
19use usbd_serial::SerialPort;
20use wasefire_logger as log;
21
22use crate::Error;
23
24/// USB serial event.
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26#[derive(Debug, PartialEq, Eq)]
27pub enum Event {
28    /// There might be data to read.
29    ///
30    /// This is only guaranteed to be triggered after a short read.
31    Read,
32
33    /// It might be possible to write data.
34    ///
35    /// This is only guaranteed to be triggered after a short write.
36    Write,
37}
38
39impl<B: crate::Api> From<Event> for crate::Event<B> {
40    fn from(event: Event) -> Self {
41        super::Event::Serial(event).into()
42    }
43}
44
45/// USB serial interface.
46pub trait Api: Send {
47    /// Reads from the USB serial into a buffer.
48    ///
49    /// Returns the number of bytes read. It could be zero if there's nothing to read.
50    fn read(output: &mut [u8]) -> Result<usize, Error>;
51
52    /// Writes from a buffer to the USB serial.
53    ///
54    /// Returns the number of bytes written. It could be zero if the other side is not ready.
55    fn write(input: &[u8]) -> Result<usize, Error>;
56
57    /// Flushes the USB serial.
58    fn flush() -> Result<(), Error>;
59
60    /// Enables a given event to be triggered.
61    fn enable(event: &Event) -> Result<(), Error>;
62
63    /// Disables a given event from being triggered.
64    fn disable(event: &Event) -> Result<(), Error>;
65}
66
67/// Helper trait for boards using the `usbd_serial` crate.
68pub trait HasSerial: Send {
69    /// USB bus type.
70    type UsbBus: UsbBus;
71
72    /// Provides scoped access to the serial type.
73    fn with_serial<R>(f: impl FnOnce(&mut Serial<Self::UsbBus>) -> R) -> R;
74}
75
76/// Wrapper type for boards using the `usbd_serial` crate.
77pub struct WithSerial<T: HasSerial> {
78    _never: !,
79    _has_serial: T,
80}
81
82/// Helper struct for boards using the `usbd_serial` crate.
83pub struct Serial<'a, T: UsbBus> {
84    port: SerialPort<'a, T>,
85    read_enabled: bool,
86    write_enabled: bool,
87}
88
89impl<'a, T: UsbBus> Serial<'a, T> {
90    /// Creates a new serial from a port.
91    pub fn new(port: SerialPort<'a, T>) -> Self {
92        Self { port, read_enabled: false, write_enabled: false }
93    }
94
95    /// Reads from the USB serial into a buffer.
96    pub fn read(&mut self, output: &mut [u8]) -> Result<usize, Error> {
97        match self.port.read(output) {
98            Ok(len) => {
99                log::trace!("{}{:?} = read({})", len, &output[.. len], output.len());
100                Ok(len)
101            }
102            Err(UsbError::WouldBlock) => Ok(0),
103            Err(e) => {
104                log::debug!("{} = read({})", log::Debug2Format(&e), output.len());
105                Err(Error::world(0))
106            }
107        }
108    }
109
110    /// Writes from a buffer to the USB serial.
111    pub fn write(&mut self, input: &[u8]) -> Result<usize, Error> {
112        if !self.port.dtr() {
113            // Data terminal is not ready.
114            return Ok(0);
115        }
116        match self.port.write(input) {
117            Ok(len) => {
118                log::trace!("{} = write({}{:?})", len, input.len(), input);
119                Ok(len)
120            }
121            Err(UsbError::WouldBlock) => Ok(0),
122            Err(e) => {
123                log::debug!("{} = write({}{:?})", log::Debug2Format(&e), input.len(), input);
124                Err(Error::world(0))
125            }
126        }
127    }
128
129    /// Flushes the USB serial.
130    pub fn flush(&mut self) -> Result<(), Error> {
131        loop {
132            match self.port.flush() {
133                Ok(()) => {
134                    log::trace!("flush()");
135                    break Ok(());
136                }
137                Err(UsbError::WouldBlock) => {
138                    log::debug!("flush() didn't flush all data, retrying");
139                    continue;
140                }
141                Err(e) => {
142                    log::debug!("{} = flush()", log::Debug2Format(&e));
143                    break Err(Error::world(0));
144                }
145            }
146        }
147    }
148
149    /// Enables a given event to be triggered.
150    pub fn enable(&mut self, event: &Event) -> Result<(), Error> {
151        self.set(event, true)
152    }
153
154    /// Disables a given event from being triggered.
155    pub fn disable(&mut self, event: &Event) -> Result<(), Error> {
156        self.set(event, false)
157    }
158
159    /// Accesses the port of a serial.
160    pub fn port(&mut self) -> &mut SerialPort<'a, T> {
161        &mut self.port
162    }
163
164    /// Pushes events based on whether the USB serial was polled.
165    pub fn tick(&mut self, polled: bool, mut push: impl FnMut(Event)) {
166        if self.read_enabled && polled {
167            push(Event::Read);
168        }
169        if self.write_enabled && self.port.dtr() {
170            push(Event::Write);
171        }
172    }
173
174    fn set(&mut self, event: &Event, enabled: bool) -> Result<(), Error> {
175        match event {
176            Event::Read => self.read_enabled = enabled,
177            Event::Write => self.write_enabled = enabled,
178        }
179        Ok(())
180    }
181}
182
183impl<T: HasSerial> Api for WithSerial<T> {
184    fn read(output: &mut [u8]) -> Result<usize, Error> {
185        T::with_serial(|x| x.read(output))
186    }
187
188    fn write(input: &[u8]) -> Result<usize, Error> {
189        T::with_serial(|x| x.write(input))
190    }
191
192    fn flush() -> Result<(), Error> {
193        T::with_serial(|x| x.flush())
194    }
195
196    fn enable(event: &Event) -> Result<(), Error> {
197        T::with_serial(|x| x.enable(event))
198    }
199
200    fn disable(event: &Event) -> Result<(), Error> {
201        T::with_serial(|x| x.disable(event))
202    }
203}