#![no_std] #![no_main] use rp_pico::entry; use defmt as _; use defmt_rtt as _; use panic_probe as _; use embedded_hal::digital::StatefulOutputPin; use rp_pico::hal::pac; use rp_pico::hal; use usb_device::{class_prelude::*, prelude::*}; use usbd_storage::subclass::scsi::{Scsi, ScsiCommand}; use usbd_storage::subclass::Command; use usbd_storage::transport::bbb::{BulkOnly, BulkOnlyError}; use usbd_storage::transport::TransportError; const DISK_BLOCK_SIZE: u32 = 512; const DISK_BLOCK_NUM: u32 = 256; const USB_PACKET_SIZE: u16 = 64; // 8,16,32,64 const MAX_LUN: u8 = 0; // max 0x0F const INQUIRY_RESPONSE: [u8; 36] = { let vendor: [u8; 8] = *b"jalr\0\0\0\0"; let model: [u8; 16] = *b"USB RAM disk\0\0\0\0"; let rev: [u8; 4] = *b"1312"; [ 0x00, // periph qualifier, periph device type 0x80, // Removable 0x04, // SPC-2 compliance 0x02, // NormACA, HiSu, Response data format 0x20, // 36 bytes in total 0x00, // additional fields, none set 0x00, // additional fields, none set 0x00, // additional fields, none set vendor[0], vendor[1], vendor[2], vendor[3], vendor[4], vendor[5], vendor[6], vendor[7], model[0], model[1], model[2], model[3], model[4], model[5], model[6], model[7], model[8], model[9], model[10], model[11], model[12], model[13], model[14], model[15], rev[0], rev[1], rev[2], rev[3] ] }; #[derive(Default)] struct State { storage_offset: usize, sense_key: Option, sense_key_code: Option, sense_qualifier: Option, } impl State { fn reset(&mut self) { self.storage_offset = 0; self.sense_key = None; self.sense_key_code = None; self.sense_qualifier = None; } } #[entry] fn main() -> ! { let mut pac = pac::Peripherals::take().unwrap(); let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); let clocks = hal::clocks::init_clocks_and_plls( rp_pico::XOSC_CRYSTAL_FREQ, pac.XOSC, pac.CLOCKS, pac.PLL_SYS, pac.PLL_USB, &mut pac.RESETS, &mut watchdog, ) .ok() .unwrap(); let timer = hal::timer::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); let sio = hal::Sio::new(pac.SIO); let pins = rp_pico::Pins::new( pac.IO_BANK0, pac.PADS_BANK0, sio.gpio_bank0, &mut pac.RESETS, ); let mut led_pin = pins.led.into_push_pull_output(); let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new( pac.USBCTRL_REGS, pac.USBCTRL_DPRAM, clocks.usb_clock, true, &mut pac.RESETS, )); let mut usb_transport_buf: [u8; 512] = [0; 512]; let mut scsi = usbd_storage::subclass::scsi::Scsi::new(&usb_bus, USB_PACKET_SIZE, MAX_LUN, usb_transport_buf.as_mut_slice()).unwrap(); let mut usb_device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0xcafe, 0x4002)) .strings(&[StringDescriptors::new(LangID::EN) .manufacturer("jalr") .product("RAM Mass Storage")]) .unwrap() .device_class(0x08) .self_powered(false) .build(); let mut storage: [u8; (DISK_BLOCK_SIZE * DISK_BLOCK_NUM) as usize] = [0u8; (DISK_BLOCK_SIZE * DISK_BLOCK_NUM) as usize]; const LBA: u32 = DISK_BLOCK_NUM - 1; let (last_cyl, last_head, last_sect) = { const HPC: u32 = 16; const SPT: u32 = 63; ( LBA / (HPC * SPT), (LBA / SPT) % HPC, (LBA % SPT) + 1 ) }; const OEM: [u8; 8] = *b"mkfs.fat"; const FILESYSTEM_ID: [u8; 4] = [0xDE, 0xAD, 0xBE, 0xEF]; const FILESYSTEM_LABEL: [u8; 11] = *b"RAM_USB "; storage[0x1B9] = 0x10; storage[0x1BE..0x1C3].copy_from_slice(&[0x00, 0x00, 0x02, 0x00, 0x0c]); storage[0x1C3] = last_head as u8; storage[0x1C4] = ((last_cyl >> 8) & 0xc0) as u8 | ((last_sect as u8) & 0x3f); storage[0x1C5] = last_cyl as u8; storage[0x1C6..0x1CA].copy_from_slice(&[0x01, 0x00, 0x00, 0x00]); // LBA of first absolute sector in the partition storage[0x1CA] = LBA as u8; storage[0x1CB] = (LBA >> 8) as u8; storage[0x1CC] = (LBA >> 16) as u8; storage[0x1CD] = (LBA >> 24) as u8; storage[0x1FE..0x200].copy_from_slice(&[0x55, 0xaa]); // boot signature // FAT12 Partition storage[0x200..0x203].copy_from_slice(&[0xEB, 0x3C, 0x90]); storage[0x203..0x20B].copy_from_slice(&OEM); storage[0x20B..0x21d].copy_from_slice(&[0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, (LBA & 0xff) as u8, (LBA >> 8) as u8, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01]); storage[0x224..0x227].copy_from_slice(&[0x80, 0x00, 0x29]); storage[0x227..0x22B].copy_from_slice(&FILESYSTEM_ID); storage[0x22B..0x236].copy_from_slice(&FILESYSTEM_LABEL); storage[0x236..0x23E].copy_from_slice(b"FAT12 "); storage[0x3FE..0x400].copy_from_slice(&[0x55, 0xAA]); storage[0x400..0x403].copy_from_slice(&[0xF8, 0xFF, 0xFF]); storage[0x600..0x603].copy_from_slice(&[0xF8, 0xFF, 0xFF]); storage[0x800..0x80B].copy_from_slice(&FILESYSTEM_LABEL); storage[0x80B] = 0x08; // file attribute 0x08 = volume label // https://de.wikipedia.org/wiki/File_Allocation_Table // Remove bootloader code: dd if=/dev/zero conv=notrunc of=foo bs=1 count=420 seek=602 let mut state: State = State { storage_offset: 0, sense_key: None, sense_key_code: None, sense_qualifier: None, }; defmt::info!("entering main loop"); let mut start_time: u64 = 0; loop { let interval = if timer.get_counter().ticks() < 2_000_000 { 25_000 } else { match (usb_device.state(), led_pin.is_set_high().unwrap_or_else(|_| false)) { // The USB device has just been created or reset. (UsbDeviceState::Default, true) => 100_000, (UsbDeviceState::Default, false) => 100_000, // The USB device has received an address from the host. (UsbDeviceState::Addressed, true) => 250_000, (UsbDeviceState::Addressed, false) => 250_000, // The USB device has been configured and is fully functional. (UsbDeviceState::Configured, true) => 1_000_000, (UsbDeviceState::Configured, false) => 0, // The USB device has been suspended by the host or it has been unplugged from the USB bus. (UsbDeviceState::Suspend, true) => 25_000, (UsbDeviceState::Suspend, false) => 2_000_000, } }; if timer.get_counter().ticks() - start_time > interval { let _ = led_pin.toggle(); start_time = timer.get_counter().ticks(); } if !usb_device.poll(&mut [&mut scsi]) { continue; } if matches!(usb_device.state(), UsbDeviceState::Default) { state.reset(); } let _ = scsi.poll(|command| { if let Err(err) = process_command(command, &mut state, &mut storage) { defmt::error!("Got error: {}", err); } }); } } fn process_command( mut command: Command>>, state: &mut State, storage: &mut [u8; (DISK_BLOCK_SIZE * DISK_BLOCK_NUM) as usize] ) -> Result<(), TransportError> { defmt::info!("Handling: {}", command.kind); match command.kind { ScsiCommand::TestUnitReady { .. } => { command.pass(); } ScsiCommand::Inquiry { .. } => { command.try_write_data_all(&INQUIRY_RESPONSE)?; command.pass(); } ScsiCommand::RequestSense { .. } => { command.try_write_data_all(&[ 0x70, // RESPONSE CODE. Set to 70h for information on current errors 0x00, // obsolete state.sense_key.unwrap_or(0), // Bits 3..0: SENSE KEY. Contains information describing the error. 0x00, 0x00, 0x00, 0x00, // INFORMATION. Device-specific or command-specific information. 0x00, // ADDITIONAL SENSE LENGTH. 0x00, 0x00, 0x00, 0x00, // COMMAND-SPECIFIC INFORMATION state.sense_key_code.unwrap_or(0), // ASC state.sense_qualifier.unwrap_or(0), // ASCQ 0x00, 0x00, 0x00, 0x00, ])?; state.reset(); command.pass(); }, ScsiCommand::ReadCapacity10 { .. } => { let mut data = [0u8; 8]; let _ = &mut data[0..4].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_NUM - 1)); let _ = &mut data[4..8].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_SIZE)); command.try_write_data_all(&data)?; command.pass(); } ScsiCommand::ReadCapacity16 { .. } => { let mut data = [0u8; 16]; let _ = &mut data[0..8].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_NUM - 1)); let _ = &mut data[8..12].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_SIZE)); command.try_write_data_all(&data)?; command.pass(); } ScsiCommand::ReadFormatCapacities { .. } => { let mut data = [0u8; 12]; let _ = &mut data[0..4].copy_from_slice(&[ 0x00, 0x00, 0x00, 0x08, // capacity list length ]); let _ = &mut data[4..8].copy_from_slice(&u32::to_be_bytes(DISK_BLOCK_NUM as u32)); // number of blocks data[8] = 0x01; //unformatted media let block_length_be = u32::to_be_bytes(DISK_BLOCK_SIZE); data[9] = block_length_be[1]; data[10] = block_length_be[2]; data[11] = block_length_be[3]; command.try_write_data_all(&data)?; command.pass(); } ScsiCommand::Read { lba, len } => { let lba = lba as u32; let len = len as u32; if state.storage_offset != (len * DISK_BLOCK_SIZE) as usize { let start = (DISK_BLOCK_SIZE * lba) as usize + state.storage_offset; let end = (DISK_BLOCK_SIZE * lba) as usize + (DISK_BLOCK_SIZE * len) as usize; // Uncomment this in order to push data in chunks smaller than a USB packet. // let end = min(start + USB_PACKET_SIZE as usize - 1, end); defmt::info!("Data transfer >>>>>>>> [{}..{}]", start, end); let count = command.write_data(&mut storage[start..end])?; state.storage_offset += count; } else { command.pass(); state.storage_offset = 0; } }, ScsiCommand::Write { lba, len } => { let lba = lba as u32; let len = len as u32; if state.storage_offset != (len * DISK_BLOCK_SIZE) as usize { let start = (DISK_BLOCK_SIZE * lba) as usize + state.storage_offset; let end = (DISK_BLOCK_SIZE * lba) as usize + (DISK_BLOCK_SIZE * len) as usize; defmt::info!("Data transfer <<<<<<<< [{}..{}]", start, end); let count = command.read_data(&mut storage[start..end])?; state.storage_offset += count; if state.storage_offset == (len * DISK_BLOCK_SIZE) as usize { command.pass(); state.storage_offset = 0; } } else { command.pass(); state.storage_offset = 0; } }, ScsiCommand::ModeSense6 { .. } => { command.try_write_data_all(&[ 0x03, // number of bytes that follow 0x00, // the media type is SBC 0x00, // not write-protected, no cache-control bytes support 0x00, // no mode-parameter block descriptors ])?; command.pass(); } ScsiCommand::ModeSense10 { .. } => { command.try_write_data_all(&[0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])?; command.pass(); } ref unknown_scsi_kind => { defmt::error!("Unknown SCSI command: {}", unknown_scsi_kind); state.sense_key.replace(0x05); // illegal request Sense Key state.sense_key_code.replace(0x20); // Invalid command operation ASC state.sense_qualifier.replace(0x00); // Invalid command operation ASCQ command.fail(); } } Ok(()) }