1use core::marker::PhantomData;
37
38use embassy_sync::blocking_mutex::raw::{NoopRawMutex, RawMutex};
39use embassy_sync::mutex::Mutex;
40use embassy_usb_driver::host::{PipeError, SplitInfo, UsbHostAllocator, UsbPipe, pipe};
41use embassy_usb_driver::{Direction as UsbDirection, EndpointAddress, EndpointInfo, EndpointType};
42
43use crate::control::{ControlType, Recipient, RequestType, SetupPacket};
44use crate::descriptor::ConfigurationDescriptor;
45use crate::handler::EnumerationInfo;
46
47const CLASS_MSC: u8 = 0x08;
49const SUBCLASS_SCSI: u8 = 0x06;
50const PROTOCOL_BBB: u8 = 0x50;
51
52const REQ_GET_MAX_LUN: u8 = 0xFE;
54const REQ_BULK_ONLY_RESET: u8 = 0xFF;
55
56const REQ_CLEAR_FEATURE: u8 = 0x01;
58const FEATURE_ENDPOINT_HALT: u16 = 0x0000;
59
60const CBW_SIGNATURE: u32 = 0x43425355; const CSW_SIGNATURE: u32 = 0x53425355; const CBW_LEN: usize = 31;
64const CSW_LEN: usize = 13;
65const CBW_FLAG_IN: u8 = 0x80;
66
67const CSW_PASSED: u8 = 0x00;
69const CSW_FAILED: u8 = 0x01;
70const CSW_PHASE_ERROR: u8 = 0x02;
71
72const SCSI_TEST_UNIT_READY: u8 = 0x00;
74const SCSI_REQUEST_SENSE: u8 = 0x03;
75const SCSI_INQUIRY: u8 = 0x12;
76const SCSI_PREVENT_ALLOW_REMOVAL: u8 = 0x1E;
77const SCSI_READ_CAPACITY_10: u8 = 0x25;
78const SCSI_READ_10: u8 = 0x28;
79const SCSI_WRITE_10: u8 = 0x2A;
80const SCSI_SYNCHRONIZE_CACHE_10: u8 = 0x35;
81const SCSI_READ_16: u8 = 0x88;
82const SCSI_WRITE_16: u8 = 0x8A;
83const SCSI_SERVICE_ACTION_IN_16: u8 = 0x9E;
84const SCSI_SA_READ_CAPACITY_16: u8 = 0x10;
85
86#[derive(Debug)]
88#[cfg_attr(feature = "defmt", derive(defmt::Format))]
89pub enum MscError {
90 Transfer(PipeError),
92 NoInterface,
94 NoPipe,
96 InvalidResponse,
98 Protocol,
100 PhaseError,
103 Scsi(SenseData),
105 Unaligned,
107 OutOfRange,
109 NoSuchLun,
111 InvalidCdb,
113 #[cfg(feature = "block-device-driver")]
117 BlockSizeMismatch,
118}
119
120impl From<PipeError> for MscError {
121 fn from(e: PipeError) -> Self {
122 Self::Transfer(e)
123 }
124}
125
126impl core::fmt::Display for MscError {
127 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128 match self {
129 Self::Transfer(_) => write!(f, "Transfer error"),
130 Self::NoInterface => write!(f, "No MSC BBB/SCSI interface found"),
131 Self::NoPipe => write!(f, "No free pipe"),
132 Self::InvalidResponse => write!(f, "Invalid response from device"),
133 Self::Protocol => write!(f, "BBB protocol violation"),
134 Self::PhaseError => write!(f, "BBB phase error"),
135 Self::Scsi(_) => write!(f, "SCSI command failed"),
136 Self::Unaligned => write!(f, "Buffer is not block-aligned"),
137 Self::OutOfRange => write!(f, "LBA out of range"),
138 Self::NoSuchLun => write!(f, "No such LUN"),
139 Self::InvalidCdb => write!(f, "Invalid CDB length"),
140 #[cfg(feature = "block-device-driver")]
141 Self::BlockSizeMismatch => write!(f, "Block size mismatch"),
142 }
143 }
144}
145
146impl core::error::Error for MscError {}
147
148pub enum DataDir<'a> {
150 None,
152 In(&'a mut [u8]),
154 Out(&'a [u8]),
156}
157
158impl DataDir<'_> {
159 fn len(&self) -> u32 {
160 match self {
161 Self::None => 0,
162 Self::In(b) => b.len() as u32,
163 Self::Out(b) => b.len() as u32,
164 }
165 }
166
167 fn cbw_flags(&self) -> u8 {
168 match self {
169 Self::In(_) => CBW_FLAG_IN,
170 _ => 0,
171 }
172 }
173}
174
175#[derive(Copy, Clone, Debug, Eq, PartialEq)]
180#[cfg_attr(feature = "defmt", derive(defmt::Format))]
181pub enum CommandOutcome {
182 Ok {
184 residue: u32,
187 },
188 Failed {
190 residue: u32,
192 },
193}
194
195#[derive(Copy, Clone, Debug, Eq, PartialEq)]
201#[cfg_attr(feature = "defmt", derive(defmt::Format))]
202pub enum PeripheralType {
203 DirectAccess,
205 SequentialAccess,
207 CdDvd,
209 Optical,
211 SimplifiedDirectAccess,
213 Other(u8),
215}
216
217impl PeripheralType {
218 fn from_bits(b: u8) -> Self {
219 match b & 0x1F {
220 0x00 => Self::DirectAccess,
221 0x01 => Self::SequentialAccess,
222 0x05 => Self::CdDvd,
223 0x07 => Self::Optical,
224 0x0E => Self::SimplifiedDirectAccess,
225 v => Self::Other(v),
226 }
227 }
228}
229
230#[derive(Copy, Clone, Debug)]
236pub struct InquiryData<'a> {
237 pub peripheral: PeripheralType,
239 pub removable: bool,
241 pub vendor: &'a [u8],
243 pub product: &'a [u8],
245 pub revision: &'a [u8],
247}
248
249#[derive(Copy, Clone, Debug, Eq, PartialEq)]
251#[cfg_attr(feature = "defmt", derive(defmt::Format))]
252#[repr(u8)]
253pub enum SenseKey {
254 NoSense = 0x0,
256 RecoveredError = 0x1,
258 NotReady = 0x2,
260 MediumError = 0x3,
262 HardwareError = 0x4,
264 IllegalRequest = 0x5,
266 UnitAttention = 0x6,
268 DataProtect = 0x7,
270 BlankCheck = 0x8,
272 VendorSpecific = 0x9,
274 CopyAborted = 0xA,
276 AbortedCommand = 0xB,
278 VolumeOverflow = 0xD,
280 Miscompare = 0xE,
282 Reserved = 0xF,
284}
285
286impl SenseKey {
287 fn from_bits(b: u8) -> Self {
288 match b & 0x0F {
289 0x0 => Self::NoSense,
290 0x1 => Self::RecoveredError,
291 0x2 => Self::NotReady,
292 0x3 => Self::MediumError,
293 0x4 => Self::HardwareError,
294 0x5 => Self::IllegalRequest,
295 0x6 => Self::UnitAttention,
296 0x7 => Self::DataProtect,
297 0x8 => Self::BlankCheck,
298 0x9 => Self::VendorSpecific,
299 0xA => Self::CopyAborted,
300 0xB => Self::AbortedCommand,
301 0xD => Self::VolumeOverflow,
302 0xE => Self::Miscompare,
303 _ => Self::Reserved,
304 }
305 }
306}
307
308#[derive(Copy, Clone, Debug, Eq, PartialEq)]
312#[cfg_attr(feature = "defmt", derive(defmt::Format))]
313pub struct SenseData {
314 pub key: SenseKey,
316 pub asc: u8,
318 pub ascq: u8,
320}
321
322#[derive(Copy, Clone, Debug, Eq, PartialEq)]
324#[cfg_attr(feature = "defmt", derive(defmt::Format))]
325pub struct BlockCapacity {
326 pub block_count: u64,
328 pub block_size: u32,
330}
331
332#[derive(Copy, Clone, Debug)]
338#[cfg_attr(feature = "defmt", derive(defmt::Format))]
339pub struct MscInfo {
340 pub interface: u8,
342 pub bulk_in_ep: u8,
344 pub bulk_in_mps: u16,
346 pub bulk_out_ep: u8,
348 pub bulk_out_mps: u16,
350}
351
352pub fn find_msc(config_desc: &[u8]) -> Option<MscInfo> {
354 let cfg = ConfigurationDescriptor::try_from_slice(config_desc).ok()?;
355
356 for iface in cfg.iter_interface() {
357 if iface.interface_class != CLASS_MSC
358 || iface.interface_subclass != SUBCLASS_SCSI
359 || iface.interface_protocol != PROTOCOL_BBB
360 || iface.alternate_setting != 0
361 {
362 continue;
363 }
364
365 let mut in_ep = None;
366 let mut out_ep = None;
367 for ep in iface.iter_endpoints() {
368 if ep.ep_type() != EndpointType::Bulk {
369 continue;
370 }
371 if ep.is_in() {
372 in_ep = Some((ep.endpoint_address, ep.max_packet_size));
373 } else {
374 out_ep = Some((ep.endpoint_address, ep.max_packet_size));
375 }
376 }
377
378 if let (Some((in_a, in_m)), Some((out_a, out_m))) = (in_ep, out_ep) {
379 return Some(MscInfo {
380 interface: iface.interface_number,
381 bulk_in_ep: in_a,
382 bulk_in_mps: in_m,
383 bulk_out_ep: out_a,
384 bulk_out_mps: out_m,
385 });
386 }
387 }
388
389 None
390}
391
392struct Transport<'d, A>
398where
399 A: UsbHostAllocator<'d>,
400{
401 ctrl: A::Pipe<pipe::Control, pipe::InOut>,
402 bulk_in: A::Pipe<pipe::Bulk, pipe::In>,
403 bulk_out: A::Pipe<pipe::Bulk, pipe::Out>,
404 interface: u8,
405 bulk_in_ep: u8,
406 bulk_out_ep: u8,
407 next_tag: u32,
408 dirty: bool,
411 _phantom: PhantomData<&'d ()>,
412}
413
414impl<'d, A> Transport<'d, A>
415where
416 A: UsbHostAllocator<'d>,
417{
418 async fn clear_halt_in(&mut self) -> Result<(), MscError> {
419 clear_endpoint_halt(&mut self.ctrl, self.bulk_in_ep).await?;
420 self.bulk_in.reset_data_toggle();
421 Ok(())
422 }
423
424 async fn clear_halt_out(&mut self) -> Result<(), MscError> {
425 clear_endpoint_halt(&mut self.ctrl, self.bulk_out_ep).await?;
426 self.bulk_out.reset_data_toggle();
427 Ok(())
428 }
429
430 async fn mass_storage_reset(&mut self) -> Result<(), MscError> {
431 let setup = SetupPacket::class_interface_out(REQ_BULK_ONLY_RESET, 0, self.interface as u16, 0);
432 self.ctrl.control_out(&setup.to_bytes(), &[]).await?;
433 self.clear_halt_in().await?;
434 self.clear_halt_out().await?;
435 Ok(())
436 }
437}
438
439async fn clear_endpoint_halt<P>(ctrl: &mut P, ep_addr: u8) -> Result<(), MscError>
440where
441 P: UsbPipe<pipe::Control, pipe::InOut>,
442{
443 let setup = SetupPacket {
444 request_type: RequestType {
445 direction: UsbDirection::Out,
446 control_type: ControlType::Standard,
447 recipient: Recipient::Endpoint,
448 },
449 request: REQ_CLEAR_FEATURE,
450 value: FEATURE_ENDPOINT_HALT,
451 index: ep_addr as u16,
452 length: 0,
453 };
454 ctrl.control_out(&setup.to_bytes(), &[]).await?;
455 Ok(())
456}
457
458async fn get_max_lun<P>(ctrl: &mut P, interface: u8) -> Result<u8, MscError>
459where
460 P: UsbPipe<pipe::Control, pipe::InOut>,
461{
462 let setup = SetupPacket::class_interface_in(REQ_GET_MAX_LUN, 0, interface as u16, 1);
463 let mut buf = [0u8; 1];
464 match ctrl.control_in(&setup.to_bytes(), &mut buf).await {
466 Ok(1) if buf[0] <= 15 => Ok(buf[0]),
467 Ok(_) => Err(MscError::InvalidResponse),
468 Err(PipeError::Stall) => Ok(0),
469 Err(e) => Err(e.into()),
470 }
471}
472
473async fn run_cycle<'d, A>(
475 t: &mut Transport<'d, A>,
476 lun: u8,
477 cdb: &[u8],
478 data: DataDir<'_>,
479) -> Result<CommandOutcome, MscError>
480where
481 A: UsbHostAllocator<'d>,
482{
483 let tag = {
484 let tag = t.next_tag;
485 t.next_tag = t.next_tag.wrapping_add(1);
486 tag
487 };
488
489 let mut cbw = [0u8; CBW_LEN];
490 cbw[0..4].copy_from_slice(&CBW_SIGNATURE.to_le_bytes());
491 cbw[4..8].copy_from_slice(&tag.to_le_bytes());
492 cbw[8..12].copy_from_slice(&data.len().to_le_bytes());
493 cbw[12] = data.cbw_flags();
494 cbw[13] = lun & 0x0F;
495 cbw[14] = cdb.len() as u8;
496 cbw[15..15 + cdb.len()].copy_from_slice(cdb);
497
498 trace!(
499 "MSC: CBW tag={:#010x} lun={} op={:#04x} data_len={} dir={}",
500 tag,
501 lun,
502 cdb[0],
503 data.len(),
504 match data {
505 DataDir::None => "none",
506 DataDir::In(_) => "in",
507 DataDir::Out(_) => "out",
508 },
509 );
510
511 if let Err(e) = t.bulk_out.request_out(&cbw, false).await {
513 if matches!(e, PipeError::Stall) {
514 t.clear_halt_out().await?;
515 }
516 return Err(e.into());
517 }
518
519 match data {
522 DataDir::None => {}
523 DataDir::In(buf) => match t.bulk_in.request_in(buf).await {
524 Ok(_) => {}
525 Err(PipeError::Stall) => t.clear_halt_in().await?,
526 Err(e) => return Err(e.into()),
527 },
528 DataDir::Out(buf) => match t.bulk_out.request_out(buf, false).await {
529 Ok(()) => {}
530 Err(PipeError::Stall) => t.clear_halt_out().await?,
531 Err(e) => return Err(e.into()),
532 },
533 }
534
535 let csw = match read_csw(t).await {
537 Ok(b) => b,
538 Err(MscError::Transfer(PipeError::Stall)) => {
539 t.clear_halt_in().await?;
540 read_csw(t).await?
541 }
542 Err(e) => return Err(e),
543 };
544
545 let signature = u32::from_le_bytes([csw[0], csw[1], csw[2], csw[3]]);
546 let csw_tag = u32::from_le_bytes([csw[4], csw[5], csw[6], csw[7]]);
547 let residue = u32::from_le_bytes([csw[8], csw[9], csw[10], csw[11]]);
548 let status = csw[12];
549
550 if signature != CSW_SIGNATURE || csw_tag != tag {
551 warn!(
552 "MSC: CSW mismatch (expected sig={:#010x} tag={:#010x}, got sig={:#010x} tag={:#010x} residue={} status={:#04x}, raw={:?})",
553 CSW_SIGNATURE, tag, signature, csw_tag, residue, status, csw,
554 );
555 t.mass_storage_reset().await.ok();
556 return Err(MscError::Protocol);
557 }
558
559 match status {
560 CSW_PASSED => Ok(CommandOutcome::Ok { residue }),
561 CSW_FAILED => Ok(CommandOutcome::Failed { residue }),
562 CSW_PHASE_ERROR => {
563 t.mass_storage_reset().await?;
564 Err(MscError::PhaseError)
565 }
566 _ => {
567 t.mass_storage_reset().await.ok();
568 Err(MscError::Protocol)
569 }
570 }
571}
572
573async fn read_csw<'d, A>(t: &mut Transport<'d, A>) -> Result<[u8; CSW_LEN], MscError>
574where
575 A: UsbHostAllocator<'d>,
576{
577 let mut buf = [0u8; CSW_LEN];
578 let n = t.bulk_in.request_in(&mut buf).await?;
579 if n == CSW_LEN {
580 trace!("MSC: CSW raw={:?}", buf);
581 Ok(buf)
582 } else {
583 warn!(
584 "MSC: short CSW ({} bytes, expected {}), data={:?}",
585 n,
586 CSW_LEN,
587 &buf[..n]
588 );
589 Err(MscError::Protocol)
590 }
591}
592
593async fn command_locked<'d, A>(
596 t: &mut Transport<'d, A>,
597 lun: u8,
598 cdb: &[u8],
599 data: DataDir<'_>,
600) -> Result<CommandOutcome, MscError>
601where
602 A: UsbHostAllocator<'d>,
603{
604 if t.dirty {
605 t.mass_storage_reset().await?;
608 t.dirty = false;
609 }
610 t.dirty = true;
611 let result = run_cycle(t, lun, cdb, data).await;
612 if result.is_ok() {
613 t.dirty = false;
614 }
615 result
616}
617
618async fn request_sense_locked<'d, A>(t: &mut Transport<'d, A>, lun: u8) -> Result<SenseData, MscError>
620where
621 A: UsbHostAllocator<'d>,
622{
623 let cdb = [SCSI_REQUEST_SENSE, 0, 0, 0, 18, 0];
624 let mut buf = [0u8; 18];
625 let _ = command_locked(t, lun, &cdb, DataDir::In(&mut buf)).await?;
627 Ok(SenseData {
628 key: SenseKey::from_bits(buf[2]),
629 asc: buf[12],
630 ascq: buf[13],
631 })
632}
633
634async fn run_with_sense_locked<'d, A>(
638 t: &mut Transport<'d, A>,
639 lun: u8,
640 cdb: &[u8],
641 data: DataDir<'_>,
642) -> Result<u32, MscError>
643where
644 A: UsbHostAllocator<'d>,
645{
646 match command_locked(t, lun, cdb, data).await? {
647 CommandOutcome::Ok { residue } => Ok(residue),
648 CommandOutcome::Failed { .. } => Err(MscError::Scsi(request_sense_locked(t, lun).await?)),
649 }
650}
651
652pub struct MscDevice<'d, A, M = NoopRawMutex>
667where
668 A: UsbHostAllocator<'d>,
669 M: RawMutex,
670{
671 transport: Mutex<M, Transport<'d, A>>,
672 interface: u8,
673 max_lun: u8,
674 _phantom: PhantomData<&'d ()>,
675}
676
677impl<'d, A> MscDevice<'d, A, NoopRawMutex>
678where
679 A: UsbHostAllocator<'d>,
680{
681 pub async fn new(alloc: &A, enum_info: &EnumerationInfo, config_desc: &[u8]) -> Result<Self, MscError> {
689 Self::new_with_raw_mutex(alloc, enum_info, config_desc).await
690 }
691}
692
693impl<'d, A, M> MscDevice<'d, A, M>
694where
695 A: UsbHostAllocator<'d>,
696 M: RawMutex,
697{
698 pub async fn new_with_raw_mutex(
706 alloc: &A,
707 enum_info: &EnumerationInfo,
708 config_desc: &[u8],
709 ) -> Result<Self, MscError> {
710 let info = find_msc(config_desc).ok_or(MscError::NoInterface)?;
711
712 let ctrl_ep_info = EndpointInfo {
713 addr: EndpointAddress::from_parts(0, UsbDirection::In),
714 ep_type: EndpointType::Control,
715 max_packet_size: enum_info.device_desc.max_packet_size0 as u16,
716 interval_ms: 0,
717 };
718
719 let in_ep_info = EndpointInfo {
720 addr: EndpointAddress::from_parts((info.bulk_in_ep & 0x0F) as usize, UsbDirection::In),
721 ep_type: EndpointType::Bulk,
722 max_packet_size: info.bulk_in_mps,
723 interval_ms: 0,
724 };
725
726 let out_ep_info = EndpointInfo {
727 addr: EndpointAddress::from_parts((info.bulk_out_ep & 0x0F) as usize, UsbDirection::Out),
728 ep_type: EndpointType::Bulk,
729 max_packet_size: info.bulk_out_mps,
730 interval_ms: 0,
731 };
732
733 let device_address = enum_info.device_address;
734 let split: Option<SplitInfo> = enum_info.split();
735
736 let mut ctrl = alloc
737 .alloc_pipe::<pipe::Control, pipe::InOut>(device_address, &ctrl_ep_info, split)
738 .map_err(|_| MscError::NoPipe)?;
739 let bulk_in = alloc
740 .alloc_pipe::<pipe::Bulk, pipe::In>(device_address, &in_ep_info, split)
741 .map_err(|_| MscError::NoPipe)?;
742 let bulk_out = alloc
743 .alloc_pipe::<pipe::Bulk, pipe::Out>(device_address, &out_ep_info, split)
744 .map_err(|_| MscError::NoPipe)?;
745
746 let max_lun = get_max_lun(&mut ctrl, info.interface).await?;
747
748 let device = Self {
749 transport: Mutex::new(Transport {
750 ctrl,
751 bulk_in,
752 bulk_out,
753 interface: info.interface,
754 bulk_in_ep: info.bulk_in_ep,
755 bulk_out_ep: info.bulk_out_ep,
756 next_tag: 1,
757 dirty: false,
758 _phantom: PhantomData,
759 }),
760 interface: info.interface,
761 max_lun,
762 _phantom: PhantomData,
763 };
764
765 if let Err(e) = device.reset().await {
770 debug!("MSC: initial Bulk-Only Reset failed ({:?}); continuing anyway", e);
771 }
772
773 Ok(device)
774 }
775
776 pub fn interface(&self) -> u8 {
778 self.interface
779 }
780
781 pub fn max_lun(&self) -> u8 {
783 self.max_lun
784 }
785
786 pub fn num_luns(&self) -> u8 {
788 self.max_lun + 1
789 }
790
791 pub fn lun(&self, lun: u8) -> Result<MscLun<'_, 'd, A, M>, MscError> {
798 if lun > self.max_lun {
799 return Err(MscError::NoSuchLun);
800 }
801 Ok(MscLun {
802 device: self,
803 lun,
804 capacity: None,
805 })
806 }
807
808 pub async fn command(&self, lun: u8, cdb: &[u8], data: DataDir<'_>) -> Result<CommandOutcome, MscError> {
825 if cdb.is_empty() || cdb.len() > 16 {
826 return Err(MscError::InvalidCdb);
827 }
828 if lun > self.max_lun {
829 return Err(MscError::NoSuchLun);
830 }
831
832 let mut t = self.transport.lock().await;
833 command_locked(&mut t, lun, cdb, data).await
834 }
835
836 pub async fn reset(&self) -> Result<(), MscError> {
839 let mut t = self.transport.lock().await;
840 t.mass_storage_reset().await?;
841 t.dirty = false;
842 Ok(())
843 }
844}
845
846pub struct MscLun<'dev, 'd, A, M = NoopRawMutex>
860where
861 A: UsbHostAllocator<'d>,
862 M: RawMutex,
863{
864 device: &'dev MscDevice<'d, A, M>,
865 lun: u8,
866 capacity: Option<BlockCapacity>,
867}
868
869impl<'dev, 'd, A, M> MscLun<'dev, 'd, A, M>
870where
871 A: UsbHostAllocator<'d>,
872 M: RawMutex,
873{
874 pub fn lun(&self) -> u8 {
876 self.lun
877 }
878
879 pub fn cached_capacity(&self) -> Option<BlockCapacity> {
882 self.capacity
883 }
884
885 pub fn invalidate_capacity(&mut self) {
887 self.capacity = None;
888 }
889
890 async fn run(&mut self, cdb: &[u8], data: DataDir<'_>) -> Result<u32, MscError> {
893 let mut t = self.device.transport.lock().await;
894 run_with_sense_locked(&mut t, self.lun, cdb, data).await
895 }
896
897 pub async fn inquiry<'a>(&mut self, buf: &'a mut [u8; 36]) -> Result<InquiryData<'a>, MscError> {
899 let cdb = [SCSI_INQUIRY, 0, 0, 0, 36, 0];
900 self.run(&cdb, DataDir::In(&mut buf[..])).await?;
901 Ok(InquiryData {
902 peripheral: PeripheralType::from_bits(buf[0]),
903 removable: buf[1] & 0x80 != 0,
904 vendor: &buf[8..16],
905 product: &buf[16..32],
906 revision: &buf[32..36],
907 })
908 }
909
910 pub async fn request_sense(&mut self) -> Result<SenseData, MscError> {
912 let mut t = self.device.transport.lock().await;
913 request_sense_locked(&mut t, self.lun).await
914 }
915
916 pub async fn test_unit_ready(&mut self) -> Result<bool, MscError> {
923 let cdb = [SCSI_TEST_UNIT_READY, 0, 0, 0, 0, 0];
924 let sense = {
928 let mut t = self.device.transport.lock().await;
929 match command_locked(&mut t, self.lun, &cdb, DataDir::None).await? {
930 CommandOutcome::Ok { .. } => return Ok(true),
931 CommandOutcome::Failed { .. } => request_sense_locked(&mut t, self.lun).await?,
932 }
933 };
934 match sense.key {
935 SenseKey::NotReady | SenseKey::UnitAttention => {
936 self.invalidate_capacity();
937 Ok(false)
938 }
939 _ => Err(MscError::Scsi(sense)),
940 }
941 }
942
943 pub async fn prevent_medium_removal(&mut self, prevent: bool) -> Result<(), MscError> {
945 let cdb = [SCSI_PREVENT_ALLOW_REMOVAL, 0, 0, 0, prevent as u8, 0];
946 self.run(&cdb, DataDir::None).await?;
947 Ok(())
948 }
949
950 pub async fn capacity(&mut self) -> Result<BlockCapacity, MscError> {
957 if let Some(c) = self.capacity {
958 return Ok(c);
959 }
960
961 let cap = {
962 let mut t = self.device.transport.lock().await;
963
964 let cdb = [SCSI_READ_CAPACITY_10, 0, 0, 0, 0, 0, 0, 0, 0, 0];
965 let mut buf = [0u8; 8];
966 run_with_sense_locked(&mut t, self.lun, &cdb, DataDir::In(&mut buf)).await?;
967 let last_lba = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
968 let block_size = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
969
970 if last_lba == 0xFFFF_FFFF {
971 let mut cdb16 = [0u8; 16];
972 cdb16[0] = SCSI_SERVICE_ACTION_IN_16;
973 cdb16[1] = SCSI_SA_READ_CAPACITY_16;
974 cdb16[13] = 32;
975 let mut buf16 = [0u8; 32];
976 run_with_sense_locked(&mut t, self.lun, &cdb16, DataDir::In(&mut buf16)).await?;
977 let last_lba = u64::from_be_bytes([
978 buf16[0], buf16[1], buf16[2], buf16[3], buf16[4], buf16[5], buf16[6], buf16[7],
979 ]);
980 let block_size = u32::from_be_bytes([buf16[8], buf16[9], buf16[10], buf16[11]]);
981 BlockCapacity {
982 block_count: last_lba.saturating_add(1),
983 block_size,
984 }
985 } else {
986 BlockCapacity {
987 block_count: last_lba as u64 + 1,
988 block_size,
989 }
990 }
991 };
992
993 if cap.block_size == 0 {
994 return Err(MscError::InvalidResponse);
995 }
996
997 self.capacity = Some(cap);
998 Ok(cap)
999 }
1000
1001 pub async fn read_blocks(&mut self, lba: u64, buf: &mut [u8]) -> Result<(), MscError> {
1010 let cap = self.capacity().await?;
1011 let (block_size, total_blocks) = check_block_args(lba, buf.len(), &cap)?;
1012
1013 let mut t = self.device.transport.lock().await;
1014
1015 let mut cur_lba = lba;
1016 let mut offset = 0usize;
1017 let mut remaining = total_blocks;
1018
1019 while remaining > 0 {
1020 let (n, use_10) = chunk_blocks(cur_lba, remaining);
1021 let bytes = n as usize * block_size;
1022 let chunk = &mut buf[offset..offset + bytes];
1023
1024 let residue = if use_10 {
1025 let cdb = read10_cdb(cur_lba as u32, n as u16);
1026 run_with_sense_locked(&mut t, self.lun, &cdb, DataDir::In(chunk)).await?
1027 } else {
1028 let cdb = read16_cdb(cur_lba, n);
1029 run_with_sense_locked(&mut t, self.lun, &cdb, DataDir::In(chunk)).await?
1030 };
1031 if residue != 0 {
1032 return Err(MscError::InvalidResponse);
1033 }
1034
1035 offset += bytes;
1036 cur_lba += n as u64;
1037 remaining -= n as u64;
1038 }
1039 Ok(())
1040 }
1041
1042 pub async fn write_blocks(&mut self, lba: u64, buf: &[u8]) -> Result<(), MscError> {
1051 let cap = self.capacity().await?;
1052 let (block_size, total_blocks) = check_block_args(lba, buf.len(), &cap)?;
1053
1054 let mut t = self.device.transport.lock().await;
1055
1056 let mut cur_lba = lba;
1057 let mut offset = 0usize;
1058 let mut remaining = total_blocks;
1059
1060 while remaining > 0 {
1061 let (n, use_10) = chunk_blocks(cur_lba, remaining);
1062 let bytes = n as usize * block_size;
1063 let chunk = &buf[offset..offset + bytes];
1064
1065 let residue = if use_10 {
1066 let cdb = write10_cdb(cur_lba as u32, n as u16);
1067 run_with_sense_locked(&mut t, self.lun, &cdb, DataDir::Out(chunk)).await?
1068 } else {
1069 let cdb = write16_cdb(cur_lba, n);
1070 run_with_sense_locked(&mut t, self.lun, &cdb, DataDir::Out(chunk)).await?
1071 };
1072 if residue != 0 {
1073 return Err(MscError::InvalidResponse);
1074 }
1075
1076 offset += bytes;
1077 cur_lba += n as u64;
1078 remaining -= n as u64;
1079 }
1080 Ok(())
1081 }
1082
1083 pub async fn flush(&mut self) -> Result<(), MscError> {
1086 let cdb = [SCSI_SYNCHRONIZE_CACHE_10, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1087 self.run(&cdb, DataDir::None).await?;
1088 Ok(())
1089 }
1090
1091 #[cfg(feature = "block-device-driver")]
1099 pub fn as_block_device<ALIGN>(&mut self) -> MscBlockDevice<'_, 'dev, 'd, A, M, ALIGN>
1100 where
1101 ALIGN: aligned::Alignment,
1102 {
1103 MscBlockDevice {
1104 lun: self,
1105 _align: PhantomData,
1106 }
1107 }
1108}
1109
1110#[cfg(feature = "block-device-driver")]
1122pub struct MscBlockDevice<'lun, 'dev, 'd, A, M, ALIGN>
1123where
1124 A: UsbHostAllocator<'d>,
1125 M: RawMutex,
1126 ALIGN: aligned::Alignment,
1127{
1128 lun: &'lun mut MscLun<'dev, 'd, A, M>,
1129 _align: PhantomData<fn() -> ALIGN>,
1130}
1131
1132#[cfg(feature = "block-device-driver")]
1133impl<'lun, 'dev, 'd, A, M, ALIGN, const SIZE: usize> block_device_driver::BlockDevice<SIZE>
1134 for MscBlockDevice<'lun, 'dev, 'd, A, M, ALIGN>
1135where
1136 A: UsbHostAllocator<'d>,
1137 M: RawMutex,
1138 ALIGN: aligned::Alignment,
1139{
1140 type Error = MscError;
1141 type Align = ALIGN;
1142
1143 async fn read(
1144 &mut self,
1145 block_address: u32,
1146 data: &mut [aligned::Aligned<ALIGN, [u8; SIZE]>],
1147 ) -> Result<(), MscError> {
1148 let cap = self.lun.capacity().await?;
1149 if cap.block_size as usize != SIZE {
1150 return Err(MscError::BlockSizeMismatch);
1151 }
1152 let bytes = block_device_driver::blocks_to_slice_mut(data);
1153 self.lun.read_blocks(block_address as u64, bytes).await
1154 }
1155
1156 async fn write(
1157 &mut self,
1158 block_address: u32,
1159 data: &[aligned::Aligned<ALIGN, [u8; SIZE]>],
1160 ) -> Result<(), MscError> {
1161 let cap = self.lun.capacity().await?;
1162 if cap.block_size as usize != SIZE {
1163 return Err(MscError::BlockSizeMismatch);
1164 }
1165 let bytes = block_device_driver::blocks_to_slice(data);
1166 self.lun.write_blocks(block_address as u64, bytes).await
1167 }
1168
1169 async fn size(&mut self) -> Result<u64, MscError> {
1170 let cap = self.lun.capacity().await?;
1171 Ok(cap.block_count.saturating_mul(cap.block_size as u64))
1172 }
1173}
1174
1175fn chunk_blocks(lba: u64, remaining: u64) -> (u32, bool) {
1182 const MAX_BLOCKS_10: u64 = u16::MAX as u64;
1183 let use_10 = lba <= u32::MAX as u64;
1184 let n = if use_10 {
1185 remaining.min(MAX_BLOCKS_10) as u32
1186 } else {
1187 remaining.min(u32::MAX as u64) as u32
1188 };
1189 (n, use_10)
1190}
1191
1192fn check_block_args(lba: u64, bytes: usize, cap: &BlockCapacity) -> Result<(usize, u64), MscError> {
1193 let block_size = cap.block_size as usize;
1194 if bytes == 0 || !bytes.is_multiple_of(block_size) {
1195 return Err(MscError::Unaligned);
1196 }
1197 let total_blocks = (bytes / block_size) as u64;
1198 if lba.checked_add(total_blocks).is_none_or(|end| end > cap.block_count) {
1199 return Err(MscError::OutOfRange);
1200 }
1201 Ok((block_size, total_blocks))
1202}
1203
1204fn read10_cdb(lba: u32, blocks: u16) -> [u8; 10] {
1205 let lba = lba.to_be_bytes();
1206 let bl = blocks.to_be_bytes();
1207 [SCSI_READ_10, 0, lba[0], lba[1], lba[2], lba[3], 0, bl[0], bl[1], 0]
1208}
1209
1210fn write10_cdb(lba: u32, blocks: u16) -> [u8; 10] {
1211 let lba = lba.to_be_bytes();
1212 let bl = blocks.to_be_bytes();
1213 [SCSI_WRITE_10, 0, lba[0], lba[1], lba[2], lba[3], 0, bl[0], bl[1], 0]
1214}
1215
1216fn read16_cdb(lba: u64, blocks: u32) -> [u8; 16] {
1217 let lba = lba.to_be_bytes();
1218 let bl = blocks.to_be_bytes();
1219 [
1220 SCSI_READ_16,
1221 0,
1222 lba[0],
1223 lba[1],
1224 lba[2],
1225 lba[3],
1226 lba[4],
1227 lba[5],
1228 lba[6],
1229 lba[7],
1230 bl[0],
1231 bl[1],
1232 bl[2],
1233 bl[3],
1234 0,
1235 0,
1236 ]
1237}
1238
1239fn write16_cdb(lba: u64, blocks: u32) -> [u8; 16] {
1240 let lba = lba.to_be_bytes();
1241 let bl = blocks.to_be_bytes();
1242 [
1243 SCSI_WRITE_16,
1244 0,
1245 lba[0],
1246 lba[1],
1247 lba[2],
1248 lba[3],
1249 lba[4],
1250 lba[5],
1251 lba[6],
1252 lba[7],
1253 bl[0],
1254 bl[1],
1255 bl[2],
1256 bl[3],
1257 0,
1258 0,
1259 ]
1260}
1261
1262#[cfg(test)]
1263mod tests {
1264 use super::*;
1265
1266 #[rustfmt::skip]
1272 const CFG_SIMPLE_MSC: [u8; 32] = [
1273 9, 0x02, 32, 0, 1, 1, 0, 0x80, 50,
1274 9, 0x04, 0, 0, 2, 0x08, 0x06, 0x50, 0,
1275 7, 0x05, 0x81, 0x02, 0x40, 0x00, 0,
1276 7, 0x05, 0x01, 0x02, 0x40, 0x00, 0,
1277 ];
1278
1279 #[test]
1280 fn find_msc_simple() {
1281 let info = find_msc(&CFG_SIMPLE_MSC).unwrap();
1282 assert_eq!(info.interface, 0);
1283 assert_eq!(info.bulk_in_ep, 0x81);
1284 assert_eq!(info.bulk_in_mps, 64);
1285 assert_eq!(info.bulk_out_ep, 0x01);
1286 assert_eq!(info.bulk_out_mps, 64);
1287 }
1288
1289 #[test]
1290 fn find_msc_rejects_non_matching_interface() {
1291 assert!(find_msc(&[]).is_none());
1293
1294 #[rustfmt::skip]
1296 let hid: [u8; 25] = [
1297 9, 0x02, 25, 0, 1, 1, 0, 0x80, 50,
1298 9, 0x04, 0, 0, 1, 0x03, 0x01, 0x01, 0,
1299 7, 0x05, 0x81, 0x03, 0x08, 0x00, 10,
1300 ];
1301 assert!(find_msc(&hid).is_none());
1302
1303 for (offset, value) in [(6, 0x08), (7, 0x01), (3, 1)] {
1305 let mut cfg = CFG_SIMPLE_MSC;
1306 cfg[9 + offset] = value;
1307 assert!(find_msc(&cfg).is_none());
1308 }
1309 }
1310
1311 #[test]
1312 fn find_msc_requires_both_bulk_endpoints() {
1313 #[rustfmt::skip]
1315 let out_only: [u8; 25] = [
1316 9, 0x02, 25, 0, 1, 1, 0, 0x80, 50,
1317 9, 0x04, 0, 0, 1, 0x08, 0x06, 0x50, 0,
1318 7, 0x05, 0x01, 0x02, 0x40, 0x00, 0,
1319 ];
1320 assert!(find_msc(&out_only).is_none());
1321
1322 #[rustfmt::skip]
1324 let in_only: [u8; 25] = [
1325 9, 0x02, 25, 0, 1, 1, 0, 0x80, 50,
1326 9, 0x04, 0, 0, 1, 0x08, 0x06, 0x50, 0,
1327 7, 0x05, 0x81, 0x02, 0x40, 0x00, 0,
1328 ];
1329 assert!(find_msc(&in_only).is_none());
1330
1331 #[rustfmt::skip]
1333 let intr: [u8; 32] = [
1334 9, 0x02, 32, 0, 1, 1, 0, 0x80, 50,
1335 9, 0x04, 0, 0, 2, 0x08, 0x06, 0x50, 0,
1336 7, 0x05, 0x81, 0x03, 0x08, 0x00, 10,
1337 7, 0x05, 0x01, 0x03, 0x08, 0x00, 10,
1338 ];
1339 assert!(find_msc(&intr).is_none());
1340 }
1341
1342 #[test]
1343 fn find_msc_skips_preceding_interfaces() {
1344 #[rustfmt::skip]
1347 let cfg: [u8; 71] = [
1348 9, 0x02, 71, 0, 2, 1, 0, 0x80, 50,
1349 9, 0x04, 0, 0, 1, 0x03, 0x01, 0x01, 0,
1350 7, 0x05, 0x82, 0x03, 0x08, 0x00, 10,
1351 9, 0x04, 1, 1, 2, 0x08, 0x06, 0x50, 0,
1352 7, 0x05, 0x83, 0x02, 0x20, 0x00, 0,
1353 7, 0x05, 0x03, 0x02, 0x20, 0x00, 0,
1354 9, 0x04, 1, 0, 2, 0x08, 0x06, 0x50, 0,
1355 7, 0x05, 0x81, 0x02, 0x40, 0x00, 0,
1356 7, 0x05, 0x01, 0x02, 0x40, 0x00, 0,
1357 ];
1358 let info = find_msc(&cfg).unwrap();
1359 assert_eq!(info.interface, 1);
1360 assert_eq!(info.bulk_in_ep, 0x81);
1361 assert_eq!(info.bulk_in_mps, 64);
1362 assert_eq!(info.bulk_out_ep, 0x01);
1363 }
1364
1365 #[test]
1370 fn chunk_blocks_prefers_read10_while_lba_fits_u32() {
1371 assert_eq!(chunk_blocks(0, 1), (1, true));
1373 assert_eq!(chunk_blocks(0, u16::MAX as u64), (u16::MAX as u32, true));
1375 assert_eq!(chunk_blocks(0, u64::MAX), (u16::MAX as u32, true));
1376 assert_eq!(chunk_blocks(u32::MAX as u64, u64::MAX), (u16::MAX as u32, true));
1377 }
1378
1379 #[test]
1380 fn chunk_blocks_falls_back_to_read16_above_u32_max() {
1381 assert_eq!(chunk_blocks(u32::MAX as u64 + 1, 100), (100, false));
1382 assert_eq!(chunk_blocks(u64::MAX - 10, u64::MAX), (u32::MAX, false));
1383 }
1384
1385 const CAP_1K_512: BlockCapacity = BlockCapacity {
1390 block_count: 1000,
1391 block_size: 512,
1392 };
1393
1394 #[test]
1395 fn check_block_args_accepts_aligned_in_range() {
1396 assert_eq!(check_block_args(0, 512, &CAP_1K_512).unwrap(), (512, 1));
1397 assert_eq!(check_block_args(0, 10 * 512, &CAP_1K_512).unwrap(), (512, 10));
1398 assert_eq!(check_block_args(999, 512, &CAP_1K_512).unwrap(), (512, 1));
1400 }
1401
1402 #[test]
1403 fn check_block_args_rejects_unaligned() {
1404 for bytes in [0, 511, 513] {
1405 assert!(matches!(
1406 check_block_args(0, bytes, &CAP_1K_512),
1407 Err(MscError::Unaligned)
1408 ));
1409 }
1410 }
1411
1412 #[test]
1413 fn check_block_args_rejects_out_of_range() {
1414 for (lba, bytes) in [(1000, 512), (999, 1024), (u64::MAX, 512)] {
1415 assert!(matches!(
1416 check_block_args(lba, bytes, &CAP_1K_512),
1417 Err(MscError::OutOfRange)
1418 ));
1419 }
1420 }
1421
1422 #[test]
1427 fn read_write_10_cdb_encoding() {
1428 let expected = [0, 0, 0x12, 0x34, 0x56, 0x78, 0, 0x12, 0x34, 0];
1429 for (op, cdb) in [
1430 (SCSI_READ_10, read10_cdb(0x1234_5678, 0x1234)),
1431 (SCSI_WRITE_10, write10_cdb(0x1234_5678, 0x1234)),
1432 ] {
1433 let mut want = expected;
1434 want[0] = op;
1435 assert_eq!(cdb, want);
1436 }
1437 }
1438
1439 #[test]
1440 fn read_write_16_cdb_encoding() {
1441 #[rustfmt::skip]
1442 let expected = [
1443 0, 0,
1444 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
1445 0xDE, 0xAD, 0xBE, 0xEF,
1446 0, 0,
1447 ];
1448 for (op, cdb) in [
1449 (SCSI_READ_16, read16_cdb(0x0123_4567_89AB_CDEF, 0xDEAD_BEEF)),
1450 (SCSI_WRITE_16, write16_cdb(0x0123_4567_89AB_CDEF, 0xDEAD_BEEF)),
1451 ] {
1452 let mut want = expected;
1453 want[0] = op;
1454 assert_eq!(cdb, want);
1455 }
1456 }
1457}