diff --git a/Cargo.lock b/Cargo.lock index 4b823ba54a..c41a1e0178 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1402,6 +1402,7 @@ version = "0.1.0" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)", "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "xhcid 0.1.0", ] diff --git a/usbscsid/Cargo.toml b/usbscsid/Cargo.toml index 7d5f1abf88..ed2808e70f 100644 --- a/usbscsid/Cargo.toml +++ b/usbscsid/Cargo.toml @@ -10,5 +10,6 @@ license = "MIT" [dependencies] base64 = "0.11" # Only for debugging plain = "0.2" +redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" } thiserror = "1" xhcid = { path = "../xhcid" } diff --git a/usbscsid/src/main.rs b/usbscsid/src/main.rs index a60712edf7..82a5529ccd 100644 --- a/usbscsid/src/main.rs +++ b/usbscsid/src/main.rs @@ -1,11 +1,18 @@ use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::os::unix::io::{FromRawFd, RawFd}; +use syscall::{CloneFlags, Packet, SchemeMut}; use xhcid_interface::{ConfigureEndpointsReq, DeviceReqData, XhciClientHandle}; pub mod protocol; pub mod scsi; -use scsi::cmds::StandardInquiryData; +mod scheme; + +use scheme::ScsiScheme; +use scsi::Scsi; fn main() { let mut args = env::args().skip(1); @@ -18,6 +25,15 @@ fn main() { println!("USB SCSI driver spawned with scheme `{}`, port {}, protocol {}", scheme, port, protocol); + // Daemonize so that xhcid can continue to do other useful work (until proper IRQs, + // async-await, and multithreading :D) + if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 { + return + } + + let disk_scheme_name = format!(":disk/{}-{}_scsi", scheme, port); + + // TODO: Use eventfds. let handle = XhciClientHandle::new(scheme, port); let desc = handle.get_standard_descs().expect("Failed to get standard descriptors"); @@ -41,27 +57,23 @@ fn main() { let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc).expect("Failed to setup protocol"); - assert_eq!(std::mem::size_of::(), 96); - let mut inquiry_buffer = [0u8; 259]; // additional_len = 255 - let mut command_buffer = [0u8; 6]; + // TODO: Let all of the USB drivers syscall clone(2), and xhcid won't have to keep track of all + // the drivers. + let socket_fd = syscall::open(disk_scheme_name, syscall::O_RDWR | syscall::O_CREAT).expect("usbscsid: failed to create disk scheme"); + let mut socket_file = unsafe { File::from_raw_fd(socket_fd as RawFd) }; - let min_inquiry_len = 5u16; + //syscall::setrens(0, 0).expect("scsid: failed to enter null namespace"); + let mut scsi = Scsi::new(&mut *protocol); + let mut scsi_scheme = ScsiScheme::new(&mut scsi); - let max_inquiry_len = { - { - let inquiry = plain::from_mut_bytes(&mut command_buffer).unwrap(); - *inquiry = scsi::cmds::Inquiry::new(false, 0, min_inquiry_len, 0); + // TODO: Use nonblocking and put all pending calls in a todo VecDeque. Use an eventfd as well. + 'scheme_loop: loop { + let mut packet = Packet::default(); + match socket_file.read(&mut packet) { + Ok(0) => break 'scheme_loop, + Ok(_) => (), + Err(err) => panic!("scsid: failed to read disk scheme: {}", err), } - protocol.send_command(&command_buffer, DeviceReqData::In(&mut inquiry_buffer[..min_inquiry_len as usize])).expect("Failed to send command"); - let standard_inquiry_data: &StandardInquiryData = dbg!(plain::from_bytes(&inquiry_buffer).unwrap()); - 4 + u16::from(standard_inquiry_data.additional_len) - }; - { - { - let inquiry = plain::from_mut_bytes(&mut command_buffer).unwrap(); - *inquiry = scsi::cmds::Inquiry::new(false, 0, max_inquiry_len, 0); - } - protocol.send_command(&command_buffer, DeviceReqData::In(&mut inquiry_buffer[..max_inquiry_len as usize])).expect("Failed to send command"); - let standard_inquiry_data: &StandardInquiryData = dbg!(plain::from_bytes(&inquiry_buffer).unwrap()); + scsi_scheme.handle(&mut packet); } } diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index e3ef9bf7d7..0aafe80aeb 100644 --- a/usbscsid/src/protocol/bot.rs +++ b/usbscsid/src/protocol/bot.rs @@ -1,12 +1,9 @@ -use std::convert::TryInto; -use std::fs::File; -use std::io::prelude::*; -use std::{io, slice}; +use std::num::NonZeroU32; +use std::slice; -use thiserror::Error; -use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle}; +use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle}; -use super::{Protocol, ProtocolError}; +use super::{Protocol, ProtocolError, SendCommandStatus}; pub const CBW_SIGNATURE: u32 = 0x43425355; @@ -130,7 +127,7 @@ impl<'a> BulkOnlyTransport<'a> { } impl<'a> Protocol for BulkOnlyTransport<'a> { - fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result<(), ProtocolError> { + fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result { self.current_tag += 1; let tag = self.current_tag; @@ -138,10 +135,10 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { println!(); let mut cbw_bytes = [0u8; 31]; - println!("{}", base64::encode(&cbw_bytes)); let cbw = plain::from_mut_bytes::(&mut cbw_bytes).unwrap(); *cbw = CommandBlockWrapper::new(tag, data.len() as u32, data.direction().into(), 0, cb)?; let cbw = *cbw; + println!("{}", base64::encode(&cbw_bytes)); dbg!(self.bulk_in.status()?, self.bulk_out.status()?); @@ -209,7 +206,13 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { dbg!(self.bulk_in.status()?, self.bulk_out.status()?); } - Ok(()) + Ok(if csw.status == CswStatus::Passed as u8 { + SendCommandStatus::Success + } else if csw.status == CswStatus::Failed as u8 { + SendCommandStatus::Failed { residue: NonZeroU32::new(csw.data_residue) } + } else { + return Err(ProtocolError::ProtocolError("bulk-only transport phase error, or other")); + }) } } diff --git a/usbscsid/src/protocol/mod.rs b/usbscsid/src/protocol/mod.rs index 569505199f..2ffe4e5d83 100644 --- a/usbscsid/src/protocol/mod.rs +++ b/usbscsid/src/protocol/mod.rs @@ -1,4 +1,5 @@ use std::io; +use std::num::NonZeroU32; use thiserror::Error; use xhcid_interface::{DeviceReqData, DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError}; @@ -16,10 +17,24 @@ pub enum ProtocolError { #[error("attempted recovery failed")] RecoveryFailed, + + #[error("protocol error")] + ProtocolError(&'static str), +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SendCommandStatus { + Success, + Failed { residue: Option }, +} +impl Default for SendCommandStatus { + fn default() -> Self { + Self::Success + } } pub trait Protocol { - fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result<(), ProtocolError>; + fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result; } /// Bulk-only transport diff --git a/usbscsid/src/scheme.rs b/usbscsid/src/scheme.rs new file mode 100644 index 0000000000..46d1a18393 --- /dev/null +++ b/usbscsid/src/scheme.rs @@ -0,0 +1,83 @@ +use std::collections::BTreeMap; +use std::str; + +use crate::scsi::Scsi; + +use syscall::SchemeMut; +use syscall::error::{Error, Result}; +use syscall::error::{EACCES, EBADF, ENOENT, ENOSYS}; +use syscall::flag::{O_DIRECTORY, O_STAT}; +use syscall::flag::{MODE_CHR, MODE_DIR}; + +// TODO: Only one disk, right? +const LIST_CONTENTS: &'static [u8] = b"disk\n"; + +enum Handle { + List(usize), + Disk(usize), + //Partition(usize, u32, usize), +} + +pub struct ScsiScheme<'a> { + scsi: &'a mut Scsi, + handles: BTreeMap, + next_fd: usize, +} + +impl<'a> ScsiScheme<'a> { + pub fn new(scsi: &'a mut Scsi) -> Self { + Self { + scsi, + handles: BTreeMap::new(), + next_fd: 0, + } + } +} + +impl<'a> SchemeMut for ScsiScheme<'a> { + fn open(&mut self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result { + if uid != 0 { + return Err(Error::new(EACCES)); + } + let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_start_matches('/'); + let handle = if path_str.is_empty() { + // List + Handle::List(0) + } else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') { + // TODO: Partitions. + return Err(Error::new(ENOSYS)); + } else { + Handle::Disk(0) + }; + self.next_fd += 1; + self.handles.insert(self.next_fd, handle); + Err(Error::new(ENOSYS)) + } + fn fstat(&mut self, fd: usize, stat: &mut syscall::Stat) -> Result { + match self.handles.get(&fd).ok_or(Error::new(EBADF))? { + Handle::Disk(_) => { + stat.st_mode = MODE_CHR; + stat.st_size = self.scsi.get_disk_size(); + // TODO: stat.st_blocks + } + Handle::List(_) => { + stat.st_mode = MODE_DIR; + stat.st_size = LIST_CONTENTS.len() as u64; + // TODO: stat.st_blocks + } + } + Err(Error::new(ENOSYS)) + } + fn fpath(&mut self, fd: usize, path: &mut [u8]) -> Result { + Err(Error::new(ENOSYS)) + } + fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result { + Err(Error::new(ENOSYS)) + } + fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result { + Err(Error::new(ENOSYS)) + } + fn write(&mut self, fd: usize, buf: &[u8]) -> Result { + Err(Error::new(ENOSYS)) + } +} diff --git a/usbscsid/src/scsi/cmds.rs b/usbscsid/src/scsi/cmds.rs index edc8ae69ac..98c6006a86 100644 --- a/usbscsid/src/scsi/cmds.rs +++ b/usbscsid/src/scsi/cmds.rs @@ -1,3 +1,4 @@ +use std::{fmt, mem, slice}; use super::opcodes::{Opcode, ServiceActionA3}; #[repr(packed)] @@ -16,7 +17,7 @@ pub struct ReportIdentInfo { unsafe impl plain::Plain for ReportIdentInfo {} impl ReportIdentInfo { - pub fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self { + pub const fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self { Self { opcode: Opcode::ServiceActionA3 as u8, serviceaction: ServiceActionA3::ReportIdentInfo as u8, @@ -72,6 +73,12 @@ impl ReportSuppOpcodes { pub const fn get_all(rctd: bool, alloc_len: u32, control: u8) -> Self { Self::new(ReportSuppOpcodesOptions::ListAll, rctd, 0, 0, alloc_len, control) } + pub const fn get_supp_no_sa(rctd: bool, opcode: Opcode, alloc_len: u32, control: u8) -> Self { + Self::new(ReportSuppOpcodesOptions::NoServicaction, rctd, opcode as u8, 0, alloc_len, control) + } + pub const fn get_supp(rctd: bool, opcode: Opcode, serviceaction: u16, alloc_len: u32, control: u8) -> Self { + Self::new(ReportSuppOpcodesOptions::ExplicitBoth, rctd, opcode as u8, serviceaction, alloc_len, control) + } } pub const REP_OPTS_MAIN_MASK: u8 = 0b0000_0111; @@ -96,6 +103,8 @@ pub enum ReportSuppOpcodesOptions { /// Returns one command with the requested opcode and service action. The command may or may /// not implement service actions, but if it does, it has to be correct for the return value to /// indicate SUPPORTED. + /// + /// This option seems to be reserved for SPC-3 (inquiry version value of 5). IndicateSupport, } @@ -104,10 +113,23 @@ pub enum ReportSuppOpcodesOptions { pub struct AllCommandsParam { /// Little endian pub data_len: u32, - pub descs: [CommandDescriptor], + pub descs: [CommandDescriptor; 0], } +impl AllCommandsParam { + pub const fn alloc_len(&self) -> u32 { + 3 + u32::from_le(self.data_len) + } + pub unsafe fn descs(&self) -> &[CommandDescriptor] { + assert_eq!(mem::size_of::(), 20); + slice::from_raw_parts(&self.descs as *const CommandDescriptor, (self.alloc_len() as usize - 4) / mem::size_of::()) + } +} + +unsafe impl plain::Plain for AllCommandsParam {} + #[repr(packed)] +#[derive(Clone, Copy, Debug)] pub struct CommandDescriptor { pub opcode: u8, pub _rsvd1: u8, @@ -118,14 +140,47 @@ pub struct CommandDescriptor { pub a: u8, /// little endian pub cdb_len: u16, + pub cmd_timeouts_desc: [u8; 12], } #[repr(packed)] pub struct OneCommandParam { pub _rsvd: u8, - /// bits 2:0 for SUPPOR, bits 6:3 reserved, and bit 7 for CTDP pub a: u8, - // TODO + pub cdb_size: u16, + pub usage_data: [u8; 0], +} +unsafe impl plain::Plain for OneCommandParam {} + +impl OneCommandParam { + pub const fn ctdp(&self) -> bool { + self.a & (1 << 7) != 0 + } + pub fn support(&self) -> OneCommandParamSupport { + let raw = self.a & 0b111; + // Safe because all possible values are covered by the enum. + unsafe { mem::transmute(raw) } + } + pub const fn total_len(&self) -> u16 { + self.cdb_size as u16 + 3 + } + /// Unsafe because the reference to self has to be valid for an additional (self.cdb_size - 1) bytes. + pub unsafe fn cdb_usage_data(&self) -> &[u8] { + slice::from_raw_parts(&self.usage_data as *const u8, self.total_len() as usize - 4) + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum OneCommandParamSupport { + NoDataAvail = 0b000, + NotSupported = 0b001, + Rsvd2 = 0b010, + Supported = 0b011, + Rsvd4 = 0b100, + SupportedVendor = 0b101, + Rsvd6 = 0b110, + Rsvd7 = 0b111, } #[repr(packed)] @@ -141,7 +196,7 @@ pub struct Inquiry { unsafe impl plain::Plain for Inquiry {} impl Inquiry { - pub fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self { + pub const fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self { Self { opcode: Opcode::Inquiry as u8, evpd: evpd as u8, @@ -172,8 +227,278 @@ pub struct StandardInquiryData { pub driver_serial_no: [u8; 8], pub vendor_uniq: [u8; 12], _rsvd1: [u8; 2], - pub vendor_descs: [u16; 8], + pub version_descs: [u16; 8], _rsvd2: [u8; 22], } - unsafe impl plain::Plain for StandardInquiryData {} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct RequestSense { + pub opcode: u8, + pub desc: u8, // bits 7:1 reserved + _rsvd: u16, + pub alloc_len: u8, + pub control: u8, +} +unsafe impl plain::Plain for RequestSense {} + +impl RequestSense { + pub const MINIMAL_ALLOC_LEN: u8 = 252; + + pub const fn new(desc: bool, alloc_len: u8, control: u8) -> Self { + Self { + opcode: Opcode::RequestSense as u8, + desc: desc as u8, + _rsvd: 0, + alloc_len, + control, + } + } +} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct FixedFormatSenseData { + pub a: u8, + _obsolete: u8, + pub b: u8, + pub info: u32, + pub add_sense_len: u8, + pub command_specific_info: u32, + pub add_sense_code: u8, + pub add_sense_code_qual: u8, + pub field_replacable_unit_code: u8, + pub sense_key_specific: [u8; 3], // little endian + pub add_sense_bytes: [u8; 0], +} +unsafe impl plain::Plain for FixedFormatSenseData {} + +impl FixedFormatSenseData { + pub const fn additional_len(&self) -> u16 { + self.add_sense_len as u16 + 7 + } + pub unsafe fn add_sense_bytes(&self) -> &[u8] { + slice::from_raw_parts(&self.add_sense_len as *const u8, self.add_sense_len as usize - 18) + } + pub fn sense_key(&self) -> SenseKey { + let sense_key_raw = self.b & 0b1111; + // Safe because all possible values (0-15) are used by the enum. + unsafe { mem::transmute(sense_key_raw) } + } +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum SenseKey { + NoSense = 0x00, + RecoveredError = 0x01, + NotReady = 0x02, + MediumError = 0x03, + HardwareError = 0x04, + IllegalRequest = 0x05, + UnitAttention = 0x06, + DataProtect = 0x07, + BlankCheck = 0x08, + VendorSpecific = 0x09, + CopyAborted = 0x0A, + AbortedCommand = 0x0B, + Reserved = 0x0C, + VolumeOverflow = 0x0D, + Miscompare = 0x0E, + Completed = 0x0F, +} +impl Default for SenseKey { + fn default() -> Self { + Self::NoSense + } +} + +pub const ADD_SENSE_CODE05_INVAL_CDB_FIELD: u8 = 0x24; + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct Read16 { + pub opcode: u8, + pub a: u8, + pub lba: u64, + pub transfer_len: u32, + pub b: u8, + pub control: u8, +} + +impl Read16 { + pub const fn new(lba: u64, transfer_len: u32, control: u8) -> Self { + // TODO: RDPROTECT, DPO, FUA, RARC + // TODO: DLD + // TODO: Group number + Self { + opcode: Opcode::Read16 as u8, + a: 0, + lba: u64::to_le(lba), + transfer_len: u32::to_le(transfer_len), + b: 0, + control, + } + } +} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct ModeSense6 { + pub opcode: u8, + pub a: u8, + pub b: u8, + pub subpage_code: u8, + pub alloc_len: u8, + pub control: u8, +} +unsafe impl plain::Plain for ModeSense6 {} + +impl ModeSense6 { + pub const fn new(dbd: bool, page_code: u8, pc: u8, subpage_code: u8, alloc_len: u8, control: u8) -> Self { + Self { + opcode: Opcode::ModeSense6 as u8, + a: (dbd as u8) << 3, + b: page_code | (pc << 6), + subpage_code, + alloc_len, + control, + } + } +} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct ModeSense10 { + pub opcode: u8, + pub a: u8, + pub b: u8, + pub subpage_code: u8, + pub _rsvd: [u8; 3], + pub alloc_len: u16, + pub control: u8, +} +unsafe impl plain::Plain for ModeSense10 {} + +impl ModeSense10 { + pub const fn new(dbd: bool, llbaa: bool, page_code: u8, pc: ModePageControl, subpage_code: u8, alloc_len: u16, control: u8) -> Self { + Self { + opcode: Opcode::ModeSense10 as u8, + a: ((dbd as u8) << 3) | ((llbaa as u8) << 4), + b: page_code | ((pc as u8) << 6), + subpage_code, + _rsvd: [0u8; 3], + alloc_len: u16::from_le(alloc_len), + control, + } + } + pub const fn get_block_desc(alloc_len: u16, control: u8) -> Self { + Self::new(false, true, 0x3F, ModePageControl::CurrentValues, 0x00, alloc_len, control) + } +} + +#[repr(u8)] +pub enum ModePageControl { + CurrentValues, + ChangeableChanges, + DefaultValues, + SavedValue, +} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct ShortLbaModeParamBlkDesc { + pub block_count: u32, + _rsvd: u8, + pub logical_block_len: [u8; 3], +} +unsafe impl plain::Plain for ShortLbaModeParamBlkDesc {} + +impl ShortLbaModeParamBlkDesc { + pub const fn block_count(&self) -> u32 { + u32::from_le(self.block_count) + } + pub const fn logical_block_len(&self) -> u32 { + u24_le_to_u32(self.logical_block_len) + } +} + +const fn u24_le_to_u32(u24: [u8; 3]) -> u32 { + ((u24[0] as u32) << 16) + | ((u24[1] as u32) << 8) + | (u24[2] as u32) +} + +/// From SPC-3, when LONGLBA is set. For newer devices, `ShortLbaModeParamBlkDesc` is used instead (I +/// think). +#[repr(packed)] +#[derive(Clone, Copy)] +pub struct GeneralModeParamBlkDesc { + pub density_code: u8, + pub block_count: [u8; 3], + _rsvd: u8, + pub block_length: [u8; 3], +} +unsafe impl plain::Plain for GeneralModeParamBlkDesc {} + +impl fmt::Debug for GeneralModeParamBlkDesc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("GeneralModeParamBlkDesc") + .field("density_code", &self.density_code) + .field("block_count", &u24_le_to_u32(self.block_count)) + .field("block_length", &u24_le_to_u32(self.block_length)) + .finish() + } +} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct LongLbaModeParamBlkDesc { + pub block_count: u64, + _rsvd: u32, + pub logical_block_len: u32, +} +unsafe impl plain::Plain for LongLbaModeParamBlkDesc {} + +impl LongLbaModeParamBlkDesc { + pub const fn block_count(&self) -> u64 { + u64::from_le(self.block_count) + } + pub const fn logical_block_len(&self) -> u32 { + u32::from_le(self.logical_block_len) + } +} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct ModeParamHeader6 { + pub mode_data_len: u8, + pub medium_ty: u8, + pub a: u8, + pub block_desc_len: u8, +} +unsafe impl plain::Plain for ModeParamHeader6 {} + +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct ModeParamHeader10 { + pub mode_data_len: u16, + pub medium_ty: u8, + pub a: u8, + pub b: u8, + _rsvd: u8, + pub block_desc_len: u16, +} +unsafe impl plain::Plain for ModeParamHeader10 {} +impl ModeParamHeader10 { + pub const fn mode_data_len(&self) -> u16 { + u16::from_le(self.mode_data_len) + } + pub const fn block_desc_len(&self) -> u16 { + u16::from_le(self.block_desc_len) + } + pub const fn longlba(&self) -> bool { + (self.b & 0x01) != 0 + } +} diff --git a/usbscsid/src/scsi/mod.rs b/usbscsid/src/scsi/mod.rs index 1b01389e80..7254dcb7a1 100644 --- a/usbscsid/src/scsi/mod.rs +++ b/usbscsid/src/scsi/mod.rs @@ -1,6 +1,178 @@ +use std::mem; + pub mod cmds; pub mod opcodes; +use thiserror::Error; +use xhcid_interface::DeviceReqData; + +use crate::protocol::{Protocol, ProtocolError, SendCommandStatus}; +use cmds::{SenseKey, StandardInquiryData}; +use opcodes::Opcode; pub struct Scsi { + command_buffer: [u8; 16], + inquiry_buffer: [u8; 259], + data_buffer: Vec, +} + +const INQUIRY_CMD_LEN: u8 = 6; +const REPORT_SUPP_OPCODES_CMD_LEN: u8 = 12; +const REQUEST_SENSE_CMD_LEN: u8 = 6; +const MIN_INQUIRY_ALLOC_LEN: u16 = 5; +const MIN_REPORT_SUPP_OPCODES_ALLOC_LEN: u32 = 4; + +#[derive(Debug, Error)] +pub enum ScsiError { + #[error("protocol error when sending command: {0}")] + ProtocolError(#[from] ProtocolError), +} + +impl Scsi { + pub fn new(protocol: &mut dyn Protocol) -> Self { + assert_eq!(std::mem::size_of::(), 96); + let mut this = Self { + command_buffer: [0u8; 16], + inquiry_buffer: [0u8; 259], // additional_len = 255 max + data_buffer: Vec::new(), + }; + + // Get the max length that the device supports, of the Standard Inquiry Data. + let max_inquiry_len = this.get_inquiry_alloc_len(protocol); + // Get the Standard Inquiry Data. + this.get_standard_inquiry_data(protocol, max_inquiry_len); + this.res_standard_inquiry_data(); + + dbg!(this.get_mode_sense10(protocol).unwrap()); + + this + } + pub fn get_inquiry_alloc_len(&mut self, protocol: &mut dyn Protocol) -> u16 { + self.get_standard_inquiry_data(protocol, MIN_INQUIRY_ALLOC_LEN); + let standard_inquiry_data = self.res_standard_inquiry_data(); + 4 + u16::from(standard_inquiry_data.additional_len) + } + pub fn get_standard_inquiry_data(&mut self, protocol: &mut dyn Protocol, max_inquiry_len: u16) { + let inquiry = self.cmd_inquiry(); + *inquiry = cmds::Inquiry::new(false, 0, max_inquiry_len, 0); + + protocol.send_command(&self.command_buffer[..INQUIRY_CMD_LEN as usize], DeviceReqData::In(&mut self.inquiry_buffer[..max_inquiry_len as usize])).expect("Failed to send INQUIRY command"); + } + /*/// Similar to `check_supp_opcode_sized`, but simply checks whether the opcode is supported, + /// without fetching any actual data. + pub fn check_supp_opcode(&mut self, protocol: &mut dyn Protocol, opcode: Opcode, sa: Option) -> Result { + self.check_supp_opcode_sized(protocol, opcode, sa, 2) + } + pub fn check_supp_opcode_sized(&mut self, protocol: &mut dyn Protocol, opcode: Opcode, sa: Option, alloc_len: u32) -> Result { + let report_supp_opcodes = self.cmd_report_supp_opcodes(); + *report_supp_opcodes = if let Some(serviceaction) = sa { + cmds::ReportSuppOpcodes::get_supp(false, opcode, serviceaction, alloc_len, 0) + } else { + cmds::ReportSuppOpcodes::get_supp_no_sa(false, opcode, alloc_len, 0) + }; + self.data_buffer.resize(std::mem::size_of::(), 0); + protocol.send_command(&self.command_buffer[..REPORT_SUPP_OPCODES_CMD_LEN as usize], DeviceReqData::In(&mut self.data_buffer[..alloc_len as usize]))?; + Ok(self.res_one_command().support() == cmds::OneCommandParamSupport::Supported) + }*/ + + /*pub fn get_supp_opcodes_alloc_len(&mut self, protocol: &mut dyn Protocol) -> u32 { + self.get_supp_opcodes(protocol, MIN_REPORT_SUPP_OPCODES_ALLOC_LEN); + self.res_all_commands().alloc_len() + }*/ + /*pub fn get_supp_opcodes(&mut self, protocol: &mut dyn Protocol, alloc_len: u32) { + let report_supp_opcodes = self.cmd_report_supp_opcodes(); + *report_supp_opcodes = cmds::ReportSuppOpcodes::get_all(false, alloc_len, 0); + self.data_buffer.resize(alloc_len as usize, 0); + let status = protocol.send_command(&self.command_buffer[..REPORT_SUPP_OPCODES_CMD_LEN as usize], DeviceReqData::In(&mut self.data_buffer[..alloc_len as usize])).expect("Failed to send REPORT_SUPP_OPCODES command"); + if status != SendCommandStatus::Success { + self.get_ff_sense(protocol, cmds::RequestSense::MINIMAL_ALLOC_LEN); + let data = self.res_ff_sense_data(); + if data.sense_key() == SenseKey::IllegalRequest && data.add_sense_code == cmds::ADD_SENSE_CODE05_INVAL_CDB_FIELD { + } + } + }*/ + pub fn get_ff_sense(&mut self, protocol: &mut dyn Protocol, alloc_len: u8) { + let request_sense = self.cmd_request_sense(); + *request_sense = cmds::RequestSense::new(false, alloc_len, 0); + self.data_buffer.resize(alloc_len.into(), 0); + protocol.send_command(&self.command_buffer[..REQUEST_SENSE_CMD_LEN as usize], DeviceReqData::In(&mut self.data_buffer[..alloc_len as usize])).expect("Failed to send REQUEST_SENSE command"); + } + pub fn get_mode_sense10(&mut self, protocol: &mut dyn Protocol) -> Result<(&cmds::ModeParamHeader10, BlkDescSlice), ScsiError> { + let initial_alloc_len = 4; // covers both mode_data_len and blk_desc_len. + let mode_sense10 = self.cmd_mode_sense10(); + *mode_sense10 = cmds::ModeSense10::get_block_desc(initial_alloc_len, 0); + self.data_buffer.resize(mem::size_of::(), 0); + if let SendCommandStatus::Failed { .. } = protocol.send_command(&self.command_buffer[..10], DeviceReqData::In(&mut self.data_buffer[..initial_alloc_len as usize]))? { + self.get_ff_sense(protocol, 252); + panic!("{:?}", self.res_ff_sense_data()); + } + + let optimal_alloc_len = self.res_mode_param_header10().block_desc_len() + self.res_mode_param_header10().mode_data_len() + mem::size_of::() as u16; + + let mode_sense10 = self.cmd_mode_sense10(); + *mode_sense10 = cmds::ModeSense10::get_block_desc(optimal_alloc_len, 0); + self.data_buffer.resize(optimal_alloc_len as usize, 0); + protocol.send_command(&self.command_buffer[..10], DeviceReqData::In(&mut self.data_buffer[..optimal_alloc_len as usize]))?; + Ok((self.res_mode_param_header10(), self.res_blkdesc_mode10())) + } + + pub fn cmd_inquiry(&mut self) -> &mut cmds::Inquiry { + plain::from_mut_bytes(&mut self.command_buffer).unwrap() + } + pub fn cmd_mode_sense6(&mut self) -> &mut cmds::ModeSense6 { + plain::from_mut_bytes(&mut self.command_buffer).unwrap() + } + pub fn cmd_mode_sense10(&mut self) -> &mut cmds::ModeSense10 { + plain::from_mut_bytes(&mut self.command_buffer).unwrap() + } + /*pub fn cmd_report_supp_opcodes(&mut self) -> &mut cmds::ReportSuppOpcodes { + plain::from_mut_bytes(&mut self.command_buffer).unwrap() + }*/ + pub fn cmd_request_sense(&mut self) -> &mut cmds::RequestSense { + plain::from_mut_bytes(&mut self.command_buffer).unwrap() + } + pub fn res_standard_inquiry_data(&self) -> &StandardInquiryData { + plain::from_bytes(&self.inquiry_buffer).unwrap() + } + /* + pub fn res_all_commands(&self) -> &cmds::AllCommandsParam { + plain::from_bytes(&self.data_buffer).unwrap() + } + pub fn res_one_command(&self) -> &cmds::OneCommandParam { + plain::from_bytes(&self.data_buffer).unwrap() + }*/ + pub fn res_ff_sense_data(&self) -> &cmds::FixedFormatSenseData { + plain::from_bytes(&self.data_buffer).unwrap() + } + pub fn res_mode_param_header6(&self) -> &cmds::ModeParamHeader6 { + plain::from_bytes(&self.data_buffer).unwrap() + } + pub fn res_mode_param_header10(&self) -> &cmds::ModeParamHeader10 { + plain::from_bytes(&self.data_buffer).unwrap() + } + pub fn res_blkdesc_mode6(&self) -> &[cmds::ShortLbaModeParamBlkDesc] { + let header = self.res_mode_param_header6(); + let descs_start = mem::size_of::(); + plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len)]).unwrap() + } + pub fn res_blkdesc_mode10(&self) -> BlkDescSlice { + let header = self.res_mode_param_header10(); + let descs_start = mem::size_of::(); + println!("MODE_SENSE PAGES: {}", base64::encode(&self.data_buffer[descs_start + header.block_desc_len() as usize..])); + if header.longlba() { + BlkDescSlice::Long(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len)]).unwrap()) + } else { + //BlkDescSlice::Short(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len)]).unwrap()) + BlkDescSlice::General(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len)]).unwrap()) + } + } + pub fn get_disk_size(&mut self) -> u64 { + todo!() + } +} +#[derive(Debug)] +pub enum BlkDescSlice<'a> { + //Short(&'a [cmds::ShortLbaModeParamBlkDesc]), + General(&'a [cmds::GeneralModeParamBlkDesc]), + Long(&'a [cmds::LongLbaModeParamBlkDesc]), }