use crate::usb::error::{UsbError, UsbResult};
use crate::usb::host::dwc2::ep0::{
self, bulk_in, bulk_out, dma_rx_slice, DMA_OFF_CBW, DMA_OFF_CSW, DMA_OFF_SECTOR,
MSC_SECTOR_DMA_CAP, PID_DATA0, PID_DATA1,
};
use crate::usb::host::MscEnumerated;
#[inline]
pub fn mass_storage_reset_setup(interface: u16) -> [u8; 8] {
[
0x21,
0xFF,
0x00,
0x00,
interface as u8,
(interface >> 8) as u8,
0x00,
0x00,
]
}
#[inline]
pub fn get_max_lun_setup(interface: u16) -> [u8; 8] {
[
0xA1,
0xFE,
0x00,
0x00,
interface as u8,
(interface >> 8) as u8,
0x01,
0x00,
]
}
pub fn bulk_only_reset(dev: u32, interface: u16, ep0_mps: u32) -> UsbResult<()> {
ep0::ep0_control_write_no_data(dev, mass_storage_reset_setup(interface), ep0_mps)
}
pub fn get_max_lun(dev: u32, interface: u16, ep0_mps: u32) -> UsbResult<u8> {
ep0::ep0_control_read_one_byte(dev, get_max_lun_setup(interface), ep0_mps)
}
pub const CBW_SIGNATURE: u32 = 0x4342_5355;
pub const CSW_SIGNATURE: u32 = 0x5342_5355;
pub const CBW_FLAG_DATA_IN: u8 = 0x80;
pub const SCSI_TEST_UNIT_READY: u8 = 0x00;
pub const SCSI_REQUEST_SENSE: u8 = 0x03;
pub const SCSI_INQUIRY: u8 = 0x12;
pub const SCSI_READ_CAPACITY_10: u8 = 0x25;
pub const SCSI_READ_10: u8 = 0x28;
#[derive(Clone, Copy, Debug)]
pub struct MscDevice {
pub addr: u32,
pub iface: u8,
pub ep0_mps: u32,
pub bulk_in_ep: u32,
pub bulk_in_mps: u32,
pub bulk_out_ep: u32,
pub bulk_out_mps: u32,
in_pid: u32,
out_pid: u32,
next_tag: u32,
}
impl MscDevice {
pub fn new(
addr: u32,
iface: u8,
ep0_mps: u32,
bulk_in_ep: u32,
bulk_in_mps: u32,
bulk_out_ep: u32,
bulk_out_mps: u32,
) -> Self {
Self {
addr,
iface,
ep0_mps,
bulk_in_ep,
bulk_in_mps,
bulk_out_ep,
bulk_out_mps,
in_pid: PID_DATA0,
out_pid: PID_DATA0,
next_tag: 0xc0ff_ee01,
}
}
pub fn from_enumerated(en: &MscEnumerated) -> UsbResult<Self> {
if en.bulk_in_ep == 0 || en.bulk_out_ep == 0 {
return Err(UsbError::Protocol("MSC bulk endpoints not enumerated"));
}
if en.bulk_in_mps == 0 || en.bulk_out_mps == 0 {
return Err(UsbError::Protocol("MSC bulk MPS unknown"));
}
Ok(Self::new(
u32::from(en.addr),
en.iface_num,
en.ep0_mps,
u32::from(en.bulk_in_ep),
u32::from(en.bulk_in_mps),
u32::from(en.bulk_out_ep),
u32::from(en.bulk_out_mps),
))
}
pub fn reset_data_toggle(&mut self) {
self.in_pid = PID_DATA0;
self.out_pid = PID_DATA0;
}
pub fn bulk_only_reset(&mut self) -> UsbResult<()> {
bulk_only_reset(self.addr, u16::from(self.iface), self.ep0_mps)?;
self.reset_data_toggle();
Ok(())
}
fn next_tag(&mut self) -> u32 {
let t = self.next_tag;
self.next_tag = self.next_tag.wrapping_add(1);
t
}
fn build_cbw(buf: &mut [u8; 31], tag: u32, data_len: u32, dir_in: bool, lun: u8, cdb: &[u8]) {
debug_assert!(!cdb.is_empty() && cdb.len() <= 16);
buf[0..4].copy_from_slice(&CBW_SIGNATURE.to_le_bytes());
buf[4..8].copy_from_slice(&tag.to_le_bytes());
buf[8..12].copy_from_slice(&data_len.to_le_bytes());
buf[12] = if dir_in { CBW_FLAG_DATA_IN } else { 0 };
buf[13] = lun;
buf[14] = cdb.len() as u8;
for i in 0..16 {
buf[15 + i] = if i < cdb.len() { cdb[i] } else { 0 };
}
}
#[inline]
fn pkt_count(bytes: usize, mps: u32) -> u32 {
if bytes == 0 {
1 } else {
((bytes as u32) + mps - 1) / mps
}
}
#[inline]
fn flip_pid(pid: u32, packets: u32) -> u32 {
if packets & 1 == 0 {
pid
} else if pid == PID_DATA0 {
PID_DATA1
} else {
PID_DATA0
}
}
fn send_cbw(
&mut self,
tag: u32,
data_len: u32,
dir_in: bool,
lun: u8,
cdb: &[u8],
) -> UsbResult<()> {
let mut cbw = [0u8; 31];
Self::build_cbw(&mut cbw, tag, data_len, dir_in, lun, cdb);
bulk_out(
self.addr,
self.bulk_out_ep,
self.bulk_out_mps,
self.out_pid,
&cbw,
DMA_OFF_CBW,
)?;
let pkts = Self::pkt_count(cbw.len(), self.bulk_out_mps);
self.out_pid = Self::flip_pid(self.out_pid, pkts);
Ok(())
}
fn recv_csw(&mut self, expect_tag: u32) -> UsbResult<u32> {
let actual = bulk_in(
self.addr,
self.bulk_in_ep,
self.bulk_in_mps,
self.in_pid,
13,
DMA_OFF_CSW,
)?;
let pkts = Self::pkt_count(actual, self.bulk_in_mps);
self.in_pid = Self::flip_pid(self.in_pid, pkts);
if actual < 13 {
return Err(UsbError::Protocol("CSW short"));
}
let csw = dma_rx_slice(DMA_OFF_CSW, 13).ok_or(UsbError::Protocol("CSW dma slice"))?;
let sig = u32::from_le_bytes([csw[0], csw[1], csw[2], csw[3]]);
let tag = u32::from_le_bytes([csw[4], csw[5], csw[6], csw[7]]);
let residue = u32::from_le_bytes([csw[8], csw[9], csw[10], csw[11]]);
let status = csw[12];
if sig != CSW_SIGNATURE {
return Err(UsbError::Protocol("CSW signature mismatch"));
}
if tag != expect_tag {
return Err(UsbError::Protocol("CSW tag mismatch"));
}
match status {
0 => Ok(residue),
1 => Err(UsbError::Protocol("SCSI command failed (CSW=1)")),
_ => Err(UsbError::Protocol("SCSI phase error (CSW=2)")),
}
}
pub fn scsi_command_in(&mut self, lun: u8, cdb: &[u8], data_len: u32) -> UsbResult<usize> {
if data_len as usize > MSC_SECTOR_DMA_CAP {
return Err(UsbError::Protocol("SCSI data exceeds DMA window"));
}
let tag = self.next_tag();
self.send_cbw(tag, data_len, true, lun, cdb)?;
let actual = if data_len > 0 {
let n = bulk_in(
self.addr,
self.bulk_in_ep,
self.bulk_in_mps,
self.in_pid,
data_len as usize,
DMA_OFF_SECTOR,
)?;
let pkts = Self::pkt_count(n, self.bulk_in_mps);
self.in_pid = Self::flip_pid(self.in_pid, pkts);
n
} else {
0
};
let _residue = self.recv_csw(tag)?;
Ok(actual)
}
pub fn scsi_command_no_data(&mut self, lun: u8, cdb: &[u8]) -> UsbResult<()> {
let tag = self.next_tag();
self.send_cbw(tag, 0, false, lun, cdb)?;
let _residue = self.recv_csw(tag)?;
Ok(())
}
pub fn inquiry(&mut self, lun: u8) -> UsbResult<[u8; 36]> {
let cdb = [SCSI_INQUIRY, 0, 0, 0, 36, 0];
let actual = self.scsi_command_in(lun, &cdb, 36)?;
if actual < 36 {
return Err(UsbError::Protocol("INQUIRY data short"));
}
let mut out = [0u8; 36];
if let Some(s) = dma_rx_slice(DMA_OFF_SECTOR, 36) {
out.copy_from_slice(s);
}
Ok(out)
}
pub fn test_unit_ready(&mut self, lun: u8) -> UsbResult<()> {
let cdb = [SCSI_TEST_UNIT_READY, 0, 0, 0, 0, 0];
self.scsi_command_no_data(lun, &cdb)
}
pub fn read_capacity_10(&mut self, lun: u8) -> UsbResult<(u32, u32)> {
let cdb = [SCSI_READ_CAPACITY_10, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let actual = self.scsi_command_in(lun, &cdb, 8)?;
if actual < 8 {
return Err(UsbError::Protocol("READ_CAPACITY data short"));
}
let s = dma_rx_slice(DMA_OFF_SECTOR, 8).ok_or(UsbError::Protocol("READ_CAPACITY dma"))?;
let lba = u32::from_be_bytes([s[0], s[1], s[2], s[3]]);
let block = u32::from_be_bytes([s[4], s[5], s[6], s[7]]);
Ok((lba, block))
}
pub fn read_10(
&mut self,
lun: u8,
lba: u32,
blocks: u16,
block_size: u32,
) -> UsbResult<usize> {
let bytes = (blocks as u32).checked_mul(block_size).ok_or(UsbError::Protocol("read len overflow"))?;
let cdb = [
SCSI_READ_10,
0,
(lba >> 24) as u8,
(lba >> 16) as u8,
(lba >> 8) as u8,
lba as u8,
0,
(blocks >> 8) as u8,
blocks as u8,
0,
];
self.scsi_command_in(lun, &cdb, bytes)
}
#[inline]
pub fn read_data(&self, off: usize, len: usize) -> Option<&'static [u8]> {
dma_rx_slice(DMA_OFF_SECTOR.checked_add(off)?, len)
}
}