Skip to main content

usbd_storage/transport/
bbb.rs

1//! Bulk Only Transport (BBB/BOT)
2
3use crate::buffer::Buffer;
4use crate::fmt::{debug, trace, warning};
5use crate::transport::{CommandStatus, Transport, TransportError};
6use core::assert_matches;
7use core::borrow::BorrowMut;
8use core::cmp::min;
9use usb_device::UsbError;
10use usb_device::bus::{UsbBus, UsbBusAllocator};
11use usb_device::class::ControlIn;
12use usb_device::class_prelude::DescriptorWriter;
13use usb_device::control::{Recipient, RequestType};
14use usb_device::endpoint::{Endpoint, In, Out};
15
16/// Bulk Only Transport interface protocol
17pub(crate) const TRANSPORT_BBB: u8 = 0x50;
18
19const CLASS_SPECIFIC_BULK_ONLY_MASS_STORAGE_RESET: u8 = 0xFF;
20const CLASS_SPECIFIC_GET_MAX_LUN: u8 = 0xFE;
21
22const CBW_SIGNATURE_LE: [u8; 4] = 0x43425355u32.to_le_bytes();
23const CSW_SIGNATURE_LE: [u8; 4] = 0x53425355u32.to_le_bytes();
24
25const CBW_LEN: usize = 31;
26const CSW_LEN: usize = 13;
27
28const FILL_BYTE: u8 = 0xFF;
29
30struct InvalidCbwError; // Inner transport-specific error
31
32/// Bulk Only Transport error
33#[derive(Debug)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub enum BulkOnlyError {
36    /// Not enough space to fit additional data
37    IoBufferOverflow,
38    /// Invalid MAX_LUN value. Refer to USB BBB doc
39    InvalidMaxLun,
40    /// Transport is not in Data Transfer state
41    InvalidState,
42    /// Data Transfer expects a full packet to be sent next but not enough data available
43    FullPacketExpected,
44    /// The IO buffer cannot fit a CBW or a single full packet
45    BufferTooSmall,
46}
47
48/// Raw Command Block bytes
49pub struct CommandBlock<'a> {
50    /// CBWCB slice of CBW
51    pub bytes: &'a [u8],
52    pub lun: u8,
53}
54
55#[derive(Debug, Copy, Clone)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57enum State {
58    /// Device is expecting CBW transfer on OUT EP
59    CommandTransfer,
60    /// CBW is invalid. This state is terminal
61    CommandTransferInvalid,
62    /// CBW is valid. No data transfer expected by the host
63    DataTransferNoData,
64    /// Device actively writes CSW.
65    /// Invariant: buffer contains CSW bytes left to send
66    StatusTransfer,
67    /// Device actively sends data to host
68    DataTransferToHost,
69    /// Device has sent all the data and now waits for status from the user
70    DataTransferToHostStatusAwait,
71    /// User has set the status before all data has been sent
72    DataTransferToHostEnding,
73    /// Actively reads data from host
74    DataTransferFromHost,
75    /// User has set the status before all data has been read
76    DataTransferFromHostEnding,
77}
78
79#[repr(u8)]
80#[derive(Default, Debug, Copy, Clone)]
81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82enum DataDirection {
83    Out,
84    In,
85    #[default]
86    NotExpected,
87}
88
89struct StatusAndResidue {
90    status: CommandStatus,
91    data_residue: u32,
92}
93
94type BulkOnlyTransportResult<T> = Result<T, TransportError<BulkOnlyError>>;
95
96/// Bulk Only Transport
97///
98/// All data goes through an underlying IO buffer in both directions.
99/// During a Data Transfer, data could be read or written via [read_data], [write_data]
100/// and [try_write_data_all] methods.
101///
102/// [read_data]: crate::transport::bbb::BulkOnly::read_data
103/// [write_data]: crate::transport::bbb::BulkOnly::write_data
104/// [try_write_data_all]: crate::transport::bbb::BulkOnly::try_write_data_all
105pub struct BulkOnly<'alloc, Bus: UsbBus, Buf: BorrowMut<[u8]>> {
106    in_ep: Endpoint<'alloc, Bus, In>,
107    out_ep: Endpoint<'alloc, Bus, Out>,
108    buf: Buffer<Buf>,
109    state: State,
110    cbw: CommandBlockWrapper,
111    cs: Option<StatusAndResidue>,
112    max_lun: u8,
113    left_to_transfer: u32,
114}
115
116impl<'alloc, Bus, Buf> BulkOnly<'alloc, Bus, Buf>
117where
118    Bus: UsbBus,
119    Buf: BorrowMut<[u8]>,
120{
121    /// Creates Bulk Only Transport instance
122    ///
123    /// # Arguments
124    /// * `alloc` - [UsbBusAllocator]
125    /// * `packet_size` - Maximum USB packet size. Allowed values: 8,16,32,64
126    /// * `max_lun` - The max index of the Logical Unit
127    /// * `buf` - The underlying IO buffer. It is **required** to fit at least a `CBW` and/or a single
128    ///   packet. It is **recommended** that buffer fits at least one `LBA` size
129    ///
130    /// # Errors
131    /// * [InvalidMaxLun]
132    /// * [BufferTooSmall]
133    ///
134    /// # Panics
135    /// Panics if endpoint allocation fails.
136    ///
137    /// [InvalidMaxLun]: crate::transport::bbb::BulkOnlyError::InvalidMaxLun
138    /// [BufferTooSmall]: crate::transport::bbb::BulkOnlyError::BufferTooSmall
139    /// [UsbBusAllocator]: usb_device::bus::UsbBusAllocator
140    pub fn new(
141        alloc: &'alloc UsbBusAllocator<Bus>,
142        packet_size: u16,
143        max_lun: u8,
144        buf: Buf,
145    ) -> Result<BulkOnly<'alloc, Bus, Buf>, BulkOnlyError> {
146        if max_lun > 0x0F {
147            return Err(BulkOnlyError::InvalidMaxLun);
148        }
149
150        let buf_len = buf.borrow().len();
151        if buf_len < CBW_LEN || buf_len < packet_size as usize {
152            return Err(BulkOnlyError::BufferTooSmall);
153        }
154
155        Ok(BulkOnly {
156            in_ep: alloc.bulk(packet_size),
157            out_ep: alloc.bulk(packet_size),
158            buf: Buffer::new(buf),
159            state: State::CommandTransfer,
160            cbw: Default::default(),
161            cs: Default::default(),
162            max_lun,
163            left_to_transfer: 0,
164        })
165    }
166
167    /// Sets a `status` of the current command
168    ///
169    /// This method doesn't try to send a status immediately. However, all further
170    /// writes to the IO buffer won't succeed. The transport will try to send all
171    /// contents of the buffer before `CSW`.
172    ///
173    /// # Panics
174    /// Panics if called during any but a Data Transfer state. Usually, this means an error in
175    /// subclass implementation.
176    pub fn set_status(&mut self, status: CommandStatus, data_processed: u32) {
177        assert_matches!(
178            self.state,
179            State::DataTransferToHost
180                | State::DataTransferFromHost
181                | State::DataTransferNoData
182                | State::DataTransferToHostStatusAwait
183        );
184        let data_residue = self.cbw.data_transfer_len.saturating_sub(data_processed);
185        trace!("usb: bbb: Set status: {} residue: {}", status, data_residue);
186        self.cs = Some(StatusAndResidue {
187            status,
188            data_residue,
189        });
190    }
191
192    /// Returns a Command Block if present
193    pub fn get_command(&self) -> Option<CommandBlock<'_>> {
194        if matches!(self.state, State::CommandTransfer) {
195            return None;
196        }
197
198        Some(CommandBlock {
199            bytes: &self.cbw.block[..self.cbw.block_len],
200            lun: self.cbw.lun,
201        })
202    }
203
204    /// Reads data from the IO buffer returning the number of bytes actually read
205    ///
206    /// # Arguments
207    /// * `dst` - buffer, to read bytes into
208    ///
209    /// # Errors
210    /// Returns [BulkOnlyError::InvalidState] if called
211    /// during any but OUT Data Transfer state.
212    ///
213    /// [BulkOnlyError::InvalidState]: crate::transport::bbb::BulkOnlyError::InvalidState
214    pub fn read_data(&mut self, dst: &mut [u8]) -> BulkOnlyTransportResult<usize> {
215        if !matches!(self.state, State::DataTransferFromHost) {
216            return Err(TransportError::Error(BulkOnlyError::InvalidState));
217        }
218
219        Ok(self
220            .buf
221            .read(|buf| {
222                // fill 'dst' with however much is in 'buf'
223                let size = min(dst.len(), buf.len());
224                dst[..size].copy_from_slice(&buf[..size]);
225                Ok::<usize, core::convert::Infallible>(size)
226            })
227            .unwrap_or(0))
228    }
229
230    /// Writes data from the IO buffer returning the number of bytes actually written
231    ///
232    /// # Arguments
233    /// * `src` - bytes to write
234    ///
235    /// # Errors
236    /// Returns [BulkOnlyError::InvalidState] if called
237    /// during any but IN Data Transfer state.
238    ///
239    /// [BulkOnlyError::InvalidState]: crate::transport::bbb::BulkOnlyError::InvalidState
240    pub fn write_data(&mut self, src: &[u8]) -> BulkOnlyTransportResult<usize> {
241        if !matches!(self.state, State::DataTransferToHost) {
242            return Err(TransportError::Error(BulkOnlyError::InvalidState));
243        }
244
245        Ok(self
246            .buf
247            .write(&src[..min(src.len(), self.left_to_transfer as usize)]))
248    }
249
250    /// Tries to write all data from `src` into the IO buffer returning the number of bytes actually written
251    ///
252    /// # Errors
253    /// * [BulkOnlyError::IoBufferOverflow] - if not enough space is available
254    /// * [BulkOnlyError::InvalidState] - if called during any but IN Data Transfer state
255    ///
256    /// [BulkOnlyError::IoBufferOverflow]: crate::transport::bbb::BulkOnlyError::IoBufferOverflow
257    /// [BulkOnlyError::InvalidState]: crate::transport::bbb::BulkOnlyError::InvalidState
258    pub fn try_write_data_all(&mut self, src: &[u8]) -> BulkOnlyTransportResult<()> {
259        if !matches!(self.state, State::DataTransferToHost) {
260            return Err(TransportError::Error(BulkOnlyError::InvalidState));
261        }
262
263        self.buf
264            .write_all(
265                src.len(),
266                TransportError::Error(BulkOnlyError::IoBufferOverflow),
267                |dst| {
268                    dst[..src.len()].copy_from_slice(src);
269                    Ok(src.len())
270                },
271            )
272            .map(|bytes_written| debug!("usb: bbb: buf written: {}", bytes_written))
273    }
274
275    /// Whether a Command Status has been set
276    pub fn has_status(&self) -> bool {
277        self.cs.is_some()
278    }
279
280    fn enter_state_command_transfer(&mut self) {
281        self.cs = None;
282        self.left_to_transfer = 0;
283        self.in_ep.unstall();
284        self.out_ep.unstall();
285
286        self.state = State::CommandTransfer;
287        trace!("usb: bbb: enter {:?}", self.state);
288    }
289
290    #[inline]
291    fn enter_state_cbw_invalid(&mut self) {
292        self.state = State::CommandTransferInvalid;
293        trace!("usb: bbb: enter {:?}", self.state);
294    }
295
296    #[inline]
297    fn enter_state_data_transfer_no_data(&mut self) {
298        self.state = State::DataTransferNoData;
299        trace!("usb: bbb: enter {:?}", self.state);
300    }
301
302    #[inline]
303    fn enter_state_data_transfer_to_host(&mut self) {
304        self.state = State::DataTransferToHost;
305        trace!("usb: bbb: enter {:?}", self.state);
306    }
307
308    #[inline]
309    fn enter_state_data_transfer_to_host_status_await(&mut self) {
310        self.state = State::DataTransferToHostStatusAwait;
311        trace!("usb: bbb: enter {:?}", self.state);
312    }
313
314    #[inline]
315    fn enter_state_data_transfer_to_host_ending(&mut self) {
316        self.state = State::DataTransferToHostEnding;
317        trace!("usb: bbb: enter {:?}", self.state);
318    }
319
320    #[inline]
321    fn enter_state_data_transfer_from_host_ending(&mut self) {
322        self.state = State::DataTransferFromHostEnding;
323        trace!("usb: bbb: enter {:?}", self.state);
324    }
325
326    #[inline]
327    fn enter_state_data_transfer_from_host(&mut self) {
328        self.state = State::DataTransferFromHost;
329        trace!("usb: bbb: enter {:?}", self.state);
330    }
331
332    fn enter_state_status_transfer(&mut self) {
333        let Some(csw_bytes) = self.build_csw() else {
334            debug!("usb: bbb: UNEXPECTED no CSW");
335            panic!("usb: bbb: UNEXPECTED no CSW");
336        };
337
338        // this state's invariant
339        self.buf.clean();
340        self.buf.write(csw_bytes.as_slice());
341
342        self.state = State::StatusTransfer;
343        trace!("usb: bbb: enter {:?}", self.state);
344    }
345
346    /// The device expects to read CBW bytes
347    fn handle_read_cbw(&mut self) -> BulkOnlyTransportResult<()> {
348        assert_matches!(self.state, State::CommandTransfer);
349
350        self.read_packet()?;
351
352        if self.buf.available_read() >= CBW_LEN {
353            // try parse CBW if enough data available
354            match self.try_parse_cbw() {
355                Ok(cbw) => {
356                    trace!("usb: bbb: Recv CBW: {}", cbw);
357                    return self.start_data_transfer(cbw);
358                }
359                Err(_) => {
360                    self.enter_state_cbw_invalid();
361                    return self.handle_cbw_invalid();
362                }
363            }
364        }
365
366        Ok(())
367    }
368
369    /// The device has read an invalid CBW - wait for reset
370    fn handle_cbw_invalid(&mut self) -> BulkOnlyTransportResult<()> {
371        assert_matches!(self.state, State::CommandTransferInvalid);
372
373        // Spec. 6.6.1
374        self.in_ep.stall();
375        self.out_ep.stall();
376
377        Ok(())
378    }
379
380    fn handle_data_transfer_no_data(&mut self) -> BulkOnlyTransportResult<()> {
381        assert_matches!(self.state, State::DataTransferNoData);
382
383        self.enter_state_data_transfer_to_host_status_await();
384        self.handle_data_transfer_to_host_status_await()?;
385
386        Ok(())
387    }
388
389    /// Actively try to send more bytes if possible
390    fn handle_data_transfer_to_host(&mut self) -> BulkOnlyTransportResult<()> {
391        assert_matches!(self.state, State::DataTransferToHost);
392
393        // data is all sent. wait for the status
394        if self.left_to_transfer == 0 {
395            self.enter_state_data_transfer_to_host_status_await();
396            return self.handle_data_transfer_to_host_status_await();
397        }
398
399        // is this a short transfer?
400        if self.has_status() {
401            self.enter_state_data_transfer_to_host_ending();
402            return self.handle_data_transfer_to_host_ending();
403        }
404
405        // allow a short packet if needed
406        let bytes_sent = if self.left_to_transfer < self.packet_size() as u32 {
407            self.write_packet()?
408        } else {
409            self.try_write_full_packet()?
410        };
411
412        self.mark_as_transferred(bytes_sent as u32);
413
414        Ok(())
415    }
416
417    /// The device attempts to send the CSW
418    fn handle_status_transfer(&mut self) -> BulkOnlyTransportResult<()> {
419        assert_matches!(self.state, State::StatusTransfer);
420
421        // has CSW been sent off?
422        if self.buf.available_read() == 0 {
423            self.enter_state_command_transfer();
424            return self.handle_read_cbw();
425        }
426
427        self.write_packet()?;
428
429        Ok(())
430    }
431
432    /// Just wait for the user to set the status
433    fn handle_data_transfer_to_host_status_await(&mut self) -> BulkOnlyTransportResult<()> {
434        assert_matches!(self.state, State::DataTransferToHostStatusAwait);
435
436        if self.has_status() {
437            self.enter_state_status_transfer();
438            self.handle_status_transfer()?;
439        }
440
441        Ok(())
442    }
443
444    /// Send off what's left
445    fn handle_data_transfer_to_host_ending(&mut self) -> BulkOnlyTransportResult<()> {
446        assert_matches!(self.state, State::DataTransferToHostEnding);
447
448        let has_to_send = min(self.left_to_transfer as usize, self.packet_size());
449
450        // data has been completely sent off. send status now
451        if has_to_send == 0 {
452            self.enter_state_status_transfer();
453            return self.handle_status_transfer();
454        }
455
456        let available = self.buf.available_read();
457
458        if available >= has_to_send {
459            // enough data to send a full packet
460            let bytes_written = self.write_packet()?;
461            self.mark_as_transferred(bytes_written as u32);
462        } else {
463            // fill up to has_to_send
464            let to_fill = has_to_send.saturating_sub(available);
465            self.buf.fill_up_to(FILL_BYTE, to_fill);
466
467            let bytes_written = self.write_packet()?;
468            self.mark_as_transferred(bytes_written as u32);
469        }
470
471        Ok(())
472    }
473
474    /// Actively keep reading bytes if there is no status set
475    fn handle_data_transfer_from_host(&mut self) -> BulkOnlyTransportResult<()> {
476        assert_matches!(self.state, State::DataTransferFromHost);
477
478        // data is all read. wait for the status
479        if self.left_to_transfer == 0 {
480            self.enter_state_data_transfer_to_host_status_await();
481            return self.handle_data_transfer_to_host_status_await();
482        }
483
484        // status is set. keep reading anyway
485        if self.has_status() {
486            self.enter_state_data_transfer_from_host_ending();
487            return self.handle_data_transfer_from_host_ending();
488        }
489
490        let bytes_read = self.read_packet()?;
491        self.mark_as_transferred(bytes_read as u32);
492
493        Ok(())
494    }
495
496    fn handle_data_transfer_from_host_ending(&mut self) -> BulkOnlyTransportResult<()> {
497        assert_matches!(self.state, State::DataTransferFromHostEnding);
498
499        // keep reading up to the data_residue
500        if self.left_to_transfer > 0 {
501            self.buf.clean(); // no one is going to read this data anyway
502
503            let bytes_read = self.read_packet()?;
504            self.mark_as_transferred(bytes_read as u32);
505        } else {
506            // all data has been read
507            self.enter_state_status_transfer();
508            return self.handle_status_transfer();
509        }
510
511        Ok(())
512    }
513
514    fn build_csw(&mut self) -> Option<[u8; CSW_LEN]> {
515        self.cs.as_ref().map(|status| {
516            let mut csw = [0u8; CSW_LEN];
517            csw[..4].copy_from_slice(CSW_SIGNATURE_LE.as_slice());
518            csw[4..8].copy_from_slice(self.cbw.tag.to_le_bytes().as_slice());
519            csw[8..12].copy_from_slice(status.data_residue.to_le_bytes().as_slice());
520            csw[12..].copy_from_slice(&[status.status as u8]);
521            csw
522        })
523    }
524
525    /// The caller must ensure that there is enough data available
526    fn try_parse_cbw(&mut self) -> Result<CommandBlockWrapper, InvalidCbwError> {
527        assert!(self.buf.available_read() >= CBW_LEN);
528
529        // read CBW from buf
530        let mut raw_cbw = [0u8; CBW_LEN];
531        // The closure always returns Ok; unwrap_or(0) is unreachable but avoids unwrap().
532        self.buf
533            .read::<core::convert::Infallible>(|buf| {
534                raw_cbw.copy_from_slice(&buf[..CBW_LEN]); // buf.len() checked in the beginning
535                Ok(CBW_LEN)
536            })
537            .unwrap_or(0);
538
539        // check if CBW is valid. Spec. 6.2.1
540        if !raw_cbw.starts_with(&CBW_SIGNATURE_LE) {
541            return Err(InvalidCbwError);
542        }
543
544        CommandBlockWrapper::from_le_bytes(&raw_cbw[4..]) // parse CBW (skipping signature)
545    }
546
547    fn start_data_transfer(&mut self, cbw: CommandBlockWrapper) -> BulkOnlyTransportResult<()> {
548        assert_matches!(self.state, State::CommandTransfer);
549
550        self.cbw = cbw;
551        self.left_to_transfer = cbw.data_transfer_len;
552
553        match cbw.direction {
554            DataDirection::Out => {
555                self.enter_state_data_transfer_from_host();
556                self.handle_data_transfer_from_host()
557            }
558            DataDirection::In => {
559                self.enter_state_data_transfer_to_host();
560                self.handle_data_transfer_to_host()
561            }
562            DataDirection::NotExpected => {
563                self.enter_state_data_transfer_no_data();
564                self.handle_data_transfer_no_data()
565            }
566        }
567    }
568
569    #[inline]
570    fn packet_size(&self) -> usize {
571        self.in_ep.max_packet_size() as usize // same for both In and Out EPs
572    }
573
574    #[inline]
575    fn mark_as_transferred(&mut self, by: u32) {
576        self.left_to_transfer = self.left_to_transfer.saturating_sub(by);
577    }
578
579    // Tries to read a single packet from OUT EP
580    fn read_packet(&mut self) -> BulkOnlyTransportResult<usize> {
581        let bytes_read = self.buf.write_all(
582            self.packet_size(),
583            TransportError::Error(BulkOnlyError::IoBufferOverflow),
584            |buf| match self.out_ep.read(buf) {
585                Ok(bytes_read) => {
586                    trace!("usb: bbb: Read bytes: {}", bytes_read);
587                    Ok(bytes_read)
588                }
589                Err(UsbError::WouldBlock) => {
590                    trace!("usb: bbb: Read bytes: WOULD_BLOCK");
591                    Err(TransportError::Usb(UsbError::WouldBlock))
592                }
593                Err(err) => Err(TransportError::Usb(err)),
594            },
595        )?;
596
597        Ok(bytes_read)
598    }
599
600    /// Tries to write a single packet into IN EP returning the number of bytes actually written
601    ///
602    /// Might write a short packet if not enough data was in the buffer
603    fn write_packet(&mut self) -> BulkOnlyTransportResult<usize> {
604        let packet_size = self.packet_size();
605
606        let bytes_written = self.buf.read(|buf| {
607            if !buf.is_empty() {
608                match self.in_ep.write(&buf[..min(packet_size, buf.len())]) {
609                    Ok(bytes_written) => {
610                        trace!("usb: bbb: Wrote bytes: {}", bytes_written);
611                        Ok(bytes_written)
612                    }
613                    Err(UsbError::WouldBlock) => {
614                        trace!("usb: bbb: Wrote bytes: WOULD_BLOCK");
615                        Err(TransportError::Usb(UsbError::WouldBlock))
616                    }
617                    Err(err) => Err(TransportError::Usb(err)),
618                }
619            } else {
620                trace!("usb: bbb: Wrote bytes: 0, buf write available: 0",);
621                Ok(0) // not enough data in buf, though it's not an error
622            }
623        })?;
624
625        Ok(bytes_written)
626    }
627
628    /// Tries to write a FULL packet into IN EP returning the number of bytes actually written
629    fn try_write_full_packet(&mut self) -> BulkOnlyTransportResult<usize> {
630        if self.buf.available_read() < self.packet_size() {
631            return Err(TransportError::Error(BulkOnlyError::FullPacketExpected));
632        }
633
634        self.write_packet()
635    }
636}
637
638impl<Bus, Buf> Transport for BulkOnly<'_, Bus, Buf>
639where
640    Bus: UsbBus,
641    Buf: BorrowMut<[u8]>,
642{
643    const PROTO: u8 = TRANSPORT_BBB;
644
645    type Bus = Bus;
646    type Error = BulkOnlyError;
647
648    fn get_endpoint_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> {
649        writer.endpoint(&self.in_ep)?;
650        writer.endpoint(&self.out_ep)?;
651        Ok(())
652    }
653
654    fn reset(&mut self) {
655        trace!("usb: bbb: Recv reset");
656        self.enter_state_command_transfer();
657    }
658
659    fn control_in(&mut self, xfer: ControlIn<Self::Bus>) {
660        let req = xfer.request();
661
662        // not interested in this request
663        if !(req.request_type == RequestType::Class && req.recipient == Recipient::Interface) {
664            return;
665        }
666
667        trace!("usb: bbb: Recv ctrl_in: {}", req);
668
669        match req.request {
670            // Spec. section 3.1
671            CLASS_SPECIFIC_BULK_ONLY_MASS_STORAGE_RESET => {
672                self.enter_state_command_transfer();
673            }
674            // Spec. section 3.2
675            CLASS_SPECIFIC_GET_MAX_LUN => {
676                // always respond with LUN. A failure here means the host
677                // misbehaved or the bus glitched mid control-transfer; log and
678                // return rather than panicking the device.
679                if let Err(err) = xfer.accept_with(&[self.max_lun]) {
680                    warning!("usb: bbb: Get Max Lun accept failed: {}", err);
681                }
682            }
683            _ => {}
684        }
685    }
686
687    /// Something has happened either on the peripheral or the internal buffer
688    fn poll(&mut self) -> BulkOnlyTransportResult<()> {
689        trace!("usb: bbb: poll");
690
691        // Spec. 3.3
692        // "The host may request Data-In or CSW before sending the associated CBW."
693        // Ignore that
694
695        match self.state {
696            State::CommandTransfer => self.handle_read_cbw(),
697            State::CommandTransferInvalid => self.handle_cbw_invalid(),
698            State::DataTransferNoData => self.handle_data_transfer_no_data(),
699            State::DataTransferToHost => self.handle_data_transfer_to_host(),
700            State::DataTransferToHostStatusAwait => {
701                self.handle_data_transfer_to_host_status_await()
702            }
703            State::DataTransferToHostEnding => self.handle_data_transfer_to_host_ending(),
704            State::DataTransferFromHost => self.handle_data_transfer_from_host(),
705            State::DataTransferFromHostEnding => self.handle_data_transfer_from_host_ending(),
706            State::StatusTransfer => self.handle_status_transfer(),
707        }
708    }
709}
710
711#[derive(Default, Debug, Copy, Clone)]
712#[cfg_attr(feature = "defmt", derive(defmt::Format))]
713struct CommandBlockWrapper {
714    tag: u32,
715    data_transfer_len: u32,
716    direction: DataDirection,
717    lun: u8,
718    block_len: usize,
719    block: [u8; 16],
720}
721
722impl CommandBlockWrapper {
723    fn from_le_bytes(value: &[u8]) -> Result<Self, InvalidCbwError> {
724        const MIN_CB_LEN: u8 = 1;
725        const MAX_CB_LEN: u8 = 16;
726
727        let block_len = value[10];
728
729        if !(MIN_CB_LEN..=MAX_CB_LEN).contains(&block_len) {
730            return Err(InvalidCbwError);
731        }
732
733        // These slices are always exactly 4 / 4 / 16 bytes given the CBW layout;
734        // map the (unreachable) TryInto failure to InvalidCbwError rather than
735        // unwrapping.
736        let tag = u32::from_le_bytes(value[..4].try_into().map_err(|_| InvalidCbwError)?);
737        let data_transfer_len =
738            u32::from_le_bytes(value[4..8].try_into().map_err(|_| InvalidCbwError)?);
739        let direction = if data_transfer_len != 0 {
740            if (value[8] & (1 << 7)) > 0 {
741                DataDirection::In
742            } else {
743                DataDirection::Out
744            }
745        } else {
746            DataDirection::NotExpected
747        };
748        let block: [u8; 16] = value[11..].try_into().map_err(|_| InvalidCbwError)?;
749
750        Ok(CommandBlockWrapper {
751            tag,
752            data_transfer_len,
753            direction,
754            lun: value[9] & 0b00001111,
755            block_len: block_len as usize,
756            block,
757        })
758    }
759}
760
761#[cfg(test)]
762mod tests {
763    use super::*;
764    use std::collections::VecDeque;
765    use std::sync::{Arc, Mutex};
766    use usb_device::UsbDirection;
767    use usb_device::bus::{PollResult, UsbBusAllocator};
768    use usb_device::class_prelude::{EndpointAddress, EndpointType};
769    use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
770
771    // The Thirteen Cases (USB Mass Storage Bulk-Only Transport, section 6.7).
772
773    // 6.7.1 Hn - host expects no data
774
775    #[test]
776    fn host_expects_no_data_device_sends_no_data() {
777        // Case 1: Hn = Dn
778        for ps in PACKET_SIZES {
779            let (csw, _) = run(ps, 0, Host::ExpectsNoData, |b| {
780                b.set_status(CommandStatus::Passed, 0)
781            });
782            assert_eq!(csw.status, 0, "ps={ps}");
783            assert_eq!(csw.residue, 0, "ps={ps}");
784        }
785    }
786
787    #[test]
788    fn host_expects_no_data_device_wants_to_send() {
789        // Case 2: Hn < Di -> Phase Error
790        for ps in PACKET_SIZES {
791            let (csw, data) = run(ps, 0, Host::ExpectsNoData, |b| {
792                assert!(is_invalid_state(b.write_data(&[0xAA; 8])));
793                b.set_status(CommandStatus::PhaseError, 0);
794            });
795            assert_eq!(csw.status, 0x02, "ps={ps}");
796            assert!(data.is_empty(), "ps={ps}");
797        }
798    }
799
800    #[test]
801    fn host_expects_no_data_device_wants_to_receive() {
802        // Case 3: Hn < Do -> Phase Error
803        for ps in PACKET_SIZES {
804            let (csw, _) = run(ps, 0, Host::ExpectsNoData, |b| {
805                assert!(is_invalid_state(b.read_data(&mut [0u8; 8])));
806                b.set_status(CommandStatus::PhaseError, 0);
807            });
808            assert_eq!(csw.status, 0x02, "ps={ps}");
809        }
810    }
811
812    // 6.7.2 Hi - host expects to receive data from the device
813
814    #[test]
815    fn host_expects_data_in_device_sends_none() {
816        // Case 4: Hi > Dn
817        for ps in PACKET_SIZES {
818            let (csw, data) = run(ps, 32, Host::ExpectsDataIn, |b| {
819                b.set_status(CommandStatus::Passed, 0)
820            });
821            assert_eq!(csw.status, 0, "ps={ps}");
822            assert_eq!(csw.residue, 32, "ps={ps}");
823            assert_eq!(data.len(), 32, "ps={ps}"); // padded with fill bytes
824        }
825    }
826
827    #[test]
828    fn host_expects_more_data_in_than_device_sends() {
829        // Case 5: Hi > Di
830        for ps in PACKET_SIZES {
831            let (csw, data) = run(ps, 32, Host::ExpectsDataIn, |b| {
832                let n = b.write_data(&[0xAA; 16]).unwrap();
833                b.set_status(CommandStatus::Passed, n as u32);
834            });
835            assert_eq!(csw.status, 0, "ps={ps}");
836            assert_eq!(csw.residue, 16, "ps={ps}");
837            assert_eq!(data.len(), 32, "ps={ps}");
838        }
839    }
840
841    #[test]
842    fn host_expects_exactly_what_device_sends() {
843        // Case 6: Hi = Di
844        for ps in PACKET_SIZES {
845            let (csw, data) = run(ps, 32, Host::ExpectsDataIn, |b| {
846                let n = b.write_data(&[0xAA; 32]).unwrap();
847                b.set_status(CommandStatus::Passed, n as u32);
848            });
849            assert_eq!(csw.status, 0, "ps={ps}");
850            assert_eq!(csw.residue, 0, "ps={ps}");
851            assert_eq!(data, vec![0xAA; 32], "ps={ps}");
852        }
853    }
854
855    #[test]
856    fn host_expects_less_data_in_than_device_sends() {
857        // Case 7: Hi < Di -> Phase Error
858        for ps in PACKET_SIZES {
859            let (csw, data) = run(ps, 16, Host::ExpectsDataIn, |b| {
860                let n = b.write_data(&[0xAA; 32]).unwrap(); // capped to dCBWDataTransferLength
861                assert_eq!(n, 16); // device wanted to send more than the host allows
862                b.set_status(CommandStatus::PhaseError, 0);
863            });
864            assert_eq!(csw.status, 0x02, "ps={ps}");
865            assert_eq!(data.len(), 16, "ps={ps}");
866        }
867    }
868
869    #[test]
870    fn host_expects_data_in_device_wants_to_receive() {
871        // Case 8: Hi <> Do -> Phase Error
872        for ps in PACKET_SIZES {
873            let (csw, data) = run(ps, 32, Host::ExpectsDataIn, |b| {
874                assert!(is_invalid_state(b.read_data(&mut [0u8; 32])));
875                b.set_status(CommandStatus::PhaseError, 0);
876            });
877            assert_eq!(csw.status, 0x02, "ps={ps}");
878            assert_eq!(data.len(), 32, "ps={ps}");
879        }
880    }
881
882    // 6.7.3 Ho - host expects to send data to the device
883
884    #[test]
885    fn host_sends_data_device_receives_none() {
886        // Case 9: Ho > Dn
887        for ps in PACKET_SIZES {
888            let (csw, _) = run(ps, 32, Host::ExpectsDataOut, |b| {
889                b.set_status(CommandStatus::Passed, 0)
890            });
891            assert_eq!(csw.status, 0, "ps={ps}");
892            assert_eq!(csw.residue, 32, "ps={ps}");
893        }
894    }
895
896    #[test]
897    fn host_sends_data_device_wants_to_send() {
898        // Case 10: Ho <> Di -> Phase Error
899        for ps in PACKET_SIZES {
900            let (csw, _) = run(ps, 32, Host::ExpectsDataOut, |b| {
901                assert!(is_invalid_state(b.write_data(&[0xAA; 8])));
902                b.set_status(CommandStatus::PhaseError, 0);
903            });
904            assert_eq!(csw.status, 0x02, "ps={ps}");
905        }
906    }
907
908    #[test]
909    fn host_sends_more_data_out_than_device_receives() {
910        // Case 11: Ho > Do
911        for ps in PACKET_SIZES {
912            let (csw, _) = run(ps, 32, Host::ExpectsDataOut, |b| {
913                let n = b.read_data(&mut [0u8; 16]).unwrap();
914                b.set_status(CommandStatus::Passed, n as u32);
915            });
916            assert_eq!(csw.status, 0, "ps={ps}");
917            assert_eq!(csw.residue, 16, "ps={ps}");
918        }
919    }
920
921    #[test]
922    fn host_sends_exactly_what_device_receives() {
923        // Case 12: Ho = Do
924        for ps in PACKET_SIZES {
925            let (csw, _) = run(ps, 32, Host::ExpectsDataOut, |b| {
926                let n = b.read_data(&mut [0u8; 32]).unwrap();
927                b.set_status(CommandStatus::Passed, n as u32);
928            });
929            assert_eq!(csw.status, 0, "ps={ps}");
930            assert_eq!(csw.residue, 0, "ps={ps}");
931        }
932    }
933
934    #[test]
935    fn host_sends_less_data_out_than_device_receives() {
936        // Case 13: Ho < Do -> Phase Error
937        for ps in PACKET_SIZES {
938            let (csw, _) = run(ps, 16, Host::ExpectsDataOut, |b| {
939                let n = b.read_data(&mut [0u8; 32]).unwrap(); // only what the host sent
940                assert_eq!(n, 16); // device wanted to receive more than the host sends
941                b.set_status(CommandStatus::PhaseError, 0);
942            });
943            assert_eq!(csw.status, 0x02, "ps={ps}");
944        }
945    }
946
947    #[test]
948    fn should_read_data_into_small_buffer() {
949        const BUF_SIZE: usize = 512;
950        const N: usize = 123;
951
952        let (_shared, mut bbb) = new_bbb(64);
953        bbb.state = State::DataTransferFromHost;
954        bbb.buf.write([0xFFu8; BUF_SIZE].as_slice()); // fill the buffer
955
956        assert_eq!(N, bbb.read_data([0u8; N].as_mut_slice()).unwrap());
957    }
958
959    // ---- test harness ----
960
961    const PACKET_SIZES: [u16; 4] = [8, 16, 32, 64];
962
963    #[derive(Copy, Clone)]
964    enum Host {
965        ExpectsNoData,
966        ExpectsDataIn,  // device -> host
967        ExpectsDataOut, // host -> device
968    }
969
970    /// Queues shared between the test and the bus moved into the allocator.
971    #[derive(Clone, Default)]
972    struct Shared {
973        out: Arc<Mutex<VecDeque<Vec<u8>>>>, // host -> device packets
974        input: Arc<Mutex<Vec<Vec<u8>>>>,    // device -> host packets
975    }
976
977    struct MockBus {
978        shared: Shared,
979    }
980
981    impl UsbBus for MockBus {
982        fn alloc_ep(
983            &mut self,
984            ep_dir: UsbDirection,
985            _ep_addr: Option<EndpointAddress>,
986            _ep_type: EndpointType,
987            _max_packet_size: u16,
988            _interval: u8,
989        ) -> usb_device::Result<EndpointAddress> {
990            Ok(EndpointAddress::from_parts(1, ep_dir))
991        }
992
993        fn enable(&mut self) {}
994        fn reset(&self) {}
995        fn set_device_address(&self, _addr: u8) {}
996
997        fn write(&self, _ep_addr: EndpointAddress, buf: &[u8]) -> usb_device::Result<usize> {
998            self.shared.input.lock().unwrap().push(buf.to_vec());
999            Ok(buf.len())
1000        }
1001
1002        fn read(&self, _ep_addr: EndpointAddress, buf: &mut [u8]) -> usb_device::Result<usize> {
1003            match self.shared.out.lock().unwrap().pop_front() {
1004                Some(pkt) => {
1005                    buf[..pkt.len()].copy_from_slice(&pkt);
1006                    Ok(pkt.len())
1007                }
1008                None => Err(UsbError::WouldBlock),
1009            }
1010        }
1011
1012        fn set_stalled(&self, _ep_addr: EndpointAddress, _stalled: bool) {}
1013        fn is_stalled(&self, _ep_addr: EndpointAddress) -> bool {
1014            false
1015        }
1016        fn suspend(&self) {}
1017        fn resume(&self) {}
1018        fn poll(&self) -> PollResult {
1019            PollResult::None
1020        }
1021    }
1022
1023    type MockBbb = BulkOnly<'static, MockBus, Vec<u8>>;
1024
1025    fn new_bbb(packet_size: u16) -> (Shared, MockBbb) {
1026        let shared = Shared::default();
1027        let bus = MockBus {
1028            shared: shared.clone(),
1029        };
1030        let alloc = Box::leak(Box::new(UsbBusAllocator::new(bus)));
1031        let bbb = BulkOnly::new(alloc, packet_size, 0, vec![0u8; 512]).unwrap();
1032        // Endpoint read/write reach the bus through a pointer set by the allocator's
1033        // (crate-private) freeze(); building a device is the only public way to trigger it.
1034        core::mem::forget(UsbDeviceBuilder::new(alloc, UsbVidPid(0xabcd, 0xabcd)).build());
1035        (shared, bbb)
1036    }
1037
1038    /// Builds a CBW. `data_len` of 0 yields a no-data command.
1039    fn cbw(data_len: u32, host: Host) -> Vec<u8> {
1040        let mut c = vec![0u8; CBW_LEN];
1041        c[..4].copy_from_slice(&0x43425355u32.to_le_bytes());
1042        c[4..8].copy_from_slice(&1u32.to_le_bytes()); // tag
1043        c[8..12].copy_from_slice(&data_len.to_le_bytes());
1044        c[12] = if matches!(host, Host::ExpectsDataIn) {
1045            0x80
1046        } else {
1047            0x00
1048        };
1049        c[14] = 6; // CBWCB length
1050        c[15] = 0x12; // opcode
1051        c
1052    }
1053
1054    /// Splits `bytes` into `packet_size` OUT packets the device will read.
1055    fn enqueue(shared: &Shared, bytes: &[u8], packet_size: u16) {
1056        let mut out = shared.out.lock().unwrap();
1057        for chunk in bytes.chunks(packet_size as usize) {
1058            out.push_back(chunk.to_vec());
1059        }
1060    }
1061
1062    struct Csw {
1063        status: u8,
1064        residue: u32,
1065    }
1066
1067    /// Drives a whole transaction and returns the CSW and the data bytes seen on the wire.
1068    fn run(
1069        packet_size: u16,
1070        data_len: u32,
1071        host: Host,
1072        device: impl FnOnce(&mut MockBbb),
1073    ) -> (Csw, Vec<u8>) {
1074        let (shared, mut bbb) = new_bbb(packet_size);
1075
1076        enqueue(&shared, &cbw(data_len, host), packet_size);
1077        if matches!(host, Host::ExpectsDataOut) {
1078            enqueue(&shared, &vec![0xAA; data_len as usize], packet_size);
1079        }
1080
1081        // Read the CBW (and, for Data-Out, all the OUT data) into the transport. A CBW
1082        // spans several packets at small MTUs, so this may take multiple polls.
1083        for _ in 0..64 {
1084            let _ = bbb.poll();
1085            let out_drained = !matches!(host, Host::ExpectsDataOut) || bbb.left_to_transfer == 0;
1086            if bbb.get_command().is_some() && out_drained {
1087                break;
1088            }
1089        }
1090
1091        device(&mut bbb);
1092
1093        for _ in 0..64 {
1094            let _ = bbb.poll();
1095            if matches!(bbb.state, State::CommandTransfer) {
1096                break;
1097            }
1098        }
1099
1100        // The stream is data (if any) followed by the 13-byte CSW. Both fragment
1101        // across packets at small MTUs, so reassemble and split off the trailing CSW.
1102        let stream = std::mem::take(&mut *shared.input.lock().unwrap()).concat();
1103        assert!(stream.len() >= CSW_LEN, "no CSW sent");
1104        let (data, raw) = stream.split_at(stream.len() - CSW_LEN);
1105        assert!(raw.starts_with(&CSW_SIGNATURE_LE));
1106        (
1107            Csw {
1108                status: raw[12],
1109                residue: u32::from_le_bytes(raw[8..12].try_into().unwrap()),
1110            },
1111            data.to_vec(),
1112        )
1113    }
1114
1115    fn is_invalid_state<T>(r: Result<T, TransportError<BulkOnlyError>>) -> bool {
1116        matches!(r, Err(TransportError::Error(BulkOnlyError::InvalidState)))
1117    }
1118}