From bd0892e45d965c8ca3601eb3f5d35028fe2ad769 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sat, 15 Feb 2020 00:26:19 +0100 Subject: [PATCH] Make the usb scsi scheme able to do block I/O. --- usbscsid/src/main.rs | 70 +++- usbscsid/src/protocol/bot.rs | 131 +++++-- usbscsid/src/protocol/mod.rs | 23 +- usbscsid/src/scheme.rs | 53 ++- usbscsid/src/scsi/cmds.rs | 74 +++- usbscsid/src/scsi/mod.rs | 138 ++++++- xhcid/src/driver_interface.rs | 134 +++++-- xhcid/src/usb/bos.rs | 11 +- xhcid/src/usb/mod.rs | 3 +- xhcid/src/xhci/extended.rs | 20 +- xhcid/src/xhci/mod.rs | 48 ++- xhcid/src/xhci/scheme.rs | 696 +++++++++++++++++++++++++--------- xhcid/src/xhci/trb.rs | 26 +- 13 files changed, 1104 insertions(+), 323 deletions(-) diff --git a/usbscsid/src/main.rs b/usbscsid/src/main.rs index 932c57af8b..d6691d47c5 100644 --- a/usbscsid/src/main.rs +++ b/usbscsid/src/main.rs @@ -20,15 +20,26 @@ fn main() { const USAGE: &'static str = "usbscsid "; let scheme = args.next().expect(USAGE); - let port = args.next().expect(USAGE).parse::().expect("port has to be a number"); - let protocol = args.next().expect(USAGE).parse::().expect("protocol has to be a number 0-255"); + let port = args + .next() + .expect(USAGE) + .parse::() + .expect("port has to be a number"); + let protocol = args + .next() + .expect(USAGE) + .parse::() + .expect("protocol has to be a number 0-255"); - println!("USB SCSI driver spawned with scheme `{}`, port {}, protocol {}", scheme, port, protocol); + 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 + return; } let disk_scheme_name = format!(":disk/{}-{}_scsi", scheme, port); @@ -36,30 +47,46 @@ fn main() { // TODO: Use eventfds. let handle = XhciClientHandle::new(scheme, port); - let desc = handle.get_standard_descs().expect("Failed to get standard descriptors"); + let desc = handle + .get_standard_descs() + .expect("Failed to get standard descriptors"); // TODO: Perhaps the drivers should just be given the config, interface, and alternate setting // from xhcid. - let (conf_desc, configuration_value, (if_desc, interface_num, alternate_setting)) = desc.config_descs.iter().find_map(|config_desc| { - let interface_desc = config_desc.interface_descs.iter().find_map(|if_desc| if if_desc.class == 8 && if_desc.sub_class == 6 && if_desc.protocol == 0x50 { - Some((if_desc.clone(), if_desc.number, if_desc.alternate_setting)) - } else { - None - })?; - Some((config_desc.clone(), config_desc.configuration_value, interface_desc)) - }).expect("Failed to find suitable configuration"); + let (conf_desc, configuration_value, (if_desc, interface_num, alternate_setting)) = desc + .config_descs + .iter() + .find_map(|config_desc| { + let interface_desc = config_desc.interface_descs.iter().find_map(|if_desc| { + if if_desc.class == 8 && if_desc.sub_class == 6 && if_desc.protocol == 0x50 { + Some((if_desc.clone(), if_desc.number, if_desc.alternate_setting)) + } else { + None + } + })?; + Some(( + config_desc.clone(), + config_desc.configuration_value, + interface_desc, + )) + }) + .expect("Failed to find suitable configuration"); - handle.configure_endpoints(&ConfigureEndpointsReq { - config_desc: configuration_value, - interface_desc: Some(interface_num), - alternate_setting: Some(alternate_setting), - }).expect("Failed to configure endpoints"); + handle + .configure_endpoints(&ConfigureEndpointsReq { + config_desc: configuration_value, + interface_desc: Some(interface_num), + alternate_setting: Some(alternate_setting), + }) + .expect("Failed to configure endpoints"); - let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc).expect("Failed to setup protocol"); + let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc) + .expect("Failed to setup protocol"); // 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 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) }; //syscall::setrens(0, 0).expect("scsid: failed to enter null namespace"); @@ -78,5 +105,8 @@ fn main() { Err(err) => panic!("scsid: failed to read disk scheme: {}", err), } scsi_scheme.handle(&mut packet); + socket_file + .write(&packet) + .expect("scsid: failed to write packet"); } } diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index 13ab6a784f..589dfa367f 100644 --- a/usbscsid/src/protocol/bot.rs +++ b/usbscsid/src/protocol/bot.rs @@ -1,7 +1,11 @@ use std::num::NonZeroU32; use std::slice; -use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle}; +use xhcid_interface::{ + ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, + PortReqRecipient, PortReqTy, PortTransferStatus, XhciClientHandle, XhciClientHandleError, + XhciEndpHandle, +}; use super::{Protocol, ProtocolError, SendCommandStatus, SendCommandStatusKind}; @@ -18,12 +22,18 @@ pub struct CommandBlockWrapper { pub tag: u32, pub data_transfer_len: u32, pub flags: u8, // upper nibble reserved - pub lun: u8, // bits 7:5 reserved + pub lun: u8, // bits 7:5 reserved pub cb_len: u8, pub command_block: [u8; 16], } impl CommandBlockWrapper { - pub fn new(tag: u32, data_transfer_len: u32, direction: EndpBinaryDirection, lun: u8, cb: &[u8]) -> Result { + pub fn new( + tag: u32, + data_transfer_len: u32, + direction: EndpBinaryDirection, + lun: u8, + cb: &[u8], + ) -> Result { let mut command_block = [0u8; 16]; if cb.len() > 16 { return Err(ProtocolError::TooLargeCommandBlock(cb.len())); @@ -86,11 +96,23 @@ pub struct BulkOnlyTransport<'a> { pub const FEATURE_ENDPOINT_HALT: u16 = 0; impl<'a> BulkOnlyTransport<'a> { - pub fn init(handle: &'a XhciClientHandle, config_desc: &ConfDesc, if_desc: &IfDesc) -> Result { + pub fn init( + handle: &'a XhciClientHandle, + config_desc: &ConfDesc, + if_desc: &IfDesc, + ) -> Result { let endpoints = &if_desc.endpoints; - let bulk_in_num = (endpoints.iter().position(|endpoint| endpoint.direction() == EndpDirection::In).unwrap() + 1) as u8; - let bulk_out_num = (endpoints.iter().position(|endpoint| endpoint.direction() == EndpDirection::Out).unwrap() + 1) as u8; + let bulk_in_num = (endpoints + .iter() + .position(|endpoint| endpoint.direction() == EndpDirection::In) + .unwrap() + + 1) as u8; + let bulk_out_num = (endpoints + .iter() + .position(|endpoint| endpoint.direction() == EndpDirection::Out) + .unwrap() + + 1) as u8; let max_lun = get_max_lun(handle, 0)?; println!("BOT_MAX_LUN {}", max_lun); @@ -108,26 +130,40 @@ impl<'a> BulkOnlyTransport<'a> { } fn clear_stall_in(&mut self) -> Result<(), XhciClientHandleError> { self.bulk_in.reset(false)?; - self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(self.bulk_in_num), FEATURE_ENDPOINT_HALT) + self.handle.clear_feature( + PortReqRecipient::Endpoint, + u16::from(self.bulk_in_num), + FEATURE_ENDPOINT_HALT, + ) } fn clear_stall_out(&mut self) -> Result<(), XhciClientHandleError> { self.bulk_out.reset(false)?; - self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(self.bulk_out_num), FEATURE_ENDPOINT_HALT) + self.handle.clear_feature( + PortReqRecipient::Endpoint, + u16::from(self.bulk_out_num), + FEATURE_ENDPOINT_HALT, + ) } fn reset_recovery(&mut self) -> Result<(), ProtocolError> { bulk_only_mass_storage_reset(self.handle, self.interface_num.into())?; self.clear_stall_in()?; self.clear_stall_out()?; - if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.status()? == EndpointStatus::Halted { - return Err(ProtocolError::RecoveryFailed) + if self.bulk_in.status()? == EndpointStatus::Halted + || self.bulk_out.status()? == EndpointStatus::Halted + { + return Err(ProtocolError::RecoveryFailed); } Ok(()) } } impl<'a> Protocol for BulkOnlyTransport<'a> { - fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result { + fn send_command( + &mut self, + cb: &[u8], + data: DeviceReqData, + ) -> Result { self.current_tag += 1; let tag = self.current_tag; @@ -160,26 +196,38 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { DeviceReqData::In(buffer) => { match self.bulk_in.transfer_read(buffer)? { PortTransferStatus::Success => (), - PortTransferStatus::ShortPacket(len) => panic!("received short packed (len {}) when transferring data", len), + PortTransferStatus::ShortPacket(len) => { + panic!("received short packed (len {}) when transferring data", len) + } PortTransferStatus::Stalled => { println!("bulk in endpoint stalled when reading data"); self.clear_stall_in()?; } - PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))), + PortTransferStatus::Unknown => { + return Err(ProtocolError::XhciError( + XhciClientHandleError::InvalidResponse(Invalid( + "unknown transfer status", + )), + )) + } }; println!("{}", base64::encode(&buffer[..])); } - DeviceReqData::Out(buffer) => { - match self.bulk_out.transfer_write(buffer)? { - PortTransferStatus::Success => (), - PortTransferStatus::ShortPacket(len) => panic!("received short packed (len {}) when transferring data", len), - PortTransferStatus::Stalled => { - println!("bulk out endpoint stalled when reading data"); - self.clear_stall_out()?; - } - PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))), + DeviceReqData::Out(buffer) => match self.bulk_out.transfer_write(buffer)? { + PortTransferStatus::Success => (), + PortTransferStatus::ShortPacket(len) => { + panic!("received short packed (len {}) when transferring data", len) } - } + PortTransferStatus::Stalled => { + println!("bulk out endpoint stalled when reading data"); + self.clear_stall_out()?; + } + PortTransferStatus::Unknown => { + return Err(ProtocolError::XhciError( + XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")), + )) + } + }, DeviceReqData::NoData => (), } @@ -190,18 +238,22 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { println!("bulk in endpoint stalled when reading CSW"); self.clear_stall_in()?; } - PortTransferStatus::ShortPacket(n) if n != 13 => panic!("received a short packet when reading CSW ({} != 13)", n), + PortTransferStatus::ShortPacket(n) if n != 13 => { + panic!("received a short packet when reading CSW ({} != 13)", n) + } _ => (), }; println!("{}", base64::encode(&csw_buffer)); let csw = plain::from_bytes::(&csw_buffer).unwrap(); + dbg!(csw); if !csw.is_valid() || csw.tag != cbw.tag { self.reset_recovery()?; } - dbg!(csw); - if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.status()? == EndpointStatus::Halted { + if self.bulk_in.status()? == EndpointStatus::Halted + || self.bulk_out.status()? == EndpointStatus::Halted + { println!("Trying to recover from stall"); dbg!(self.bulk_in.status()?, self.bulk_out.status()?); } @@ -212,19 +264,38 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { } else if csw.status == CswStatus::Failed as u8 { SendCommandStatusKind::Failed } else { - return Err(ProtocolError::ProtocolError("bulk-only transport phase error, or other")); + return Err(ProtocolError::ProtocolError( + "bulk-only transport phase error, or other", + )); }, residue: NonZeroU32::new(csw.data_residue), }) } } -pub fn bulk_only_mass_storage_reset(handle: &XhciClientHandle, if_num: u16) -> Result<(), XhciClientHandleError> { - handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFF, 0, if_num, DeviceReqData::NoData) +pub fn bulk_only_mass_storage_reset( + handle: &XhciClientHandle, + if_num: u16, +) -> Result<(), XhciClientHandleError> { + handle.device_request( + PortReqTy::Class, + PortReqRecipient::Interface, + 0xFF, + 0, + if_num, + DeviceReqData::NoData, + ) } pub fn get_max_lun(handle: &XhciClientHandle, if_num: u16) -> Result { let mut lun = 0u8; let buffer = slice::from_mut(&mut lun); - handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFE, 0, if_num, DeviceReqData::In(buffer))?; + handle.device_request( + PortReqTy::Class, + PortReqRecipient::Interface, + 0xFE, + 0, + if_num, + DeviceReqData::In(buffer), + )?; Ok(lun) } diff --git a/usbscsid/src/protocol/mod.rs b/usbscsid/src/protocol/mod.rs index 7315ab9805..a580765f1d 100644 --- a/usbscsid/src/protocol/mod.rs +++ b/usbscsid/src/protocol/mod.rs @@ -2,7 +2,9 @@ use std::io; use std::num::NonZeroU32; use thiserror::Error; -use xhcid_interface::{DeviceReqData, DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError}; +use xhcid_interface::{ + ConfDesc, DevDesc, DeviceReqData, IfDesc, XhciClientHandle, XhciClientHandleError, +}; #[derive(Debug, Error)] pub enum ProtocolError { @@ -40,7 +42,6 @@ pub enum SendCommandStatusKind { Failed, } - impl Default for SendCommandStatusKind { fn default() -> Self { Self::Success @@ -48,7 +49,11 @@ impl Default for SendCommandStatusKind { } pub trait Protocol { - fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result; + fn send_command( + &mut self, + command: &[u8], + data: DeviceReqData, + ) -> Result; } /// Bulk-only transport @@ -60,9 +65,17 @@ mod uas { use bot::BulkOnlyTransport; -pub fn setup<'a>(handle: &'a XhciClientHandle, protocol: u8, dev_desc: &DevDesc, conf_desc: &ConfDesc, if_desc: &IfDesc) -> Option> { +pub fn setup<'a>( + handle: &'a XhciClientHandle, + protocol: u8, + dev_desc: &DevDesc, + conf_desc: &ConfDesc, + if_desc: &IfDesc, +) -> Option> { match protocol { - 0x50 => Some(Box::new(BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap())), + 0x50 => Some(Box::new( + BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap(), + )), _ => None, } } diff --git a/usbscsid/src/scheme.rs b/usbscsid/src/scheme.rs index 0c88ba6177..07b776604e 100644 --- a/usbscsid/src/scheme.rs +++ b/usbscsid/src/scheme.rs @@ -4,12 +4,12 @@ use std::{cmp, str}; use crate::protocol::Protocol; use crate::scsi::Scsi; -use syscall::SchemeMut; use syscall::error::{Error, Result}; use syscall::error::{EACCES, EBADF, EINVAL, EIO, ENOENT, ENOSYS}; -use syscall::flag::{O_DIRECTORY, O_STAT}; use syscall::flag::{MODE_CHR, MODE_DIR}; +use syscall::flag::{O_DIRECTORY, O_STAT}; use syscall::flag::{SEEK_CUR, SEEK_END, SEEK_SET}; +use syscall::SchemeMut; // TODO: Only one disk, right? const LIST_CONTENTS: &'static [u8] = b"0\n"; @@ -43,7 +43,9 @@ impl<'a> SchemeMut for ScsiScheme<'a> { if uid != 0 { return Err(Error::new(EACCES)); } - let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_start_matches('/'); + 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) @@ -55,7 +57,7 @@ impl<'a> SchemeMut for ScsiScheme<'a> { }; self.next_fd += 1; self.handles.insert(self.next_fd, handle); - Err(Error::new(ENOSYS)) + Ok(self.next_fd) } fn fstat(&mut self, fd: usize, stat: &mut syscall::Stat) -> Result { match self.handles.get(&fd).ok_or(Error::new(EBADF))? { @@ -70,10 +72,17 @@ impl<'a> SchemeMut for ScsiScheme<'a> { stat.st_size = LIST_CONTENTS.len() as u64; } } - Err(Error::new(ENOSYS)) + Ok(0) } - fn fpath(&mut self, fd: usize, path: &mut [u8]) -> Result { - Err(Error::new(ENOSYS)) + fn fpath(&mut self, fd: usize, buf: &mut [u8]) -> Result { + let path = match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + Handle::Disk(_) => "0", + Handle::List(_) => "", + } + .as_bytes(); + let min = std::cmp::min(path.len(), buf.len()); + buf[..min].copy_from_slice(&path[..min]); + Ok(min) } fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result { match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { @@ -102,11 +111,18 @@ impl<'a> SchemeMut for ScsiScheme<'a> { fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result { match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { Handle::Disk(ref mut offset) => { - if *offset as u64 % u64::from(self.scsi.block_size) != 0 || buf.len() as u64 % u64::from(self.scsi.block_size) != 0 { + if *offset as u64 % u64::from(self.scsi.block_size) != 0 + || buf.len() as u64 % u64::from(self.scsi.block_size) != 0 + { return Err(Error::new(EINVAL)); } let lba = *offset as u64 / u64::from(self.scsi.block_size); - let bytes_read = self.scsi.read(self.protocol, lba, buf).map_err(|err| dbg!(err)).or(Err(Error::new(EIO)))?; + let bytes_read = self + .scsi + .read(self.protocol, lba, buf) + .map_err(|err| dbg!(err)) + .or(Err(Error::new(EIO)))?; + *offset += bytes_read as usize; Ok(bytes_read as usize) } Handle::List(ref mut offset) => { @@ -121,6 +137,23 @@ impl<'a> SchemeMut for ScsiScheme<'a> { } } fn write(&mut self, fd: usize, buf: &[u8]) -> Result { - Err(Error::new(ENOSYS)) + match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + Handle::Disk(ref mut offset) => { + if *offset as u64 % u64::from(self.scsi.block_size) != 0 + || buf.len() as u64 % u64::from(self.scsi.block_size) != 0 + { + return Err(Error::new(EINVAL)); + } + let lba = *offset as u64 / u64::from(self.scsi.block_size); + let bytes_written = self + .scsi + .write(self.protocol, lba, buf) + .map_err(|err| dbg!(err)) + .or(Err(Error::new(EIO)))?; + *offset += bytes_written as usize; + Ok(bytes_written as usize) + } + Handle::List(_) => Err(Error::new(EBADF)), + } } } diff --git a/usbscsid/src/scsi/cmds.rs b/usbscsid/src/scsi/cmds.rs index 31949566bd..ca6b2baf4e 100644 --- a/usbscsid/src/scsi/cmds.rs +++ b/usbscsid/src/scsi/cmds.rs @@ -1,5 +1,5 @@ -use std::{fmt, mem, slice}; use super::opcodes::Opcode; +use std::{fmt, mem, slice}; #[repr(packed)] pub struct Inquiry { @@ -124,7 +124,10 @@ impl FixedFormatSenseData { 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) + 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; @@ -189,6 +192,32 @@ impl Read16 { } } +#[repr(packed)] +#[derive(Clone, Copy, Debug)] +pub struct Write16 { + pub opcode: u8, + pub a: u8, + pub lba: u64, // big endian + pub transfer_len: u32, + pub b: u8, + pub control: u8, +} +unsafe impl plain::Plain for Write16 {} + +impl Write16 { + pub const fn new(lba: u64, transfer_len: u32, control: u8) -> Self { + Self { + // TODO + opcode: Opcode::Write16 as u8, + a: 0, + lba, + transfer_len, + b: 0, + control, + } + } +} + #[repr(packed)] #[derive(Clone, Copy, Debug)] pub struct ModeSense6 { @@ -202,7 +231,14 @@ pub struct ModeSense6 { 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 { + 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, @@ -228,7 +264,15 @@ pub struct ModeSense10 { 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 { + 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), @@ -240,7 +284,15 @@ impl ModeSense10 { } } pub const fn get_block_desc(alloc_len: u16, control: u8) -> Self { - Self::new(false, true, 0x3F, ModePageControl::CurrentValues, 0x00, alloc_len, control) + Self::new( + false, + true, + 0x3F, + ModePageControl::CurrentValues, + 0x00, + alloc_len, + control, + ) } } @@ -279,9 +331,7 @@ impl fmt::Debug for ShortLbaModeParamBlkDesc { } const fn u24_be_to_u32(u24: [u8; 3]) -> u32 { - ((u24[0] as u32) << 16) - | ((u24[1] as u32) << 8) - | (u24[2] as u32) + ((u24[0] as u32) << 16) | ((u24[1] as u32) << 8) | (u24[2] as u32) } /// From SPC-3, when LONGLBA is not set, and the peripheral device type of the INQUIRY data indicates that the device is not a direct access device. Otherwise, `ShortLbaModeParamBlkDesc` is used instead. @@ -434,7 +484,9 @@ impl<'a> Iterator for ModePageIter<'a> { if !spf { if page_code == 0x01 { - Some(AnyModePage::RwErrorRecovery(plain::from_bytes(next_buf).ok()?)) + Some(AnyModePage::RwErrorRecovery( + plain::from_bytes(next_buf).ok()?, + )) } else { println!("Unimplemented sub_page {}", base64::encode(next_buf)); None @@ -447,5 +499,7 @@ impl<'a> Iterator for ModePageIter<'a> { } pub fn mode_page_iter(buffer: &[u8]) -> impl Iterator { - ModePageIter { raw: ModePageIterRaw { buffer } } + ModePageIter { + raw: ModePageIterRaw { buffer }, + } } diff --git a/usbscsid/src/scsi/mod.rs b/usbscsid/src/scsi/mod.rs index 3368db7aac..a4b1f3e243 100644 --- a/usbscsid/src/scsi/mod.rs +++ b/usbscsid/src/scsi/mod.rs @@ -1,5 +1,5 @@ -use std::{mem, ops}; use std::convert::TryFrom; +use std::{mem, ops}; pub mod cmds; pub mod opcodes; @@ -29,6 +29,9 @@ const MIN_REPORT_SUPP_OPCODES_ALLOC_LEN: u32 = 4; pub enum ScsiError { #[error("protocol error when sending command: {0}")] ProtocolError(#[from] ProtocolError), + + #[error("overflow")] + Overflow(&'static str), } impl Scsi { @@ -70,31 +73,67 @@ impl Scsi { 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"); + 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"); } 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"); + 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, impl Iterator), ScsiError> { + pub fn get_mode_sense10( + &mut self, + protocol: &mut dyn Protocol, + ) -> Result< + ( + &cmds::ModeParamHeader10, + BlkDescSlice, + impl Iterator, + ), + 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 { kind: SendCommandStatusKind::Failed, .. } = protocol.send_command(&self.command_buffer[..10], DeviceReqData::In(&mut self.data_buffer[..initial_alloc_len as usize]))? { + self.data_buffer + .resize(mem::size_of::(), 0); + if let SendCommandStatus { + kind: SendCommandStatusKind::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 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(), self.res_mode_pages10())) + 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(), + self.res_mode_pages10(), + )) } pub fn cmd_inquiry(&mut self) -> &mut cmds::Inquiry { @@ -112,6 +151,9 @@ impl Scsi { pub fn cmd_read16(&mut self) -> &mut cmds::Read16 { plain::from_mut_bytes(&mut self.command_buffer).unwrap() } + pub fn cmd_write16(&mut self) -> &mut cmds::Write16 { + plain::from_mut_bytes(&mut self.command_buffer).unwrap() + } pub fn res_standard_inquiry_data(&self) -> &StandardInquiryData { plain::from_bytes(&self.inquiry_buffer).unwrap() } @@ -127,17 +169,41 @@ impl Scsi { 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() + 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::(); if header.longlba() { - BlkDescSlice::Long(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len())]).unwrap()) - } else if self.res_standard_inquiry_data().periph_dev_ty() != cmds::PeriphDeviceType::DirectAccess as u8 && self.res_standard_inquiry_data().version() == cmds::InquiryVersion::Spc3 as u8 { - BlkDescSlice::General(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len())]).unwrap()) + BlkDescSlice::Long( + plain::slice_from_bytes( + &self.data_buffer + [descs_start..descs_start + usize::from(header.block_desc_len())], + ) + .unwrap(), + ) + } else if self.res_standard_inquiry_data().periph_dev_ty() + != cmds::PeriphDeviceType::DirectAccess as u8 + && self.res_standard_inquiry_data().version() == cmds::InquiryVersion::Spc3 as u8 + { + BlkDescSlice::General( + 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::Short( + plain::slice_from_bytes( + &self.data_buffer + [descs_start..descs_start + usize::from(header.block_desc_len())], + ) + .unwrap(), + ) } } @@ -150,15 +216,51 @@ impl Scsi { pub fn get_disk_size(&mut self) -> u64 { self.block_count * u64::from(self.block_size) } - pub fn read(&mut self, protocol: &mut dyn Protocol, lba: u64, buffer: &mut [u8]) -> Result { + pub fn read( + &mut self, + protocol: &mut dyn Protocol, + lba: u64, + buffer: &mut [u8], + ) -> Result { let blocks_to_read = buffer.len() as u64 / u64::from(self.block_size); - let transfer_len = u32::try_from(blocks_to_read * u64::from(self.block_size)).ok().unwrap_or(std::u32::MAX); + let bytes_to_read = blocks_to_read as usize * self.block_size as usize; + let transfer_len = u32::try_from(blocks_to_read).or(Err(ScsiError::Overflow( + "number of blocks to read couldn't fit inside a u32", + )))?; { let read = self.cmd_read16(); *read = cmds::Read16::new(lba, transfer_len, 0); } - let status = protocol.send_command(&self.command_buffer[..16], DeviceReqData::In(&mut buffer[..transfer_len as usize]))?; - Ok(status.bytes_transferred(transfer_len)) + // TODO: Use the to-be-written TransferReadStream instead of relying on everything being + // able to fit within a single buffer. + let status = protocol.send_command( + &self.command_buffer[..16], + DeviceReqData::In(&mut buffer[..bytes_to_read]), + )?; + Ok(status.bytes_transferred(bytes_to_read as u32)) + } + pub fn write( + &mut self, + protocol: &mut dyn Protocol, + lba: u64, + buffer: &[u8], + ) -> Result { + let blocks_to_write = buffer.len() as u64 / u64::from(self.block_size); + let bytes_to_write = blocks_to_write as usize * self.block_size as usize; + let transfer_len = u32::try_from(blocks_to_write).or(Err(ScsiError::Overflow( + "number of blocks to write couldn't fit inside a u32", + )))?; + { + let read = self.cmd_write16(); + *read = cmds::Write16::new(lba, transfer_len, 0); + } + // TODO: Use the to-be-written TransferReadStream instead of relying on everything being + // able to fit within a single buffer. + let status = protocol.send_command( + &self.command_buffer[..16], + DeviceReqData::Out(&buffer[..bytes_to_write]), + )?; + Ok(status.bytes_transferred(bytes_to_write as u32)) } } #[derive(Debug)] diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index 5d2c248e12..055bf3096f 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -190,7 +190,9 @@ impl EndpDesc { } pub fn isoch_mult(&self, lec: bool) -> u8 { if !lec && self.is_isoch() { - if self.is_superspeedplus() { return 0 } + if self.is_superspeedplus() { + return 0; + } self.ssc .as_ref() .map(|ssc| ssc.attributes & 0x3) @@ -203,7 +205,9 @@ impl EndpDesc { self.ssc.map(|ssc| ssc.max_burst).unwrap_or(0) } pub fn has_ssp_companion(&self) -> bool { - self.ssc.map(|ssc| ssc.attributes & (1 << 7) != 0).unwrap_or(false) + self.ssc + .map(|ssc| ssc.attributes & (1 << 7) != 0) + .unwrap_or(false) } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -421,21 +425,12 @@ impl XhciClientHandle { let string = std::fs::read_to_string(path)?; Ok(string.parse()?) } - pub fn open_endpoint_ctl( - &self, - num: u8, - ) -> result::Result { + pub fn open_endpoint_ctl(&self, num: u8) -> result::Result { let path = format!("{}:port{}/endpoints/{}/ctl", self.scheme, self.port, num); Ok(File::open(path)?) } - pub fn open_endpoint_data( - &self, - num: u8, - ) -> result::Result { - let path = format!( - "{}:port{}/endpoints/{}/data", - self.scheme, self.port, num - ); + pub fn open_endpoint_data(&self, num: u8) -> result::Result { + let path = format!("{}:port{}/endpoints/{}/data", self.scheme, self.port, num); Ok(File::open(path)?) } pub fn open_endpoint(&self, num: u8) -> result::Result { @@ -541,13 +536,18 @@ pub struct XhciEndpHandle { ctl: File, } +/// The direction of a transfer. #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum XhciEndpCtlDirection { + /// Host to device Out, + /// Device to host In, + /// No data, and hence no I/O on the Data interface file at all. NoData, } +/// A request to an endpoint Ctl interface file. Currently serialized with JSON. #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[non_exhaustive] pub enum XhciEndpCtlReq { @@ -555,9 +555,19 @@ pub enum XhciEndpCtlReq { // TODO: Allow to send multiple buffers in one transfer. /// Tells xhcid that a buffer is about to be sent from the Data interface file, to the /// endpoint. - Transfer(XhciEndpCtlDirection), - // TODO: Allow clients to specify what to reset. + Transfer { + /// The direction of the transfer. If the direction is `XhciEndpCtlDirection::NoData`, no + /// bytes will be transferred, and therefore no reads or writes shall be done to the Data + /// driver interface file. + direction: XhciEndpCtlDirection, + /// The number of bytes to be read or written. This field must be set to zero if the + /// direction is `XhciEndpCtlDirection::NoData`. When all bytes have been read or written, + /// the transfer will be considered complete by xhcid, and a non-pending status will be + /// returned. + count: u32, + }, + // TODO: Allow clients to specify what to reset. /// Tells xhcid that the endpoint is going to be reset. Reset { /// Only issue the Reset Endpoint and Set TR Dequeue Pointer commands, and let the client @@ -568,6 +578,7 @@ pub enum XhciEndpCtlReq { /// Tells xhcid that the endpoint status is going to be retrieved from the Ctl interface file. Status, } +/// A response from an endpoint Ctl interface file. Currently serialized with JSON. #[derive(Clone, Copy, Debug, Serialize, Deserialize)] #[non_exhaustive] pub enum XhciEndpCtlRes { @@ -586,9 +597,9 @@ pub enum XhciEndpCtlRes { impl XhciEndpHandle { fn ctl_req(&mut self, ctl_req: &XhciEndpCtlReq) -> result::Result<(), XhciClientHandleError> { - let ctl_buffer = serde_json::to_vec(ctl_req)?; + let ctl_buffer = serde_json::to_vec(ctl_req).expect("serde"); - let ctl_bytes_written = self.ctl.write(&ctl_buffer)?; + let ctl_bytes_written = self.ctl.write(&ctl_buffer).expect("ctlwrite"); if ctl_bytes_written != ctl_buffer.len() { return Err(Invalid("xhcid didn't process all of the ctl bytes").into()); } @@ -598,7 +609,7 @@ impl XhciEndpHandle { fn ctl_res(&mut self) -> result::Result { // a response must never exceed 256 bytes let mut ctl_buffer = [0u8; 256]; - let ctl_bytes_read = self.ctl.read(&mut ctl_buffer)?; + let ctl_bytes_read = self.ctl.read(&mut ctl_buffer).expect("ctlread"); let ctl_res = serde_json::from_slice(&ctl_buffer[..ctl_bytes_read as usize])?; Ok(ctl_res) @@ -607,35 +618,89 @@ impl XhciEndpHandle { self.ctl_req(&XhciEndpCtlReq::Reset { no_clear_feature }) } pub fn status(&mut self) -> result::Result { - self.ctl_req(&XhciEndpCtlReq::Status)?; - match self.ctl_res()? { + self.ctl_req(&XhciEndpCtlReq::Status) + .expect("status ctlreq"); + match self.ctl_res().expect("status ctlres") { XhciEndpCtlRes::Status(s) => Ok(s), _ => Err(Invalid("expected status response").into()), } } - fn generic_transfer io::Result>(&mut self, direction: XhciEndpCtlDirection, f: F, expected_len: usize) -> result::Result { - let req = XhciEndpCtlReq::Transfer(direction); - self.ctl_req(&req)?; + fn generic_transfer io::Result>( + &mut self, + direction: XhciEndpCtlDirection, + f: F, + expected_len: u32, + ) -> result::Result { + let req = XhciEndpCtlReq::Transfer { + direction, + count: expected_len, + }; + self.ctl_req(&req).expect("ctl_req"); - let bytes_read = f(&mut self.data)?; - let res = self.ctl_res()?; + let bytes_read = f(&mut self.data).expect("f"); + let res = self.ctl_res().expect("ctl_res"); match res { - XhciEndpCtlRes::TransferResult(PortTransferStatus::Success) if bytes_read != expected_len => Err(Invalid("no short packet, but fewer bytes were read/written").into()), + XhciEndpCtlRes::TransferResult(PortTransferStatus::Success) + if bytes_read != expected_len as usize => + { + Err(Invalid("no short packet, but fewer bytes were read/written").into()) + } XhciEndpCtlRes::TransferResult(r) => Ok(r), _ => Err(Invalid("expected transfer result").into()), } } - pub fn transfer_write(&mut self, buf: &[u8]) -> result::Result { - self.generic_transfer(XhciEndpCtlDirection::Out, |data| data.write(buf), buf.len()) + pub fn transfer_write( + &mut self, + buf: &[u8], + ) -> result::Result { + self.generic_transfer( + XhciEndpCtlDirection::Out, + |data| data.write(buf), + buf.len() as u32, + ) } - pub fn transfer_read(&mut self, buf: &mut [u8]) -> result::Result { - let len = buf.len(); + pub fn transfer_read( + &mut self, + buf: &mut [u8], + ) -> result::Result { + let len = buf.len() as u32; self.generic_transfer(XhciEndpCtlDirection::In, |data| data.read(buf), len) } - pub fn transfer_nodata(&mut self, buf: &[u8]) -> result::Result { - self.generic_transfer(XhciEndpCtlDirection::NoData, |_| Ok(0), buf.len()) + pub fn transfer_nodata(&mut self) -> result::Result { + self.generic_transfer(XhciEndpCtlDirection::NoData, |_| Ok(0), 0) } + fn transfer_stream(&mut self, total_len: u32) -> TransferStream { + TransferStream { + bytes_to_transfer: total_len, + bytes_transferred: 0, + bytes_per_transfer: 32768, // TODO + endp_handle: self, + } + } + pub fn transfer_write_stream(&mut self, total_len: u32) -> TransferWriteStream { + TransferWriteStream { + inner: self.transfer_stream(total_len), + } + } + pub fn transfer_read_stream(&mut self, total_len: u32) -> TransferReadStream { + TransferReadStream { + inner: self.transfer_stream(total_len), + } + } +} + +pub struct TransferWriteStream<'a> { + inner: TransferStream<'a>, +} +pub struct TransferReadStream<'a> { + inner: TransferStream<'a>, +} +struct TransferStream<'a> { + bytes_to_transfer: u32, + bytes_transferred: u32, + bytes_per_transfer: u32, + endp_handle: &'a mut XhciEndpHandle, } #[derive(Debug, Error)] @@ -651,4 +716,7 @@ pub enum XhciClientHandleError { #[error("transfer buffer too large ({0} > 65536)")] TransferBufTooLarge(usize), + + #[error("unexpected short packet of size {0}")] + UnexpectedShortPacket(usize), } diff --git a/xhcid/src/usb/bos.rs b/xhcid/src/usb/bos.rs index 116a8f6d76..ea6c0cfe23 100644 --- a/xhcid/src/usb/bos.rs +++ b/xhcid/src/usb/bos.rs @@ -75,7 +75,12 @@ impl BosSuperSpeedPlusDesc { (self.attrs & 0x0000_000F) as u8 } pub fn sublink_speed_attr(&self) -> &[u32] { - unsafe { slice::from_raw_parts(&self.sublink_speed_attr as *const u32, self.ssac() as usize + 1) } + unsafe { + slice::from_raw_parts( + &self.sublink_speed_attr as *const u32, + self.ssac() as usize + 1, + ) + } } } @@ -155,7 +160,9 @@ impl<'a> Iterator for BosAnyDevDescIter<'a> { } else if base.cap_ty == DeviceCapability::SuperSpeed as u8 { Some(BosAnyDevDesc::SuperSpeed(*plain::from_bytes(slice).ok()?)) } else if base.cap_ty == DeviceCapability::SuperSpeedPlus as u8 { - Some(BosAnyDevDesc::SuperSpeedPlus(*plain::from_bytes(slice).ok()?)) + Some(BosAnyDevDesc::SuperSpeedPlus( + *plain::from_bytes(slice).ok()?, + )) } else if base.cap_ty == 0 { // TODO return None; diff --git a/xhcid/src/usb/mod.rs b/xhcid/src/usb/mod.rs index 4b22430793..00de50dfce 100644 --- a/xhcid/src/usb/mod.rs +++ b/xhcid/src/usb/mod.rs @@ -2,7 +2,8 @@ pub use self::bos::{bos_capability_descs, BosAnyDevDesc, BosDescriptor, BosSuper pub use self::config::ConfigDescriptor; pub use self::device::DeviceDescriptor; pub use self::endpoint::{ - EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor, SuperSpeedPlusIsochCmpDescriptor, ENDP_ATTR_TY_MASK, + EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor, + SuperSpeedPlusIsochCmpDescriptor, ENDP_ATTR_TY_MASK, }; pub use self::interface::InterfaceDescriptor; pub use self::setup::Setup; diff --git a/xhcid/src/xhci/extended.rs b/xhcid/src/xhci/extended.rs index 2330b29f0d..6e303c30b7 100644 --- a/xhcid/src/xhci/extended.rs +++ b/xhcid/src/xhci/extended.rs @@ -126,16 +126,28 @@ impl ProtocolSpeed { self.psim() == 5 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeed } pub fn is_superspeedplus_gen2x1(&self) -> bool { - self.psim() == 10 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus + self.psim() == 10 + && self.psie() == Psie::Gbps + && self.pfd() + && self.lp() == Lp::SuperSpeedPlus } pub fn is_superspeedplus_gen1x2(&self) -> bool { - self.psim() == 10 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus + self.psim() == 10 + && self.psie() == Psie::Gbps + && self.pfd() + && self.lp() == Lp::SuperSpeedPlus } pub fn is_superspeedplus_gen2x2(&self) -> bool { - self.psim() == 20 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus + self.psim() == 20 + && self.psie() == Psie::Gbps + && self.pfd() + && self.lp() == Lp::SuperSpeedPlus } pub fn is_superspeed_gen_x(&self) -> bool { - self.is_superspeed_gen1x1() || self.is_superspeedplus_gen2x1() || self.is_superspeedplus_gen1x2() || self.is_superspeedplus_gen2x2() + self.is_superspeed_gen1x1() + || self.is_superspeedplus_gen2x1() + || self.is_superspeedplus_gen1x2() + || self.is_superspeedplus_gen2x2() } /// Protocol speed ID value pub fn psiv(&self) -> u8 { diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index e71c9dc225..d8f27df065 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -301,7 +301,8 @@ impl Xhci { pub fn enable_port_slot(&mut self, slot_ty: u8) -> Result { assert_eq!(slot_ty & 0x1F, slot_ty); - let cloned_event_trb = self.execute_command("ENABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?; + let cloned_event_trb = + self.execute_command("ENABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?; Ok(cloned_event_trb.event_slot()) } pub fn disable_port_slot(&mut self, slot: u8) -> Result<()> { @@ -329,7 +330,10 @@ impl Xhci { println!(" - Enable slot"); - let slot_ty = self.supported_protocol(i as u8).expect("Failed to find supported protocol information for port").proto_slot_ty(); + let slot_ty = self + .supported_protocol(i as u8) + .expect("Failed to find supported protocol information for port") + .proto_slot_ty(); let slot = self.enable_port_slot(slot_ty)?; println!(" - Slot {}", slot); @@ -358,7 +362,7 @@ impl Xhci { EndpointState { transfer: RingOrStreams::Ring(ring), driver_if_state: EndpIfState::Init, - } + }, )) .collect::>(), }; @@ -379,7 +383,12 @@ impl Xhci { Ok(()) } - pub fn update_default_control_pipe(&mut self, input_context: &mut Dma, slot_id: u8, dev_desc: &DevDesc) -> Result<()> { + pub fn update_default_control_pipe( + &mut self, + input_context: &mut Dma, + slot_id: u8, + dev_desc: &DevDesc, + ) -> Result<()> { input_context.add_context.write(1 << 1); input_context.drop_context.write(0); @@ -393,14 +402,21 @@ impl Xhci { b &= 0x0000_FFFF; b |= (new_max_packet_size) << 16; endp_ctx.b.write(b); - + self.execute_command("EVALUATE_CONTEXT", |trb, cycle| { trb.evaluate_context(slot_id, input_context.physical(), false, cycle) })?; Ok(()) } - pub fn address_device(&mut self, input_context: &mut Dma, i: usize, slot_ty: u8, slot: u8, speed: u8) -> Result { + pub fn address_device( + &mut self, + input_context: &mut Dma, + i: usize, + slot_ty: u8, + slot: u8, + speed: u8, + ) -> Result { let mut ring = Ring::new(true)?; { @@ -418,7 +434,7 @@ impl Xhci { route_string | (u32::from(mtt) << 25) | (u32::from(hub) << 26) - | (u32::from(context_entries) << 27) + | (u32::from(context_entries) << 27), ); let max_exit_latency = 0u16; @@ -427,7 +443,7 @@ impl Xhci { slot_ctx.b.write( u32::from(max_exit_latency) | (u32::from(root_hub_port_num) << 16) - | (u32::from(number_of_ports) << 24) + | (u32::from(number_of_ports) << 24), ); // TODO @@ -441,12 +457,14 @@ impl Xhci { u32::from(parent_hud_slot_id) | (u32::from(parent_port_num) << 8) | (u32::from(ttt) << 16) - | (u32::from(interrupter) << 22) + | (u32::from(interrupter) << 22), ); let endp_ctx = &mut input_context.device.endpoints[0]; - let speed_id = self.lookup_psiv(root_hub_port_num, speed).expect("Failed to retrieve speed ID"); + let speed_id = self + .lookup_psiv(root_hub_port_num, speed) + .expect("Failed to retrieve speed ID"); let max_error_count = 3u8; // recommended value according to the XHCI spec let ep_ty = 4u8; // control endpoint, bidirectional @@ -470,19 +488,20 @@ impl Xhci { | (u32::from(ep_ty) << 3) | (u32::from(host_initiate_disable) << 7) | (u32::from(max_burst_size) << 8) - | (u32::from(max_packet_size) << 16) + | (u32::from(max_packet_size) << 16), ); let tr = ring.register(); endp_ctx.trh.write((tr >> 32) as u32); endp_ctx.trl.write(tr as u32); } - + let input_context_physical = input_context.physical(); self.execute_command("ADDRESS_DEVICE", |trb, cycle| { trb.address_device(slot, input_context_physical, false, cycle) - }).expect("ADDRESS_DEVICE failed"); + }) + .expect("ADDRESS_DEVICE failed"); Ok(ring) } @@ -584,8 +603,7 @@ impl Xhci { }) } pub fn supported_protocol(&self, port: u8) -> Option<&'static SupportedProtoCap> { - self - .supported_protocols_iter() + self.supported_protocols_iter() .find(|supp_proto| supp_proto.compat_port_range().contains(&port)) } pub fn supported_protocol_speeds( diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index 11465d64fb..50fb886b2b 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -9,8 +9,9 @@ use syscall::io::{Dma, Io}; use syscall::scheme::SchemeMut; use syscall::{ Error, Result, Stat, EACCES, EBADF, EBADFD, EBADMSG, EEXIST, EINVAL, EIO, EISDIR, ENOENT, - ENOSYS, ENOTDIR, ENXIO, EOPNOTSUPP, EOVERFLOW, EPERM, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, - O_CREAT, O_DIRECTORY, O_RDONLY, O_RDWR, O_STAT, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, + ENOSYS, ENOTDIR, ENXIO, EOPNOTSUPP, EOVERFLOW, EPERM, EPROTO, ESPIPE, MODE_CHR, MODE_DIR, + MODE_FILE, O_CREAT, O_DIRECTORY, O_RDONLY, O_RDWR, O_STAT, O_WRONLY, SEEK_CUR, SEEK_END, + SEEK_SET, }; use super::{port, usb}; @@ -18,7 +19,8 @@ use super::{Device, EndpointState, Xhci}; use super::command::CommandRing; use super::context::{ - InputContext, SlotState, StreamContext, StreamContextArray, StreamContextType, ENDPOINT_CONTEXT_STATUS_MASK, + InputContext, SlotState, StreamContext, StreamContextArray, StreamContextType, + ENDPOINT_CONTEXT_STATUS_MASK, }; use super::doorbell::Doorbell; use super::extended::ProtocolSpeed; @@ -38,7 +40,11 @@ pub enum ControlFlow { #[derive(Clone, Copy, Debug)] pub enum EndpIfState { Init, - WaitingForDataPipe(XhciEndpCtlDirection), + WaitingForDataPipe { + direction: XhciEndpCtlDirection, + bytes_transferred: u32, + bytes_to_transfer: u32, + }, WaitingForStatus, WaitingForTransferResult(PortTransferStatus), } @@ -205,7 +211,11 @@ impl AnyDescriptor { } impl Xhci { - pub fn execute_command(&mut self, cmd_name: &str, f: F) -> Result { + pub fn execute_command( + &mut self, + cmd_name: &str, + f: F, + ) -> Result { self.run.ints[0].erdp.write(self.cmd.erdp()); let (cmd, cycle, event) = self.cmd.next(); @@ -232,12 +242,22 @@ impl Xhci { self.run.ints[0].erdp.write(self.cmd.erdp()); Ok(ret) } - pub fn execute_control_transfer(&mut self, port_num: usize, setup: usb::Setup, tk: TransferKind, name: &str, mut d: D) -> Result - where - D: FnMut(&mut Trb, bool) -> ControlFlow, + pub fn execute_control_transfer( + &mut self, + port_num: usize, + setup: usb::Setup, + tk: TransferKind, + name: &str, + mut d: D, + ) -> Result + where + D: FnMut(&mut Trb, bool) -> ControlFlow, { let slot = self.port_state(port_num)?.slot; - let ring = self.endpoint_state_mut(port_num, 0)?.ring().ok_or(Error::new(EIO))?; + let ring = self + .endpoint_state_mut(port_num, 0)? + .ring() + .ok_or(Error::new(EIO))?; { let (cmd, cycle) = ring.next(); @@ -264,8 +284,16 @@ impl Xhci { println!(" - {} Waiting for event", name); } - if event.completion_code() != TrbCompletionCode::Success as u8 || event.trb_type() != TrbType::Transfer as u8 { - println!("{} CONTROL TRANSFER ERROR, EVENT TRB {:#0x} {:#0x} {:#0}»", name, event.data.read(), event.status.read(), event.control.read()); + if event.completion_code() != TrbCompletionCode::Success as u8 + || event.trb_type() != TrbType::Transfer as u8 + { + println!( + "{} CONTROL TRANSFER ERROR, EVENT TRB {:#0x} {:#0x} {:#0}»", + name, + event.data.read(), + event.status.read(), + event.control.read() + ); } event.clone() }; @@ -276,9 +304,16 @@ impl Xhci { } /// NOTE: There has to be AT LEAST one successful invocation of `d`, that actually updates the /// TRB (it could be a NO-OP in the worst case). - pub fn execute_transfer(&mut self, port_num: usize, endp_num: u8, stream_id: u16, name: &str, mut d: D) -> Result - where - D: FnMut(&mut Trb, bool) -> ControlFlow, + pub fn execute_transfer( + &mut self, + port_num: usize, + endp_num: u8, + stream_id: u16, + name: &str, + mut d: D, + ) -> Result + where + D: FnMut(&mut Trb, bool) -> ControlFlow, { let port_state = self.port_state_mut(port_num)?; let slot = port_state.slot; @@ -287,15 +322,18 @@ impl Xhci { .get_mut(&endp_num) .ok_or(Error::new(EBADF))?; - let ring: &mut Ring = match endp_state { - EndpointState { transfer: super::RingOrStreams::Ring(ref mut ring), .. } => ring, - EndpointState { transfer: super::RingOrStreams::Streams(stream_ctx_array), .. } => { - stream_ctx_array - .rings - .get_mut(&1) - .ok_or(Error::new(EBADF))? - } + EndpointState { + transfer: super::RingOrStreams::Ring(ref mut ring), + .. + } => ring, + EndpointState { + transfer: super::RingOrStreams::Streams(stream_ctx_array), + .. + } => stream_ctx_array + .rings + .get_mut(&1) + .ok_or(Error::new(EBADF))?, }; loop { @@ -306,7 +344,11 @@ impl Xhci { } } - self.dbs[usize::from(slot)].write(Self::endp_doorbell(endp_num, self.endp_desc(port_num, endp_num)?, stream_id)); + self.dbs[usize::from(slot)].write(Self::endp_doorbell( + endp_num, + self.endp_desc(port_num, endp_num)?, + stream_id, + )); let cloned_trb = { let event = self.cmd.next_event(); @@ -347,7 +389,13 @@ impl Xhci { Ok(cloned_trb) } fn device_req_no_data(&mut self, port: usize, req: usb::Setup) -> Result<()> { - self.execute_control_transfer(port, req, TransferKind::NoData, "DEVICE_REQ_NO_DATA", |_, _| ControlFlow::Break)?; + self.execute_control_transfer( + port, + req, + TransferKind::NoData, + "DEVICE_REQ_NO_DATA", + |_, _| ControlFlow::Break, + )?; Ok(()) } fn set_configuration(&mut self, port: usize, config: u8) -> Result<()> { @@ -374,7 +422,9 @@ impl Xhci { .ok_or(Error::new(EBADF))? .slot; - self.execute_command("RESET_ENDPOINT", |trb, cycle| trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle))?; + self.execute_command("RESET_ENDPOINT", |trb, cycle| { + trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle) + })?; Ok(()) } @@ -391,7 +441,11 @@ impl Xhci { } else if speed_id.is_fullspeed() && endp_desc.is_isoch() { // bInterval has values 1-16, ranging from 1 ms to 32,768 ms. endp_desc.interval - 1 + MILLISEC_PERIODS - } else if (speed_id.is_fullspeed() || endp_desc.is_superspeed() || endp_desc.is_superspeedplus()) && (endp_desc.is_interrupt() || endp_desc.is_isoch()) { + } else if (speed_id.is_fullspeed() + || endp_desc.is_superspeed() + || endp_desc.is_superspeedplus()) + && (endp_desc.is_interrupt() || endp_desc.is_isoch()) + { // bInterval has values 1-16, but ranging from 125 µs to 4096 ms. endp_desc.interval - 1 } else { @@ -399,7 +453,11 @@ impl Xhci { 0 } } - fn endp_ctx_max_burst(speed_id: &ProtocolSpeed, dev_desc: &DevDesc, endp_desc: &EndpDesc) -> u8 { + fn endp_ctx_max_burst( + speed_id: &ProtocolSpeed, + dev_desc: &DevDesc, + endp_desc: &EndpDesc, + ) -> u8 { if speed_id.is_highspeed() && (endp_desc.is_interrupt() || endp_desc.is_isoch()) { assert_eq!(dev_desc.major_version(), 2); ((endp_desc.max_packet_size & 0x0C00) >> 11) as u8 @@ -408,13 +466,18 @@ impl Xhci { } else { 0 } - } fn endp_ctx_max_packet_size(endp_desc: &EndpDesc) -> u16 { // TODO: Control endpoint? Encoding? endp_desc.max_packet_size & 0x07FF } - fn endp_ctx_max_esit_payload(speed_id: &ProtocolSpeed, dev_desc: &DevDesc, endp_desc: &EndpDesc, max_packet_size: u16, max_burst_size: u8) -> u32 { + fn endp_ctx_max_esit_payload( + speed_id: &ProtocolSpeed, + dev_desc: &DevDesc, + endp_desc: &EndpDesc, + max_packet_size: u16, + max_burst_size: u8, + ) -> u32 { const KIB: u32 = 1024; if dev_desc.major_version() == 2 && endp_desc.is_periodic() { @@ -427,7 +490,9 @@ impl Xhci { 64 } else if speed_id.is_fullspeed() && endp_desc.is_isoch() { 1 * KIB - } else if (speed_id.is_highspeed() && (endp_desc.is_interrupt() || endp_desc.is_isoch())) || endp_desc.is_superspeed() && endp_desc.is_interrupt() { + } else if (speed_id.is_highspeed() && (endp_desc.is_interrupt() || endp_desc.is_isoch())) + || endp_desc.is_superspeed() && endp_desc.is_interrupt() + { 3 * KIB } else if endp_desc.is_superspeed() && endp_desc.is_isoch() { 48 * KIB @@ -444,13 +509,21 @@ impl Xhci { self.port_states.get_mut(&port).ok_or(Error::new(EBADF)) } fn endpoint_state_mut(&mut self, port: usize, endp_num: u8) -> Result<&mut EndpointState> { - self.port_state_mut(port)?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF)) + self.port_state_mut(port)? + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADF)) } fn input_context(&mut self, port: usize) -> Result<&mut Dma> { Ok(&mut self.port_state_mut(port)?.input_context) } - fn endp_ctx(&mut self, port: usize, endp_num_xhc: u8) -> Result<&mut super::context::EndpointContext> { - Ok(self.input_context(port)? + fn endp_ctx( + &mut self, + port: usize, + endp_num_xhc: u8, + ) -> Result<&mut super::context::EndpointContext> { + Ok(self + .input_context(port)? .device .endpoints .get_mut(endp_num_xhc as usize - 1) @@ -463,10 +536,22 @@ impl Xhci { Ok(&self.dev_desc(port)?.config_descs) } fn config_desc(&self, port: usize, desc: u8) -> Result<&ConfDesc> { - Ok(self.config_descs(port)?.get(usize::from(desc)).ok_or(Error::new(EBADF))?) + Ok(self + .config_descs(port)? + .get(usize::from(desc)) + .ok_or(Error::new(EBADF))?) } fn endp_descs(&self, port: usize, config_desc: u8, if_desc: u8) -> Result<&[EndpDesc]> { - Ok(&self.port_state(port)?.dev_desc.config_descs.get(usize::from(config_desc)).ok_or(Error::new(EIO))?.interface_descs.get(usize::from(if_desc)).ok_or(Error::new(EIO))?.endpoints) + Ok(&self + .port_state(port)? + .dev_desc + .config_descs + .get(usize::from(config_desc)) + .ok_or(Error::new(EIO))? + .interface_descs + .get(usize::from(if_desc)) + .ok_or(Error::new(EIO))? + .endpoints) } fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> { let mut req: ConfigureEndpointsReq = @@ -485,22 +570,28 @@ impl Xhci { } let (endp_desc_count, new_context_entries) = { - let endpoints = self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?; + let endpoints = + self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?; if endpoints.len() >= 31 { return Err(Error::new(EIO)); } - (endpoints.len(), (match endpoints.last() { - Some(l) => Self::endp_num_to_dci(endpoints.len() as u8, l), - None => 1, - }) + 1) + ( + endpoints.len(), + (match endpoints.last() { + Some(l) => Self::endp_num_to_dci(endpoints.len() as u8, l), + None => 1, + }) + 1, + ) }; let lec = self.cap.lec(); let max_psa_size = self.cap.max_psa_size(); let port_speed_id = self.ports[port].speed(); - let speed_id: &ProtocolSpeed = self.lookup_psiv(port as u8, port_speed_id).ok_or(Error::new(EIO))?; + let speed_id: &ProtocolSpeed = self + .lookup_psiv(port as u8, port_speed_id) + .ok_or(Error::new(EIO))?; { let input_context = self.input_context(port)?; @@ -526,11 +617,11 @@ impl Xhci { ); } - for endp_idx in 0..endp_desc_count as u8 { let endp_num = endp_idx + 1; - let endpoints = self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?; + let endpoints = + self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?; let dev_desc = self.dev_desc(port)?; let endp_desc = endpoints.get(endp_idx as usize).ok_or(Error::new(EIO))?; @@ -554,7 +645,13 @@ impl Xhci { let max_packet_size = Self::endp_ctx_max_packet_size(endp_desc); let max_burst_size = Self::endp_ctx_max_burst(speed_id, dev_desc, endp_desc); - let max_esit_payload = Self::endp_ctx_max_esit_payload(speed_id, dev_desc, endp_desc, max_packet_size, max_burst_size); + let max_esit_payload = Self::endp_ctx_max_esit_payload( + speed_id, + dev_desc, + endp_desc, + max_packet_size, + max_burst_size, + ); let max_esit_payload_lo = max_esit_payload as u16; let max_esit_payload_hi = ((max_esit_payload & 0x00FF_0000) >> 16) as u8; @@ -594,7 +691,10 @@ impl Xhci { ); port_state.endpoint_states.insert( endp_num, - EndpointState { transfer: super::RingOrStreams::Streams(array), driver_if_state: EndpIfState::Init }, + EndpointState { + transfer: super::RingOrStreams::Streams(array), + driver_if_state: EndpIfState::Init, + }, ); array_ptr @@ -609,7 +709,10 @@ impl Xhci { ); port_state.endpoint_states.insert( endp_num, - EndpointState { transfer: super::RingOrStreams::Ring(ring), driver_if_state: EndpIfState::Init }, + EndpointState { + transfer: super::RingOrStreams::Ring(ring), + driver_if_state: EndpIfState::Init, + }, ); ring_ptr }; @@ -625,7 +728,7 @@ impl Xhci { | u32::from(primary_streams) << 10 | u32::from(linear_stream_array) << 15 | u32::from(interval) << 16 - | u32::from(max_esit_payload_hi) << 24 + | u32::from(max_esit_payload_hi) << 24, ); endp_ctx.b.write( max_error_count << 1 @@ -638,15 +741,16 @@ impl Xhci { endp_ctx.trl.write(ring_ptr as u32); endp_ctx.trh.write((ring_ptr >> 32) as u32); - endp_ctx.c.write( - u32::from(avg_trb_len) - | (u32::from(max_esit_payload_lo) << 16) - ); + endp_ctx + .c + .write(u32::from(avg_trb_len) | (u32::from(max_esit_payload_lo) << 16)); } let slot = self.port_state(port)?.slot; let input_context_physical = self.input_context(port)?.physical(); - self.execute_command("CONFIGURE_ENDPOINT", |trb, cycle| trb.configure_endpoint(slot, input_context_physical, cycle)); + self.execute_command("CONFIGURE_ENDPOINT", |trb, cycle| { + trb.configure_endpoint(slot, input_context_physical, cycle) + }); // Tell the device about this configuration. let configuration_value = self.config_desc(port, req.config_desc)?.configuration_value; @@ -693,23 +797,29 @@ impl Xhci { // TODO: Wrap DCIs and driver-level endp_num into distinct types, due to the high chance of // mixing the two up. fn endp_num_to_dci(endp_num: u8, desc: &EndpDesc) -> u8 { - if endp_num == 0 { unreachable!("EndpDesc cannot be obtained from the default control endpoint") } + if endp_num == 0 { + unreachable!("EndpDesc cannot be obtained from the default control endpoint") + } if desc.is_control() || desc.direction() == EndpDirection::In { endp_num * 2 + 1 } else if desc.direction() == EndpDirection::Out { endp_num * 2 - } else { unreachable!() } + } else { + unreachable!() + } } fn endp_desc(&self, port_num: usize, endp_num: u8) -> Result<&EndpDesc> { - Ok(self.endp_descs(port_num, 0, 0)?.get(usize::from(endp_num) - 1).ok_or(Error::new(EBADF))?) + Ok(self + .endp_descs(port_num, 0, 0)? + .get(usize::from(endp_num) - 1) + .ok_or(Error::new(EBADF))?) } fn endp_doorbell(endp_num: u8, desc: &EndpDesc, stream_id: u16) -> u32 { let db_target = Self::endp_num_to_dci(endp_num, desc); let db_task_id: u16 = stream_id; - (u32::from(db_task_id) << 16) - | u32::from(db_target) + (u32::from(db_task_id) << 16) | u32::from(db_target) } // TODO: Rename DeviceReqData to something more general. fn transfer( @@ -724,11 +834,17 @@ impl Xhci { // UB. let dma_buffer = match buf { DeviceReqData::Out(sbuf) => { + if sbuf.is_empty() { + return Err(Error::new(EINVAL)); + } let mut dma_buffer = unsafe { Dma::<[u8]>::zeroed_unsized(sbuf.len()) }?; dma_buffer.copy_from_slice(sbuf); Some(dma_buffer) } DeviceReqData::In(ref dbuf) => { + if dbuf.is_empty() { + return Err(Error::new(EINVAL)); + } Some(unsafe { Dma::<[u8]>::zeroed_unsized(dbuf.len()) }?) } DeviceReqData::NoData => None, @@ -760,48 +876,73 @@ impl Xhci { return Err(Error::new(EBADF)); } - // TODO: Scatter-gather transfers, possibly allowing >64KiB sizes. - let len = u16::try_from(buf.len()).or(Err(Error::new(ENOSYS)))?; let max_packet_size = endp_desc.max_packet_size; + let max_transfer_size = 65536u32; let (buffer, idt, estimated_td_size) = { - let (buffer, idt) = if len <= 8 && max_packet_size >= 8 && direction != EndpDirection::In { - buf.map_buf(|sbuf| { - let mut bytes = [0u8; 8]; - bytes[..len as usize].copy_from_slice(&sbuf[..len as usize]); - // FIXME: little endian, right? - (u64::from_le_bytes(bytes), true) - }) - .unwrap_or((0, false)) - } else { - ( - dma_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, - false, + let (buffer, idt) = + if buf.len() <= 8 && max_packet_size >= 8 && direction != EndpDirection::In { + buf.map_buf(|sbuf| { + let mut bytes = [0u8; 8]; + bytes[..buf.len()].copy_from_slice(&sbuf[..buf.len()]); + // FIXME: little endian, right? + (u64::from_le_bytes(bytes), true) + }) + .unwrap_or((0, false)) + } else { + ( + dma_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, + false, + ) + }; + let estimated_td_size = cmp::min( + u8::try_from( + div_round_up(buf.len(), max_transfer_size as usize) * mem::size_of::(), ) - }; - let estimated_td_size = mem::size_of::() as u8; // one trb per td + .ok() + .unwrap_or(0x1F), + 0x1F, + ); // one trb per td (buffer, idt, estimated_td_size) }; let stream_id = 1u16; - let event = self.execute_transfer(port_num, endp_num, stream_id, "CUSTOM_TRANSFER", |trb, cycle| { - trb.normal( - buffer, - len, - cycle, - estimated_td_size, - 0, - false, - true, - false, - true, - idt, - false, - ); - ControlFlow::Break - })?; - let bytes_transferred = u32::from(len) - event.transfer_length(); + let mut bytes_left = buf.len(); + + let event = self.execute_transfer( + port_num, + endp_num, + stream_id, + "CUSTOM_TRANSFER", + |trb, cycle| { + let len = cmp::min(bytes_left, max_transfer_size as usize) as u32; + + trb.normal( + buffer, + len, + cycle, + estimated_td_size, + 0, + false, + true, + false, + true, + idt, + false, + ); + + bytes_left -= len as usize; + + if bytes_left != 0 { + ControlFlow::Continue + } else { + ControlFlow::Break + } + }, + )?; + + let bytes_transferred = buf.len() as u32 - event.transfer_length(); if let DeviceReqData::In(dbuf) = &mut buf { dbuf.copy_from_slice(&*dma_buffer.as_ref().unwrap()); @@ -876,7 +1017,8 @@ impl Xhci { let (bos_desc, bos_data) = dev.get_bos()?; let supports_superspeed = usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeed()); - let supports_superspeedplus = usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeedplus()); + let supports_superspeedplus = + usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeedplus()); let config_descs = (0..raw_dd.configurations) .map(|index| -> Result<_> { @@ -997,10 +1139,21 @@ impl Xhci { // be better. Maybe something simple like bincode could be used, if a custom binary struct // is too much overkill. - self.execute_control_transfer(port_num, setup, transfer_kind, "CUSTOM_DEVICE_REQ", |trb, cycle| { - trb.data(data_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0), setup.length, transfer_kind == TransferKind::In, cycle); - ControlFlow::Break - })?; + self.execute_control_transfer( + port_num, + setup, + transfer_kind, + "CUSTOM_DEVICE_REQ", + |trb, cycle| { + trb.data( + data_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0), + setup.length, + transfer_kind == TransferKind::In, + cycle, + ); + ControlFlow::Break + }, + )?; Ok(()) } fn port_req_init_st(&mut self, port_num: usize, req: &PortReq) -> Result { @@ -1450,8 +1603,8 @@ impl SchemeMut for Xhci { &mut Handle::Endpoint(port_num, endp_num, ref ep_file_ty) => match ep_file_ty { EndpointHandleTy::Ctl => self.on_write_endp_ctl(port_num, endp_num, buf), EndpointHandleTy::Data => self.on_write_endp_data(port_num, endp_num, buf), - _ => return Err(Error::new(EBADF)), - } + EndpointHandleTy::Root(_, _) => return Err(Error::new(EBADF)), + }, &mut Handle::PortReq(port_num, ref mut st) => { let state = std::mem::replace(st, PortReqState::Tmp); self.handle_port_req_write(fd, port_num, state, buf) @@ -1470,13 +1623,27 @@ impl SchemeMut for Xhci { } impl Xhci { pub fn get_endp_status(&mut self, port_num: usize, endp_num: u8) -> Result { - let slot = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?.slot; + let slot = self + .port_states + .get(&port_num) + .ok_or(Error::new(EBADFD))? + .slot; let endp_num_xhc = if endp_num != 0 { Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?) } else { 1 }; - let raw = self.dev_ctx.contexts.get(slot as usize).ok_or(Error::new(EBADFD))?.endpoints.get(endp_num_xhc as usize - 1).ok_or(Error::new(EBADFD))?.a.read() & super::context::ENDPOINT_CONTEXT_STATUS_MASK; + let raw = self + .dev_ctx + .contexts + .get(slot as usize) + .ok_or(Error::new(EBADFD))? + .endpoints + .get(endp_num_xhc as usize - 1) + .ok_or(Error::new(EBADFD))? + .a + .read() + & super::context::ENDPOINT_CONTEXT_STATUS_MASK; Ok(match raw { 0 => EndpointStatus::Disabled, 1 => EndpointStatus::Enabled, @@ -1486,9 +1653,15 @@ impl Xhci { _ => return Err(Error::new(EIO)), }) } - pub fn on_req_reset_device(&mut self, port_num: usize, endp_num: u8, clear_feature: bool) -> Result<()> { + pub fn on_req_reset_device( + &mut self, + port_num: usize, + endp_num: u8, + clear_feature: bool, + ) -> Result<()> { + dbg!(); if self.get_endp_status(port_num, endp_num)? != EndpointStatus::Halted { - return Err(Error::new(EBADF)); + return Err(Error::new(EPROTO)); } // Change the endpoint state from anything, but most likely HALTED (otherwise resetting // would be quite meaningless), to stopped. @@ -1496,28 +1669,49 @@ impl Xhci { self.restart_endpoint(port_num, endp_num)?; if clear_feature { - self.device_req_no_data(port_num, usb::Setup { - kind: 0b0000_0010, // endpoint recipient - request: 0x01, // CLEAR_FEATURE - value: 0x00, // ENDPOINT_HALT - index: 0, // TODO: interface num - length: 0, - })?; + self.device_req_no_data( + port_num, + usb::Setup { + kind: 0b0000_0010, // endpoint recipient + request: 0x01, // CLEAR_FEATURE + value: 0x00, // ENDPOINT_HALT + index: 0, // TODO: interface num + length: 0, + }, + )?; } Ok(()) } pub fn restart_endpoint(&mut self, port_num: usize, endp_num: u8) -> Result<()> { - let port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; + let port_state = self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADFD))?; let direction = if endp_num != 0 { - let endp_desc = port_state.dev_desc.config_descs.get(0).ok_or(Error::new(EIO))?.interface_descs.get(0).ok_or(Error::new(EIO))?.endpoints.get(endp_num as usize - 1).ok_or(Error::new(EBADFD))?; + let endp_desc = port_state + .dev_desc + .config_descs + .get(0) + .ok_or(Error::new(EIO))? + .interface_descs + .get(0) + .ok_or(Error::new(EIO))? + .endpoints + .get(endp_num as usize - 1) + .ok_or(Error::new(EBADFD))?; endp_desc.direction() } else { EndpDirection::Bidirectional }; - let endpoint_state: &mut EndpointState = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADFD))?; + let endpoint_state: &mut EndpointState = port_state + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADFD))?; let ring = match &mut endpoint_state.transfer { &mut super::RingOrStreams::Ring(ref mut ring) => ring, - &mut super::RingOrStreams::Streams(ref mut arr) => arr.rings.get_mut(&1).ok_or(Error::new(EBADFD))?, + &mut super::RingOrStreams::Streams(ref mut arr) => { + arr.rings.get_mut(&1).ok_or(Error::new(EBADFD))? + } }; let (cmd, cycle) = ring.next(); @@ -1529,51 +1723,130 @@ impl Xhci { self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle)?; let stream_id = 1u16; - self.dbs[slot as usize].write(Self::endp_doorbell(endp_num, self.endp_desc(port_num, endp_num)?, stream_id)); + self.dbs[slot as usize].write(Self::endp_doorbell( + endp_num, + self.endp_desc(port_num, endp_num)?, + stream_id, + )); Ok(()) } pub fn endp_direction(&self, port_num: usize, endp_num: u8) -> Result { - Ok(self.port_states.get(&port_num).ok_or(Error::new(EIO))?.dev_desc.config_descs.first().ok_or(Error::new(EIO))?.interface_descs.first().ok_or(Error::new(EIO))?.endpoints.get(endp_num as usize).ok_or(Error::new(EIO))?.direction()) + Ok(self + .port_states + .get(&port_num) + .ok_or(Error::new(EIO))? + .dev_desc + .config_descs + .first() + .ok_or(Error::new(EIO))? + .interface_descs + .first() + .ok_or(Error::new(EIO))? + .endpoints + .get(endp_num as usize) + .ok_or(Error::new(EIO))? + .direction()) } pub fn slot(&self, port_num: usize) -> Result { Ok(self.port_states.get(&port_num).ok_or(Error::new(EIO))?.slot) } - pub fn set_tr_deque_ptr(&mut self, port_num: usize, endp_num: u8, deque_ptr_and_cycle: u64) -> Result<()> { + pub fn set_tr_deque_ptr( + &mut self, + port_num: usize, + endp_num: u8, + deque_ptr_and_cycle: u64, + ) -> Result<()> { let slot = self.slot(port_num)?; let endp_num_xhc = Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?); - self.execute_command("SET_TR_DEQUEUE_POINTER", |trb, cycle| trb.set_tr_deque_ptr(deque_ptr_and_cycle, cycle, StreamContextType::PrimaryRing, 1, endp_num_xhc, slot))?; + self.execute_command("SET_TR_DEQUEUE_POINTER", |trb, cycle| { + trb.set_tr_deque_ptr( + deque_ptr_and_cycle, + cycle, + StreamContextType::PrimaryRing, + 1, + endp_num_xhc, + slot, + ) + })?; Ok(()) } - pub fn on_write_endp_ctl(&mut self, port_num: usize, endp_num: u8, buf: &[u8]) -> Result { - let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; + pub fn on_write_endp_ctl( + &mut self, + port_num: usize, + endp_num: u8, + buf: &[u8], + ) -> Result { + dbg!(); + let ep_if_state = &mut self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADF))? + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADF))? + .driver_if_state; + dbg!(); let req = serde_json::from_slice::(buf).or(Err(Error::new(EBADMSG)))?; + dbg!(); match req { XhciEndpCtlReq::Status => match ep_if_state { state @ EndpIfState::Init => *state = EndpIfState::WaitingForStatus, - _ => return Err(Error::new(EBADF)), - } - XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state { - EndpIfState::Init => self.on_req_reset_device(port_num, endp_num, !no_clear_feature)?, - _ => return Err(Error::new(EBADF)), - } - XhciEndpCtlReq::Transfer(direction) => match ep_if_state { - state @ EndpIfState::Init => if direction == XhciEndpCtlDirection::NoData { - // Yield the result directly because no bytes have to be sent or received - // beforehand. - let (completion_code, bytes_transferred) = self.transfer(port_num, endp_num - 1, DeviceReqData::NoData)?; - if bytes_transferred > 0 { return Err(Error::new(EIO)) } - let result = Self::transfer_result(completion_code, 0); - - let new_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; - *new_state = EndpIfState::WaitingForTransferResult(result) - } else { - *state = EndpIfState::WaitingForDataPipe(direction) + other => { + dbg!(other); + return Err(Error::new(EBADF)); } - _ => return Err(Error::new(EBADF)), + }, + XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state { + EndpIfState::Init => { + self.on_req_reset_device(port_num, endp_num, !no_clear_feature)? + } + other => { + dbg!(other); + return Err(Error::new(EBADF)); + } + }, + XhciEndpCtlReq::Transfer { direction, count } => match ep_if_state { + state @ EndpIfState::Init => { + if direction == XhciEndpCtlDirection::NoData { + dbg!(); + // Yield the result directly because no bytes have to be sent or received + // beforehand. + let (completion_code, bytes_transferred) = + self.transfer(port_num, endp_num - 1, DeviceReqData::NoData)?; + if bytes_transferred > 0 { + return Err(Error::new(EIO)); + } + let result = Self::transfer_result(completion_code, 0); + dbg!(); + let new_state = &mut self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADF))? + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADF))? + .driver_if_state; + *new_state = EndpIfState::WaitingForTransferResult(result) + } else { + dbg!(); + *state = EndpIfState::WaitingForDataPipe { + direction, + bytes_to_transfer: count, + bytes_transferred: 0, + }; + } + } + other => { + dbg!(other); + return Err(Error::new(EBADF)); + } + }, + other => { + dbg!(other); + return Err(Error::new(EBADF)); } - _ => return Err(Error::new(ENOSYS)), } Ok(buf.len()) } @@ -1588,25 +1861,61 @@ impl Xhci { PortTransferStatus::Unknown } } - pub fn on_write_endp_data(&mut self, port_num: usize, endp_num: u8, buf: &[u8]) -> Result { - { - let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; - match ep_if_state { - state @ EndpIfState::WaitingForDataPipe(XhciEndpCtlDirection::Out) => (), - _ => return Err(Error::new(EBADF)), - } - } - { - let (completion_code, bytes_transferred) = self.transfer_write(port_num, endp_num - 1, buf)?; - let result = Self::transfer_result(completion_code, bytes_transferred); + pub fn on_write_endp_data( + &mut self, + port_num: usize, + endp_num: u8, + buf: &[u8], + ) -> Result { + let ep_if_state = &mut self.endpoint_state_mut(port_num, endp_num)?.driver_if_state; + match ep_if_state { + &mut EndpIfState::WaitingForDataPipe { + direction: XhciEndpCtlDirection::Out, + bytes_to_transfer: total_bytes_to_transfer, + bytes_transferred, + } => { + if buf.len() > total_bytes_to_transfer as usize - bytes_transferred as usize { + return Err(Error::new(EINVAL)); + } + let (completion_code, some_bytes_transferred) = + self.transfer_write(port_num, endp_num - 1, buf)?; + let result = Self::transfer_result(completion_code, some_bytes_transferred); - let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; - *ep_if_state = EndpIfState::WaitingForTransferResult(result); - Ok(bytes_transferred as usize) + let ep_if_state = &mut self.endpoint_state_mut(port_num, endp_num)?.driver_if_state; + + if let &mut EndpIfState::WaitingForDataPipe { + direction: XhciEndpCtlDirection::Out, + bytes_to_transfer, + ref mut bytes_transferred, + } = ep_if_state + { + if *bytes_transferred + some_bytes_transferred == bytes_to_transfer { + *ep_if_state = EndpIfState::WaitingForTransferResult(result); + } else { + *bytes_transferred += some_bytes_transferred; + } + } else { + unreachable!() + } + Ok(some_bytes_transferred as usize) + } + _ => return Err(Error::new(EBADF)), } } - pub fn on_read_endp_ctl(&mut self, port_num: usize, endp_num: u8, buf: &mut [u8]) -> Result { - let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; + pub fn on_read_endp_ctl( + &mut self, + port_num: usize, + endp_num: u8, + buf: &mut [u8], + ) -> Result { + let ep_if_state = &mut self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADF))? + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADF))? + .driver_if_state; let res: XhciEndpCtlRes = match ep_if_state { &mut EndpIfState::Init => XhciEndpCtlRes::Idle, @@ -1615,7 +1924,7 @@ impl Xhci { *state = EndpIfState::Init; XhciEndpCtlRes::Status(self.get_endp_status(port_num, endp_num)?) } - &mut EndpIfState::WaitingForDataPipe(_) => XhciEndpCtlRes::Pending, + &mut EndpIfState::WaitingForDataPipe { .. } => XhciEndpCtlRes::Pending, &mut EndpIfState::WaitingForTransferResult(status) => { *ep_if_state = EndpIfState::Init; XhciEndpCtlRes::TransferResult(status) @@ -1626,21 +1935,70 @@ impl Xhci { serde_json::to_writer(&mut cursor, &res).or(Err(Error::new(EIO)))?; Ok(cursor.seek(io::SeekFrom::Current(0)).unwrap() as usize) } - pub fn on_read_endp_data(&mut self, port_num: usize, endp_num: u8, buf: &mut [u8]) -> Result { - { - let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; - match ep_if_state { - EndpIfState::WaitingForDataPipe(XhciEndpCtlDirection::In) => (), - _ => return Err(Error::new(EBADF)), - } - } - { - let (completion_code, bytes_transferred) = self.transfer_read(port_num, endp_num - 1, buf)?; - let result = Self::transfer_result(completion_code, bytes_transferred); + pub fn on_read_endp_data( + &mut self, + port_num: usize, + endp_num: u8, + buf: &mut [u8], + ) -> Result { + let ep_if_state = &mut self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADF))? + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADF))? + .driver_if_state; + match ep_if_state { + &mut EndpIfState::WaitingForDataPipe { + direction: XhciEndpCtlDirection::In, + bytes_transferred, + bytes_to_transfer: total_bytes_to_transfer, + } => { + if buf.len() > total_bytes_to_transfer as usize - bytes_transferred as usize { + return Err(Error::new(EINVAL)); + } - let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state; - *ep_if_state = EndpIfState::WaitingForTransferResult(result); - Ok(bytes_transferred as usize) + let (completion_code, some_bytes_transferred) = + self.transfer_read(port_num, endp_num - 1, buf)?; + let result = Self::transfer_result(completion_code, some_bytes_transferred); + + let ep_if_state = &mut self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADF))? + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADF))? + .driver_if_state; + if let &mut EndpIfState::WaitingForDataPipe { + direction: XhciEndpCtlDirection::In, + bytes_to_transfer, + ref mut bytes_transferred, + } = ep_if_state + { + if *bytes_transferred + some_bytes_transferred == bytes_to_transfer { + *ep_if_state = EndpIfState::WaitingForTransferResult(result); + } else { + *bytes_transferred += some_bytes_transferred; + } + } else { + unreachable!() + } + Ok(some_bytes_transferred as usize) + } + _ => return Err(Error::new(EBADF)), } } } +use std::ops::{Add, Div, Rem}; +fn div_round_up(a: T, b: T) -> T +where + T: Add + Div + Rem + PartialEq + From + Copy, +{ + if a % b != T::from(0u8) { + a / b + T::from(1u8) + } else { + a / b + } +} diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index 4dafc85860..d6d4cb30e0 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -213,7 +213,10 @@ impl Trb { self.set( input_ctx_ptr as u64, 0, - (u32::from(slot_id) << 24) | ((TrbType::AddressDevice as u32) << 10) | (u32::from(bsr) << 9) | u32::from(cycle), + (u32::from(slot_id) << 24) + | ((TrbType::AddressDevice as u32) << 10) + | (u32::from(bsr) << 9) + | u32::from(cycle), ); } // Synchronizes the input context endpoints with the device context endpoints, I think. @@ -241,7 +244,10 @@ impl Trb { self.set( input_ctx_ptr as u64, 0, - (u32::from(slot_id) << 24) | ((TrbType::EvaluateContext as u32) << 10) | (u32::from(bsr) << 9) | u32::from(cycle), + (u32::from(slot_id) << 24) + | ((TrbType::EvaluateContext as u32) << 10) + | (u32::from(bsr) << 9) + | u32::from(cycle), ); } pub fn reset_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, tsp: bool, cycle: bool) { @@ -257,7 +263,15 @@ impl Trb { ); } /// The deque_ptr has to contain the DCS bit (bit 0). - pub fn set_tr_deque_ptr(&mut self, deque_ptr: u64, cycle: bool, sct: StreamContextType, stream_id: u16, endp_num_xhc: u8, slot: u8) { + pub fn set_tr_deque_ptr( + &mut self, + deque_ptr: u64, + cycle: bool, + sct: StreamContextType, + stream_id: u16, + endp_num_xhc: u8, + slot: u8, + ) { assert_eq!(deque_ptr & 0xFFFF_FFFF_FFFF_FFF1, deque_ptr); assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc); @@ -298,7 +312,7 @@ impl Trb { | (u32::from(ioc) << 5) | (u32::from(ch) << 4) | (u32::from(ent) << 1) - | u32::from(cycle) + | u32::from(cycle), ); } @@ -334,7 +348,7 @@ impl Trb { pub fn normal( &mut self, buffer: u64, - len: u16, + len: u32, cycle: bool, estimated_td_size: u8, interrupter: u8, @@ -349,7 +363,7 @@ impl Trb { // NOTE: The interrupter target and no snoop flags have been omitted. self.set( buffer, - u32::from(len) | (u32::from(estimated_td_size) << 17) | (u32::from(interrupter) << 22), + len | (u32::from(estimated_td_size) << 17) | (u32::from(interrupter) << 22), u32::from(cycle) | (u32::from(ent) << 1) | (u32::from(isp) << 2)