From d82ae0a5f6e1088aff0aaa1e422b3f9604f45147 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 10 Feb 2020 23:12:11 +0100 Subject: [PATCH] Add periodic (driver interface-level) transfers. --- usbscsid/src/main.rs | 10 +- usbscsid/src/protocol/bot.rs | 99 ++++++--- usbscsid/src/protocol/mod.rs | 4 +- usbscsid/src/scsi/cmds.rs | 23 +++ xhcid/src/driver_interface.rs | 176 ++++++++++++++-- xhcid/src/xhci/capability.rs | 3 +- xhcid/src/xhci/extended.rs | 38 ++-- xhcid/src/xhci/mod.rs | 53 +++-- xhcid/src/xhci/operational.rs | 2 +- xhcid/src/xhci/scheme.rs | 375 +++++++++++++++++++++++++++------- xhcid/src/xhci/trb.rs | 63 +++++- 11 files changed, 680 insertions(+), 166 deletions(-) diff --git a/usbscsid/src/main.rs b/usbscsid/src/main.rs index 0d1be15988..0c740af631 100644 --- a/usbscsid/src/main.rs +++ b/usbscsid/src/main.rs @@ -1,6 +1,6 @@ use std::env; -use xhcid_interface::{ConfigureEndpointsReq, XhciClientHandle}; +use xhcid_interface::{ConfigureEndpointsReq, DeviceReqData, XhciClientHandle}; pub mod protocol; pub mod scsi; @@ -39,14 +39,16 @@ fn main() { let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc).expect("Failed to setup protocol"); - let get_info = { + /*let get_info = { // Max number of bytes that can be recieved from a "REPORT IDENTIFYING INFORMATION" // command. let alloc_len = 256; let info_ty = scsi::cmds::ReportIdInfoInfoTy::IdentInfoSupp; let control = 0; // TODO: NACA? scsi::cmds::ReportIdentInfo::new(alloc_len, info_ty, control) - }; + };*/ + let inquiry = scsi::cmds::Inquiry::new(false, 0, 36, 0); + let mut buffer = [0u8; 36]; use protocol::Protocol; - protocol.send_command(unsafe { plain::as_bytes(&get_info) }).expect("Failed to send command"); + protocol.send_command(unsafe { plain::as_bytes(&inquiry) }, DeviceReqData::In(&mut buffer)).expect("Failed to send command"); } diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index d4f3021457..86065ac128 100644 --- a/usbscsid/src/protocol/bot.rs +++ b/usbscsid/src/protocol/bot.rs @@ -4,7 +4,7 @@ use std::io::prelude::*; use std::{io, slice}; use thiserror::Error; -use xhcid_interface::{ConfDesc, DeviceReqData, EndpDirection, EndpointStatus, IfDesc, PortReqDirection, PortReqTy, PortReqRecipient, XhciClientHandle, XhciClientHandleError, XhciEndpStatusHandle}; +use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpStatusHandle, XhciEndpTransferHandle}; use super::{Protocol, ProtocolError}; @@ -25,6 +25,28 @@ pub struct CommandBlockWrapper { 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 { + let mut command_block = [0u8; 16]; + if cb.len() > 16 { + return Err(ProtocolError::TooLargeCommandBlock(cb.len())); + } + + command_block[..cb.len()].copy_from_slice(&cb); + Ok(Self { + signature: CBW_SIGNATURE, + tag, + data_transfer_len, + flags: match direction { + EndpBinaryDirection::Out => 0, + EndpBinaryDirection::In => 1, + } << CBW_FLAGS_DIRECTION_SHIFT, + lun, + cb_len: cb.len() as u8, + command_block, + }) + } +} unsafe impl plain::Plain for CommandBlockWrapper {} pub const CSW_SIGNATURE: u32 = 0x53425355; @@ -47,10 +69,16 @@ pub struct CommandStatusWrapper { } unsafe impl plain::Plain for CommandStatusWrapper {} +impl CommandStatusWrapper { + pub fn is_valid(&self) -> bool { + self.signature == CSW_SIGNATURE + } +} + pub struct BulkOnlyTransport<'a> { handle: &'a XhciClientHandle, - bulk_in: File, - bulk_out: File, + bulk_in: XhciEndpTransferHandle, + bulk_out: XhciEndpTransferHandle, bulk_in_status: XhciEndpStatusHandle, bulk_out_status: XhciEndpStatusHandle, bulk_in_num: u8, @@ -59,6 +87,8 @@ pub struct BulkOnlyTransport<'a> { current_tag: u32, } +pub const FEATURE_ENDPOINT_HALT: u16 = 0; + impl<'a> BulkOnlyTransport<'a> { pub fn init(handle: &'a XhciClientHandle, config_desc: &ConfDesc, if_desc: &IfDesc) -> Result { let endpoints = &if_desc.endpoints; @@ -81,11 +111,13 @@ impl<'a> BulkOnlyTransport<'a> { current_tag: 0, }) } - fn recover_from_stall(&mut self) -> Result<(), ProtocolError> { + fn clear_stall(&mut self, endp_num: u8) -> Result<(), XhciClientHandleError> { + self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(endp_num), FEATURE_ENDPOINT_HALT) + } + fn reset_recovery(&mut self) -> Result<(), ProtocolError> { bulk_only_mass_storage_reset(self.handle, 0)?; - const ENDPOINT_HALT: u16 = 0; - self.handle.clear_feature(PortReqRecipient::Endpoint, self.bulk_in_num.into(), ENDPOINT_HALT)?; - self.handle.clear_feature(PortReqRecipient::Endpoint, self.bulk_out_num.into(), ENDPOINT_HALT)?; + self.clear_stall(self.bulk_in_num.into())?; + self.clear_stall(self.bulk_out_num.into())?; if self.bulk_in_status.current_status()? == EndpointStatus::Halted || self.bulk_out_status.current_status()? == EndpointStatus::Halted { return Err(ProtocolError::RecoveryFailed) @@ -95,7 +127,7 @@ impl<'a> BulkOnlyTransport<'a> { } impl<'a> Protocol for BulkOnlyTransport<'a> { - fn send_command(&mut self, cb: &[u8]) -> Result<(), ProtocolError> { + fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result<(), ProtocolError> { dbg!(self.bulk_in_status.current_status()?); dbg!(self.bulk_out_status.current_status()?); self.current_tag += 1; @@ -110,34 +142,55 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { let cbw = CommandBlockWrapper { signature: CBW_SIGNATURE, tag, - data_transfer_len: 256, // TODO + data_transfer_len: data.len() as u32, lun: 0, // TODO - flags: 1 << 7, // TODO + flags: u8::from(data.direction() == PortReqDirection::DeviceToHost) << 7, cb_len: cb.len().try_into().or(Err(ProtocolError::TooLargeCommandBlock(cb.len())))?, command_block, }; - let bytes_written = self.bulk_out.write(unsafe { plain::as_bytes(&cbw) })?; - if bytes_written != 31 { - panic!("invalid number of cbw bytes written"); + match self.bulk_out.transfer_write(unsafe { plain::as_bytes(&cbw) })? { + PortTransferStatus::ShortPacket(31) => (), + PortTransferStatus::Stalled => { + panic!("bulk out endpoint stalled when sending CBW"); + } + _ => panic!("invalid number of CBW bytes written; expected a short packed of length 31 (0x1F)"), } - let mut buffer = [0u8; 256]; - let bytes_read = self.bulk_in.read(&mut buffer)?; - if bytes_read != 256 { - panic!("invalid number of bytes read"); - } - println!("{}", base64::encode(&buffer[..])); + + match data { + 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::Stalled => { + println!("bulk in endpoint stalled when reading data"); + self.clear_stall(self.bulk_in_num)?; + } + PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))), + }; + println!("{}", base64::encode(&buffer[..])); + } + DeviceReqData::Out(ref buffer) => todo!(), + DeviceReqData::NoData => todo!(), + }; let mut csw = CommandStatusWrapper::default(); - let csw_bytes_read = self.bulk_in.read(unsafe { plain::as_mut_bytes(&mut csw) })?; - if csw_bytes_read != 13 { - panic!("invalid number of csw bytes read"); + match self.bulk_in.transfer_read(unsafe { plain::as_mut_bytes(&mut csw) })? { + PortTransferStatus::ShortPacket(13) => (), + PortTransferStatus::Stalled => { + println!("bulk in endpoint stalled when reading CSW"); + self.clear_stall(self.bulk_in_num)?; + } + _ => panic!("invalid number of CSW bytes read; expected a short packet of length 13 (0xD)"), + }; + + if !csw.is_valid() { + self.reset_recovery()?; } dbg!(csw); if self.bulk_in_status.current_status()? == EndpointStatus::Halted || self.bulk_out_status.current_status()? == EndpointStatus::Halted { println!("Trying to recover from stall"); - self.recover_from_stall()?; dbg!(self.bulk_in_status.current_status()?, self.bulk_out_status.current_status()?); } diff --git a/usbscsid/src/protocol/mod.rs b/usbscsid/src/protocol/mod.rs index 87a0b8eb69..569505199f 100644 --- a/usbscsid/src/protocol/mod.rs +++ b/usbscsid/src/protocol/mod.rs @@ -1,7 +1,7 @@ use std::io; use thiserror::Error; -use xhcid_interface::{DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError}; +use xhcid_interface::{DeviceReqData, DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError}; #[derive(Debug, Error)] pub enum ProtocolError { @@ -19,7 +19,7 @@ pub enum ProtocolError { } pub trait Protocol { - fn send_command(&mut self, command: &[u8]) -> Result<(), ProtocolError>; + fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result<(), ProtocolError>; } /// Bulk-only transport diff --git a/usbscsid/src/scsi/cmds.rs b/usbscsid/src/scsi/cmds.rs index 476ed5b758..582d2dd628 100644 --- a/usbscsid/src/scsi/cmds.rs +++ b/usbscsid/src/scsi/cmds.rs @@ -122,3 +122,26 @@ pub struct OneCommandParam { pub a: u8, // TODO } + +#[repr(packed)] +pub struct Inquiry { + pub opcode: u8, + /// bits 7:2 are reserved, bit 1 (CMDDT) is obsolete, bit 0 is EVPD + pub evpd: u8, + pub page_code: u8, + /// little endian + pub alloc_len: u16, + pub control: u8, +} + +impl Inquiry { + pub fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self { + Self { + opcode: Opcode::Inquiry as u8, + evpd: evpd as u8, + page_code, + alloc_len: u16::to_le(alloc_len), + control, + } + } +} diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index 48c9c2fb0e..e029e4ab94 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -63,6 +63,21 @@ pub enum EndpDirection { In, Bidirectional, } + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum EndpBinaryDirection { + Out, + In, +} +impl From for EndpDirection { + fn from(b: EndpBinaryDirection) -> Self { + match b { + EndpBinaryDirection::In => Self::In, + EndpBinaryDirection::Out => Self::Out, + } + } +} + impl From for EndpDirection { fn from(d: PortReqDirection) -> Self { match d { @@ -185,7 +200,7 @@ pub struct PortReq { pub length: u16, pub transfers_data: bool, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum PortReqDirection { HostToDevice, DeviceToHost, @@ -230,7 +245,7 @@ impl PortState { } #[derive(Debug, Error)] #[error("invalid input")] -pub struct Invalid; +pub struct Invalid(pub &'static str); impl str::FromStr for PortState { type Err = Invalid; @@ -241,7 +256,7 @@ impl str::FromStr for PortState { "default" => Self::Default, "addressed" => Self::Addressed, "configured" => Self::Configured, - _ => return Err(Invalid), + _ => return Err(Invalid("read reserved port state")), }) } } @@ -256,6 +271,14 @@ pub enum EndpointStatus { Error, } +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum PortTransferStatus { + Success, + ShortPacket(u16), + Stalled, + Unknown, +} + impl EndpointStatus { pub fn as_str(&self) -> &'static str { match self { @@ -278,7 +301,7 @@ impl str::FromStr for EndpointStatus { "halted" => Self::Halted, "stopped" => Self::Stopped, "error" => Self::Error, - _ => return Err(Invalid), + _ => return Err(Invalid("read reserved endpoint state")), }) } } @@ -334,7 +357,9 @@ impl XhciClientHandle { let mut file = OpenOptions::new().read(false).write(true).open(path)?; let json_bytes_written = file.write(&json)?; if json_bytes_written != json.len() { - return Err(XhciClientHandleError::InvalidResponse(Invalid)); + return Err(XhciClientHandleError::InvalidResponse(Invalid( + "configure_endpoints didn't read as many bytes as were requested", + ))); } Ok(()) } @@ -356,14 +381,35 @@ impl XhciClientHandle { num: u8, ) -> result::Result { let path = format!("{}:port{}/endpoints/{}/status", self.scheme, self.port, num); - Ok(XhciEndpStatusHandle(OpenOptions::new().read(true).write(false).create(false).open(path)?)) + Ok(XhciEndpStatusHandle( + OpenOptions::new() + .read(true) + .write(false) + .create(false) + .open(path)?, + )) } - pub fn open_endpoint(&self, num: u8, direction: PortReqDirection) -> result::Result { - let path = format!("{}:port{}/endpoints/{}/transfer", self.scheme, self.port, num); - Ok(match direction { - PortReqDirection::HostToDevice => OpenOptions::new().read(false).write(true).create(false).open(path)?, - PortReqDirection::DeviceToHost => OpenOptions::new().read(true).write(false).create(false).open(path)?, - }) + pub fn open_endpoint( + &self, + num: u8, + direction: PortReqDirection, + ) -> result::Result { + let path = format!( + "{}:port{}/endpoints/{}/transfer", + self.scheme, self.port, num + ); + Ok(XhciEndpTransferHandle(match direction { + PortReqDirection::HostToDevice => OpenOptions::new() + .read(false) + .write(true) + .create(false) + .open(path)?, + PortReqDirection::DeviceToHost => OpenOptions::new() + .read(true) + .write(false) + .create(false) + .open(path)?, + })) } pub fn device_request<'a>( &self, @@ -374,7 +420,8 @@ impl XhciClientHandle { index: u16, data: DeviceReqData<'a>, ) -> result::Result<(), XhciClientHandleError> { - let length = u16::try_from(data.len()).or(Err(XhciClientHandleError::TransferBufTooLarge(data.len())))?; + let length = u16::try_from(data.len()) + .or(Err(XhciClientHandleError::TransferBufTooLarge(data.len())))?; let req = PortReq { direction: data.direction(), @@ -393,7 +440,9 @@ impl XhciClientHandle { let json_bytes_written = file.write(&json)?; if json_bytes_written != json.len() { - return Err(XhciClientHandleError::InvalidResponse(Invalid)); + return Err(XhciClientHandleError::InvalidResponse(Invalid( + "device_request didn't return the same number of bytes as were written", + ))); } match data { @@ -401,25 +450,55 @@ impl XhciClientHandle { let bytes_read = file.read(buf)?; if bytes_read != buf.len() { - return Err(XhciClientHandleError::InvalidResponse(Invalid)); + return Err(XhciClientHandleError::InvalidResponse(Invalid( + "device_request didn't transfer (host2dev) all bytes", + ))); } } DeviceReqData::Out(buf) => { let bytes_read = file.write(&buf)?; if bytes_read != buf.len() { - return Err(XhciClientHandleError::InvalidResponse(Invalid)); + return Err(XhciClientHandleError::InvalidResponse(Invalid( + "device_request didn't transfer (dev2host) all bytes", + ))); } } DeviceReqData::NoData => (), } Ok(()) } - pub fn get_descriptor(&self, recipient: PortReqRecipient, ty: u8, idx: u8, windex: u16, buffer: &mut [u8]) -> result::Result<(), XhciClientHandleError> { - self.device_request(PortReqTy::Standard, recipient, 0x06, (u16::from(ty) << 8) | u16::from(idx), windex, DeviceReqData::In(buffer)) + pub fn get_descriptor( + &self, + recipient: PortReqRecipient, + ty: u8, + idx: u8, + windex: u16, + buffer: &mut [u8], + ) -> result::Result<(), XhciClientHandleError> { + self.device_request( + PortReqTy::Standard, + recipient, + 0x06, + (u16::from(ty) << 8) | u16::from(idx), + windex, + DeviceReqData::In(buffer), + ) } - pub fn clear_feature(&self, recipient: PortReqRecipient, index: u16, feature_sel: u16) -> result::Result<(), XhciClientHandleError> { - self.device_request(PortReqTy::Standard, recipient, 0x01, feature_sel, index, DeviceReqData::NoData) + pub fn clear_feature( + &self, + recipient: PortReqRecipient, + index: u16, + feature_sel: u16, + ) -> result::Result<(), XhciClientHandleError> { + self.device_request( + PortReqTy::Standard, + recipient, + 0x01, + feature_sel, + index, + DeviceReqData::NoData, + ) } } @@ -431,8 +510,61 @@ impl XhciEndpStatusHandle { self.0.seek(io::SeekFrom::Start(0))?; let mut status_buf = [0u8; 16]; let len = self.0.read(&mut status_buf)?; - let status = std::str::from_utf8(&status_buf[..len]).or(Err(XhciClientHandleError::InvalidResponse(Invalid)))?; - Ok(status.parse::().or(Err(XhciClientHandleError::InvalidResponse(Invalid)))?) + let status = std::str::from_utf8(&status_buf[..len]).or(Err( + XhciClientHandleError::InvalidResponse(Invalid("non-utf8 endpoint state")), + ))?; + Ok(status + .parse::() + .or(Err(XhciClientHandleError::InvalidResponse(Invalid( + "malformed endpoint state", + ))))?) + } + pub fn into_inner(self) -> File { + self.0 + } +} + +#[derive(Debug)] +pub struct XhciEndpTransferHandle(File); + +impl XhciEndpTransferHandle { + fn get_status( + &mut self, + requested_len: usize, + bytes_transferred: usize, + ) -> result::Result { + let mut status_buf = [0u8; 32]; + let status_bytes_read = self.0.read(&mut status_buf)?; + + let status = serde_json::from_slice(dbg!(&status_buf[..status_bytes_read]))?; + + if let PortTransferStatus::ShortPacket(len) = status { + if len as usize != bytes_transferred { + return Err(XhciClientHandleError::InvalidResponse(Invalid("xhcid gave a short packet with a different length than the bytes transferred (which should have been the same)"))); + } + } else if let PortTransferStatus::Success = status { + if requested_len != bytes_transferred { + return Err(XhciClientHandleError::InvalidResponse(Invalid("xhcid transferred fewer or more bytes than requested, but didn't return a short packed"))); + } + } + Ok(status) + } + pub fn transfer_write( + &mut self, + buffer: &[u8], + ) -> result::Result { + let bytes_transferred = self.0.write(buffer)?; + self.get_status(buffer.len(), bytes_transferred) + } + pub fn transfer_read( + &mut self, + buffer: &mut [u8], + ) -> result::Result { + let bytes_transferred = self.0.read(buffer)?; + self.get_status(buffer.len(), bytes_transferred) + } + pub fn into_inner(self) -> File { + self.0 } } diff --git a/xhcid/src/xhci/capability.rs b/xhcid/src/xhci/capability.rs index 3654534f18..59e841b24e 100644 --- a/xhcid/src/xhci/capability.rs +++ b/xhcid/src/xhci/capability.rs @@ -39,7 +39,8 @@ impl CapabilityRegs { as u8 } pub fn max_ports(&self) -> u8 { - ((self.hcs_params1.read() & HCS_PARAMS1_MAX_PORTS_MASK) >> HCS_PARAMS1_MAX_PORTS_SHIFT) as u8 + ((self.hcs_params1.read() & HCS_PARAMS1_MAX_PORTS_MASK) >> HCS_PARAMS1_MAX_PORTS_SHIFT) + as u8 } pub fn max_slots(&self) -> u8 { (self.hcs_params1.read() & HCS_PARAMS1_MAX_SLOTS_MASK) as u8 diff --git a/xhcid/src/xhci/extended.rs b/xhcid/src/xhci/extended.rs index d444810b86..a128a778b4 100644 --- a/xhcid/src/xhci/extended.rs +++ b/xhcid/src/xhci/extended.rs @@ -8,9 +8,7 @@ pub struct ExtendedCapabilitiesIter { } impl ExtendedCapabilitiesIter { pub unsafe fn new(base: *const u8) -> Self { - Self { - base, - } + Self { base } } } impl Iterator for ExtendedCapabilitiesIter { @@ -26,7 +24,11 @@ impl Iterator for ExtendedCapabilitiesIter { let next_rel = u16::from(next_rel_in_dwords) << 2; - self.base = if next_rel != 0 { self.base.offset(next_rel as isize) } else { ptr::null() }; + self.base = if next_rel != 0 { + self.base.offset(next_rel as isize) + } else { + ptr::null() + }; Some((current, capability_id)) } @@ -109,9 +111,7 @@ pub enum Lp { impl ProtocolSpeed { pub const fn from_raw(raw: u32) -> Self { - Self { - a: Mmio::from(raw), - } + Self { a: Mmio::from(raw) } } pub fn is_lowspeed(&self) -> bool { self.psim() == 1500 && self.psie() == Psie::Kbps @@ -196,11 +196,17 @@ pub const SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT: u8 = 0; impl SupportedProtoCap { pub unsafe fn protocol_speeds(&self) -> &[ProtocolSpeed] { - slice::from_raw_parts(&self.protocol_speeds as *const u8 as *const _, self.psic() as usize) + slice::from_raw_parts( + &self.protocol_speeds as *const u8 as *const _, + self.psic() as usize, + ) } pub unsafe fn protocol_speeds_mut(&mut self) -> &mut [ProtocolSpeed] { // XXX: Variance really is annoying sometimes. - slice::from_raw_parts_mut(&self.protocol_speeds as *const u8 as *mut u8 as *mut _, self.psic() as usize) + slice::from_raw_parts_mut( + &self.protocol_speeds as *const u8 as *mut u8 as *mut _, + self.psic() as usize, + ) } pub fn rev_minor(&self) -> u8 { ((self.a.read() & SUPP_PROTO_CAP_REV_MIN_MASK) >> SUPP_PROTO_CAP_REV_MIN_SHIFT) as u8 @@ -213,10 +219,12 @@ impl SupportedProtoCap { u32::to_le_bytes(self.b.read()) } pub fn compat_port_offset(&self) -> u8 { - ((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_OFF_MASK) >> SUPP_PROTO_CAP_COMPAT_PORT_OFF_SHIFT) as u8 + ((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_OFF_MASK) + >> SUPP_PROTO_CAP_COMPAT_PORT_OFF_SHIFT) as u8 } pub fn compat_port_count(&self) -> u8 { - ((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_CNT_MASK) >> SUPP_PROTO_CAP_COMPAT_PORT_CNT_SHIFT) as u8 + ((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_CNT_MASK) + >> SUPP_PROTO_CAP_COMPAT_PORT_CNT_SHIFT) as u8 } pub fn compat_port_range(&self) -> Range { self.compat_port_offset()..self.compat_port_offset() + self.compat_port_count() @@ -229,12 +237,12 @@ impl SupportedProtoCap { ((self.c.read() & SUPP_PROTO_CAP_PSIC_MASK) >> SUPP_PROTO_CAP_PSIC_SHIFT) as u8 } pub fn proto_slot_ty(&self) -> u8 { - ((self.d.read() & SUPP_PROTO_CAP_PORT_SLOT_TYPE_MASK) >> SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT) as u8 + ((self.d.read() & SUPP_PROTO_CAP_PORT_SLOT_TYPE_MASK) + >> SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT) as u8 } } impl fmt::Debug for SupportedProtoCap { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("SupportedProtoCap") .field("rev_minor", &self.rev_minor()) .field("rev_major", &self.rev_major()) @@ -244,7 +252,9 @@ impl fmt::Debug for SupportedProtoCap { .field("proto_defined", &self.proto_defined()) .field("psic", &self.psic()) .field("proto_slot_ty", &self.proto_slot_ty()) - .field("proto_speeds", unsafe { &self.protocol_speeds().to_owned() }) + .field("proto_speeds", unsafe { + &self.protocol_speeds().to_owned() + }) .finish() } } diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index a74b7b0c26..6f3cd5d157 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -351,7 +351,11 @@ impl Xhci { { input.add_context.write(1 << 1 | 1); // Enable the slot (zeroth bit) and the control endpoint (first bit). - input.device.slot.a.write((1 << 27) | (u32::from(speed) << 20)); // FIXME: The speed field, bits 23:20, is deprecated. + input + .device + .slot + .a + .write((1 << 27) | (u32::from(speed) << 20)); // FIXME: The speed field, bits 23:20, is deprecated. input.device.slot.b.write(((i as u32 + 1) & 0xFF) << 16); // control endpoint? @@ -501,18 +505,26 @@ impl Xhci { Ok(()) } pub fn capabilities_iter(&self) -> ExtendedCapabilitiesIter { - unsafe { ExtendedCapabilitiesIter::new((self.base as *mut u8).offset((self.cap.ext_caps_ptr_in_dwords() << 2) as isize)) } + unsafe { + ExtendedCapabilitiesIter::new( + (self.base as *mut u8).offset((self.cap.ext_caps_ptr_in_dwords() << 2) as isize), + ) + } } pub fn supported_protocols_iter(&self) -> impl Iterator { - self.capabilities_iter().filter_map(|(pointer, cap_num)| unsafe { - if cap_num == CapabilityId::SupportedProtocol as u8 { - Some(&*pointer.cast::().as_ptr()) - } else { - None - } - }) + self.capabilities_iter() + .filter_map(|(pointer, cap_num)| unsafe { + if cap_num == CapabilityId::SupportedProtocol as u8 { + Some(&*pointer.cast::().as_ptr()) + } else { + None + } + }) } - pub fn supported_protocol_speeds(&self, port: u8) -> Option> { + pub fn supported_protocol_speeds( + &self, + port: u8, + ) -> Option> { use extended::*; const DEFAULT_SUPP_PROTO_SPEEDS: [ProtocolSpeed; 7] = [ // Full-speed @@ -521,7 +533,7 @@ impl Xhci { | (false as u32) << PROTO_SPEED_PFD_SHIFT | (Psie::Mbps as u32) << PROTO_SPEED_PSIE_SHIFT | 12 << PROTO_SPEED_PSIM_SHIFT - | 1 << PROTO_SPEED_PSIV_SHIFT + | 1 << PROTO_SPEED_PSIV_SHIFT, ), // Low-speed ProtocolSpeed::from_raw( @@ -529,7 +541,7 @@ impl Xhci { | (false as u32) << PROTO_SPEED_PFD_SHIFT | (Psie::Kbps as u32) << PROTO_SPEED_PSIE_SHIFT | 1500 << PROTO_SPEED_PSIM_SHIFT - | 2 << PROTO_SPEED_PSIV_SHIFT + | 2 << PROTO_SPEED_PSIV_SHIFT, ), // High-speed ProtocolSpeed::from_raw( @@ -537,7 +549,7 @@ impl Xhci { | (false as u32) << PROTO_SPEED_PFD_SHIFT | (Psie::Mbps as u32) << PROTO_SPEED_PSIE_SHIFT | 480 << PROTO_SPEED_PSIM_SHIFT - | 3 << PROTO_SPEED_PSIV_SHIFT + | 3 << PROTO_SPEED_PSIV_SHIFT, ), // SuperSpeed Gen1 x1 ProtocolSpeed::from_raw( @@ -546,7 +558,7 @@ impl Xhci { | (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT | 5 << PROTO_SPEED_PSIM_SHIFT | (Lp::SuperSpeed as u32) << PROTO_SPEED_LP_SHIFT - | 4 << PROTO_SPEED_PSIV_SHIFT + | 4 << PROTO_SPEED_PSIV_SHIFT, ), // SuperSpeedPlus Gen2 x1 ProtocolSpeed::from_raw( @@ -555,7 +567,7 @@ impl Xhci { | (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT | 10 << PROTO_SPEED_PSIM_SHIFT | (Lp::SuperSpeedPlus as u32) << PROTO_SPEED_LP_SHIFT - | 5 << PROTO_SPEED_PSIV_SHIFT + | 5 << PROTO_SPEED_PSIV_SHIFT, ), // SuperSpeedPlus Gen1 x2 ProtocolSpeed::from_raw( @@ -564,7 +576,7 @@ impl Xhci { | (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT | 10 << PROTO_SPEED_PSIM_SHIFT | (Lp::SuperSpeedPlus as u32) << PROTO_SPEED_LP_SHIFT - | 6 << PROTO_SPEED_PSIV_SHIFT + | 6 << PROTO_SPEED_PSIV_SHIFT, ), // SuperSpeedPlus Gen2 x2 ProtocolSpeed::from_raw( @@ -573,10 +585,12 @@ impl Xhci { | (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT | 20 << PROTO_SPEED_PSIM_SHIFT | (Lp::SuperSpeedPlus as u32) << PROTO_SPEED_LP_SHIFT - | 7 << PROTO_SPEED_PSIV_SHIFT + | 7 << PROTO_SPEED_PSIV_SHIFT, ), ]; - let supp_proto = self.supported_protocols_iter().find(|supp_proto| supp_proto.compat_port_range().contains(&port))?; + let supp_proto = self + .supported_protocols_iter() + .find(|supp_proto| supp_proto.compat_port_range().contains(&port))?; Some(if supp_proto.psic() != 0 { unsafe { supp_proto.protocol_speeds().iter() } @@ -585,7 +599,8 @@ impl Xhci { }) } pub fn lookup_psiv(&self, port: u8, psiv: u8) -> Option<&'static ProtocolSpeed> { - self.supported_protocol_speeds(port)?.find(|speed| speed.psiv() == psiv) + self.supported_protocol_speeds(port)? + .find(|speed| speed.psiv() == psiv) } } #[derive(Deserialize)] diff --git a/xhcid/src/xhci/operational.rs b/xhcid/src/xhci/operational.rs index 638f210333..0d4af23901 100644 --- a/xhcid/src/xhci/operational.rs +++ b/xhcid/src/xhci/operational.rs @@ -1,5 +1,5 @@ -use std::slice; use std::num::NonZeroU8; +use std::slice; use syscall::io::{Io, Mmio}; use super::CapabilityRegs; diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index e1f0917d02..0ee5dc2448 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; use std::io::prelude::*; -use std::{cmp, mem, path, str}; +use std::{cmp, io, mem, path, str}; use serde::{Deserialize, Serialize}; use smallvec::{smallvec, SmallVec}; @@ -8,9 +8,9 @@ use smallvec::{smallvec, SmallVec}; 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, + 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, }; use super::{port, usb}; @@ -33,7 +33,7 @@ use crate::driver_interface::*; pub enum EndpointHandleTy { /// portX/endpoints/Y/transfer. Write calls transfer data to the device, and read calls /// transfer data from the device. - Transfer, + Transfer(PortTransferState), /// portX/endpoints/Y/status Status(usize), // offset @@ -42,10 +42,19 @@ pub enum EndpointHandleTy { Root(usize, Vec), // offset, content } +#[derive(Clone, Copy)] +pub enum PortTransferState { + /// Ready to read or write to do another transfer + Ready, + + /// Transfer has completed, and the status has to be read. + WaitingForStatusReq(PortTransferStatus), +} + pub enum PortReqState { Init, WaitingForDeviceBytes(Dma<[u8]>, usb::Setup), // buffer, setup params - WaitingForHostBytes(Dma<[u8]>, usb::Setup), // buffer, setup params + WaitingForHostBytes(Dma<[u8]>, usb::Setup), // buffer, setup params TmpSetup(usb::Setup), Tmp, } @@ -182,11 +191,7 @@ impl Xhci { { let (cmd, cycle) = ring.next(); - cmd.setup( - req, - TransferKind::NoData, - cycle, - ); + cmd.setup(req, TransferKind::NoData, cycle); } { let (cmd, cycle) = ring.next(); @@ -203,10 +208,7 @@ impl Xhci { let control = event.control.read(); if (status >> 24) != TrbCompletionCode::Success as u32 { - println!( - "DEVICE_REQ ERROR, COMPLETION CODE {:#0x}", - (status >> 24) - ); + println!("DEVICE_REQ ERROR, COMPLETION CODE {:#0x}", (status >> 24)); } println!( @@ -224,15 +226,56 @@ impl Xhci { fn set_configuration(&mut self, port: usize, config: u8) -> Result<()> { self.device_req_no_data(port, usb::Setup::set_configuration(config)) } - fn set_interface(&mut self, port: usize, interface_num: u8, alternate_setting: u8) -> Result<()> { - self.device_req_no_data(port, usb::Setup::set_interface(interface_num, alternate_setting)) + fn set_interface( + &mut self, + port: usize, + interface_num: u8, + alternate_setting: u8, + ) -> Result<()> { + self.device_req_no_data( + port, + usb::Setup::set_interface(interface_num, alternate_setting), + ) + } + + fn reset_endpoint(&mut self, port_num: usize, endp_num: u8, tsp: bool) -> Result<()> { + let slot = self + .port_states + .get(&port_num) + .ok_or(Error::new(EBADF))? + .slot; + { + let (cmd, cycle, event) = self.cmd.next(); + cmd.reset_endpoint(slot, endp_num + 1, tsp, cycle); + + self.dbs[0].write(0); + + while event.data.read() == 0 { + println!(" - Waiting for event"); + } + + if event.completion_code() != TrbCompletionCode::Success as u8 + || event.trb_type() != TrbType::CommandCompletion as u8 + { + println!("RESET_ENDPOINT failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read()); + return Err(Error::new(EIO)); + } + + cmd.reserved(false); + event.reserved(false); + + self.run.ints[0].erdp.write(self.cmd.erdp()); + } + Ok(()) } fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> { let mut req: ConfigureEndpointsReq = serde_json::from_slice(json_buf).or(Err(Error::new(EBADMSG)))?; - if (!self.cap.cic() || !self.op.cie()) && (req.config_desc != 0 || req.interface_desc != None || req.alternate_setting != None) { + if (!self.cap.cic() || !self.op.cie()) + && (req.config_desc != 0 || req.interface_desc != None || req.alternate_setting != None) + { //return Err(Error::new(EOPNOTSUPP)); req.config_desc = 0; req.alternate_setting = None; @@ -259,8 +302,13 @@ impl Xhci { let current_slot_a = input_context.device.slot.a.read(); - let endpoints = - &port_state.dev_desc.config_descs.get(req.config_desc as usize).ok_or(Error::new(EBADMSG))?.interface_descs[0].endpoints; + let endpoints = &port_state + .dev_desc + .config_descs + .get(req.config_desc as usize) + .ok_or(Error::new(EBADMSG))? + .interface_descs[0] + .endpoints; if endpoints.len() >= 31 { return Err(Error::new(EIO)); @@ -273,7 +321,11 @@ impl Xhci { | ((u32::from(new_context_entries) << CONTEXT_ENTRIES_SHIFT) & CONTEXT_ENTRIES_MASK), ); - input_context.control.write((u32::from(req.alternate_setting.unwrap_or(0)) << 16) | (u32::from(req.interface_desc.unwrap_or(0)) << 8) | u32::from(req.config_desc)); + input_context.control.write( + (u32::from(req.alternate_setting.unwrap_or(0)) << 16) + | (u32::from(req.interface_desc.unwrap_or(0)) << 8) + | u32::from(req.config_desc), + ); let lec = self.cap.lec(); @@ -425,20 +477,49 @@ impl Xhci { .configuration_value; self.set_configuration(port, configuration_value)?; - if let (Some(interface_num), Some(alternate_setting)) = (req.interface_desc, req.alternate_setting) { + if let (Some(interface_num), Some(alternate_setting)) = + (req.interface_desc, req.alternate_setting) + { self.set_interface(port, interface_num, alternate_setting)?; } Ok(()) } - fn transfer_read(&mut self, port_num: usize, endp_idx: u8, buf: &mut [u8]) -> Result { - self.transfer(port_num, endp_idx, if !buf.is_empty() { DeviceReqData::In(buf) } else { DeviceReqData::NoData }) + fn transfer_read( + &mut self, + port_num: usize, + endp_idx: u8, + buf: &mut [u8], + ) -> Result<(u8, u32)> { + self.transfer( + port_num, + endp_idx, + if !buf.is_empty() { + DeviceReqData::In(buf) + } else { + DeviceReqData::NoData + }, + ) } - fn transfer_write(&mut self, port_num: usize, endp_idx: u8, buf: &[u8]) -> Result { - self.transfer(port_num, endp_idx, if !buf.is_empty() { DeviceReqData::Out(buf) } else { DeviceReqData::NoData }) + fn transfer_write(&mut self, port_num: usize, endp_idx: u8, buf: &[u8]) -> Result<(u8, u32)> { + self.transfer( + port_num, + endp_idx, + if !buf.is_empty() { + DeviceReqData::Out(buf) + } else { + DeviceReqData::NoData + }, + ) } // TODO: Rename DeviceReqData to something more general. - fn transfer(&mut self, port_num: usize, endp_idx: u8, mut buf: DeviceReqData) -> Result { + fn transfer( + &mut self, + port_num: usize, + endp_idx: u8, + mut buf: DeviceReqData, + ) -> Result<(u8, u32)> { + // TODO: Check that only readable enpoints are read, etc. let endp_num = endp_idx + 1; let xhc_endp_num = endp_num + 1; // TODO: Check that buf has a nonzero size, otherwise (at least for Rust's GlobalAlloc), @@ -455,8 +536,21 @@ impl Xhci { DeviceReqData::NoData => None, }; - let port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; - let endp_desc: &EndpDesc = 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_idx as usize).ok_or(Error::new(EBADFD))?; + let port_state = self + .port_states + .get_mut(&port_num) + .ok_or(Error::new(EBADFD))?; + let endp_desc: &EndpDesc = 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_idx as usize) + .ok_or(Error::new(EBADFD))?; if endp_desc.is_isoch() { return Err(Error::new(ENOSYS)); @@ -467,11 +561,19 @@ impl Xhci { return Err(Error::new(EBADF)); } - let endp_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?; + let endp_state = port_state + .endpoint_states + .get_mut(&endp_num) + .ok_or(Error::new(EBADF))?; let ring: &mut Ring = match endp_state { EndpointState::Ready(super::RingOrStreams::Ring(ref mut ring)) => ring, - EndpointState::Ready(super::RingOrStreams::Streams(stream_ctx_array)) => stream_ctx_array.rings.get_mut(&1).ok_or(Error::new(EBADF))?, + EndpointState::Ready(super::RingOrStreams::Streams(stream_ctx_array)) => { + stream_ctx_array + .rings + .get_mut(&1) + .ok_or(Error::new(EBADF))? + } EndpointState::Init => return Err(Error::new(EIO)), }; // TODO: Scatter-gather transfers, possibly allowing >64KiB sizes. @@ -485,42 +587,72 @@ impl Xhci { 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)) + }) + .unwrap_or((0, false)) } else { - (dma_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, false) + ( + dma_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, + false, + ) }; let estimated_td_size = mem::size_of_val(&trb) as u8; // one trb per td - trb.normal(buffer, len, cycle, estimated_td_size, false, true, false, true, idt, false); + trb.normal( + buffer, + len, + cycle, + estimated_td_size, + false, + true, + false, + true, + idt, + false, + ); } let stream_id = 1u16; - self.dbs[port_state.slot as usize].write(u32::from(xhc_endp_num) | (u32::from(stream_id) << 16)); + self.dbs[port_state.slot as usize] + .write(u32::from(xhc_endp_num) | (u32::from(stream_id) << 16)); - let bytes_transferred = { + let (completion_code, bytes_transferred) = { let event = self.cmd.next_event(); while event.data.read() == 0 { println!(" - Waiting for event"); } // FIXME: EDTLA if event data was set - if event.completion_code() != TrbCompletionCode::ShortPacket as u8 && event.transfer_length() != 0 { - println!("Event trb yielded a short packet, but all bytes were still transferred"); + if event.completion_code() != TrbCompletionCode::ShortPacket as u8 + && event.transfer_length() != 0 + { + println!( + "Event trb didn't yield a short packet, but some bytes were not transferred" + ); } - if event.completion_code() != TrbCompletionCode::Success as u8 || event.trb_type() != TrbType::Transfer as u8 { - println!("Custom transfer event failed with {:#0x} {:#0x} {:#0x}", 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!( + "Custom transfer event failed with {:#0x} {:#0x} {:#0x}", + event.data.read(), + event.status.read(), + event.control.read() + ); } // TODO: Handle event data println!("EVENT DATA: {:?}", event.event_data()); - u32::from(len) - event.transfer_length() + ( + event.completion_code(), + u32::from(len) - event.transfer_length(), + ) }; if let DeviceReqData::In(dbuf) = &mut buf { dbuf.copy_from_slice(&*dma_buffer.as_ref().unwrap()); } - Ok(bytes_transferred) + Ok((completion_code, bytes_transferred as u32)) } pub(crate) fn get_dev_desc(&mut self, port_id: usize) -> Result { let st = self @@ -689,7 +821,13 @@ impl Xhci { bytes_to_read } - fn port_req_transfer(&mut self, port_num: usize, data_buffer: Option<&mut Dma<[u8]>>, setup: usb::Setup, transfer_kind: TransferKind) -> Result<()> { + fn port_req_transfer( + &mut self, + port_num: usize, + data_buffer: Option<&mut Dma<[u8]>>, + setup: usb::Setup, + transfer_kind: TransferKind, + ) -> Result<()> { // TODO: This json format might be too high level, but is useful for debugging, // but when actual device-specific drivers are written, a binary format would // be better. Maybe something simple like bincode could be used, if a custom binary struct @@ -786,7 +924,13 @@ impl Xhci { // FIXME: Make sure there aren't any other PortReq handles, perhaps by storing the state in // PortState? } - fn handle_port_req_write(&mut self, fd: usize, port_num: usize, mut st: PortReqState, buf: &[u8]) -> Result { + fn handle_port_req_write( + &mut self, + fd: usize, + port_num: usize, + mut st: PortReqState, + buf: &[u8], + ) -> Result { let bytes_written = match st { PortReqState::Init => { let req = serde_json::from_slice::(buf).or(Err(Error::new(EBADMSG)))?; @@ -821,7 +965,13 @@ impl Xhci { } Ok(bytes_written) } - fn handle_port_req_read(&mut self, fd: usize, port_num: usize, mut st: PortReqState, buf: &mut [u8]) -> Result { + fn handle_port_req_read( + &mut self, + fd: usize, + port_num: usize, + mut st: PortReqState, + buf: &mut [u8], + ) -> Result { let bytes_read = match st { PortReqState::WaitingForDeviceBytes(mut dma_buffer, setup) => { if buf.len() != dma_buffer.len() { @@ -834,7 +984,9 @@ impl Xhci { buf.len() } - PortReqState::Init | PortReqState::WaitingForHostBytes(_, _) => return Err(Error::new(EBADF)), + PortReqState::Init | PortReqState::WaitingForHostBytes(_, _) => { + return Err(Error::new(EBADF)) + } PortReqState::Tmp | PortReqState::TmpSetup(_) => unreachable!(), }; match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { @@ -843,6 +995,84 @@ impl Xhci { } Ok(bytes_read) } + fn completion_code_to_status( + completion_code: u8, + bytes_transferred: u16, + ) -> PortTransferStatus { + if completion_code == TrbCompletionCode::Success as u8 { + PortTransferStatus::Success + } else if completion_code == TrbCompletionCode::ShortPacket as u8 { + PortTransferStatus::ShortPacket(bytes_transferred as u16) + } else if completion_code == TrbCompletionCode::Stall as u8 { + PortTransferStatus::Stalled + } else { + PortTransferStatus::Unknown + } + } + + fn handle_transfer_write( + &mut self, + fd: usize, + port_num: usize, + endp_num: u8, + mut st: PortTransferState, + buf: &[u8], + ) -> Result { + let bytes_written = match st { + PortTransferState::Ready => { + let (completion_code, bytes_written) = + self.transfer_write(port_num, endp_num - 1, buf)?; + st = PortTransferState::WaitingForStatusReq(Self::completion_code_to_status( + completion_code, + bytes_written as u16, + )); + bytes_written + } + PortTransferState::WaitingForStatusReq(_) => return Err(Error::new(EBADF)), + }; + match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + Handle::Endpoint(_, _, EndpointHandleTy::Transfer(ref mut state)) => *state = st, + _ => unreachable!(), + } + Ok(bytes_written as usize) + } + fn handle_transfer_read( + &mut self, + fd: usize, + port_num: usize, + endp_num: u8, + mut st: PortTransferState, + mut buf: &mut [u8], + ) -> Result { + let bytes_read = match st { + PortTransferState::Ready => { + let (completion_code, bytes_transferred) = + self.transfer_read(port_num, endp_num - 1, buf)?; + // TODO: Perhaps transfers with large buffers could be broken up to smaller + // transfers. + st = PortTransferState::WaitingForStatusReq(Self::completion_code_to_status( + completion_code, + bytes_transferred as u16, + )); + bytes_transferred as usize + } + PortTransferState::WaitingForStatusReq(status) => { + // Use a cursor to count the number of bytes written + let mut cursor = io::Cursor::new(buf); + serde_json::to_writer(&mut cursor, &status).or(Err(Error::new(EIO)))?; + st = PortTransferState::Ready; + + cursor + .seek(io::SeekFrom::Current(0)) + .or(Err(Error::new(EIO)))? as usize + } + }; + match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + Handle::Endpoint(_, _, EndpointHandleTy::Transfer(ref mut state)) => *state = st, + _ => unreachable!(), + } + Ok(bytes_read) + } } impl SchemeMut for Xhci { @@ -982,11 +1212,7 @@ impl SchemeMut for Xhci { let port_state = self.port_states.get(&port_num).ok_or(Error::new(ENOENT))?; - if port_state - .endpoint_states - .get(&endpoint_num) - .is_none() - { + if port_state.endpoint_states.get(&endpoint_num).is_none() { return Err(Error::new(ENOENT)); } @@ -1004,17 +1230,23 @@ impl SchemeMut for Xhci { // endpoint. return Err(Error::new(ENOENT)); } - 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(endpoint_num as usize - 1).ok_or(Error::new(ENOENT))?; - match endp_desc.direction() { - EndpDirection::Out => if flags & O_RDWR != O_WRONLY && flags & O_STAT != 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(endpoint_num as usize - 1) + .ok_or(Error::new(ENOENT))?; + if let EndpDirection::In = endp_desc.direction() { + if flags & O_RDWR != O_RDONLY && flags & O_STAT != 0 { return Err(Error::new(EACCES)); } - EndpDirection::In => if flags & O_RDWR != O_RDONLY && flags & O_STAT != 0 { - return Err(Error::new(EACCES)); - } - _ => (), } - EndpointHandleTy::Transfer + EndpointHandleTy::Transfer(PortTransferState::Ready) } _ => return Err(Error::new(ENOENT)), }; @@ -1064,15 +1296,19 @@ impl SchemeMut for Xhci { stat.st_mode = MODE_FILE; stat.st_size = buf.len() as u64; } - Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _)) | Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => { + Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _)) + | Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => { stat.st_mode = MODE_CHR; stat.st_size = buf.len() as u64; } - Handle::PortReq(_, PortReqState::Tmp) | Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(), + Handle::PortReq(_, PortReqState::Tmp) + | Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(), Handle::PortState(_, _) | Handle::PortReq(_, _) => stat.st_mode = MODE_CHR, Handle::Endpoint(_, _, st) => match st { - EndpointHandleTy::Status(_) | EndpointHandleTy::Transfer => stat.st_mode = MODE_CHR, + EndpointHandleTy::Status(_) | EndpointHandleTy::Transfer(_) => { + stat.st_mode = MODE_CHR + } EndpointHandleTy::Root(_, ref buf) => { stat.st_mode = MODE_DIR; stat.st_size = buf.len() as u64; @@ -1107,7 +1343,7 @@ impl SchemeMut for Xhci { match st { EndpointHandleTy::Root(_, _) => "", EndpointHandleTy::Status(_) => "status", - EndpointHandleTy::Transfer => "transfer", + EndpointHandleTy::Transfer(_) => "transfer", } ) .unwrap(), @@ -1172,11 +1408,8 @@ impl SchemeMut for Xhci { Handle::ConfigureEndpoints(_) => return Err(Error::new(EBADF)), &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { - EndpointHandleTy::Transfer => { - self.transfer_read(port_num, endp_num - 1, buf)?; - // TODO: Perhaps transfers with large buffers could be broken up a to smaller - // transfers. - Ok(buf.len()) + &mut EndpointHandleTy::Transfer(state) => { + self.handle_transfer_read(fd, port_num, endp_num, state, buf) } EndpointHandleTy::Status(ref mut offset) => { let ps = self.port_states.get(&port_num).ok_or(Error::new(EBADF))?; @@ -1245,10 +1478,8 @@ impl SchemeMut for Xhci { self.configure_endpoints(port_num, buf)?; Ok(buf.len()) } - &mut Handle::Endpoint(port_num, endp_num, EndpointHandleTy::Transfer) => { - self.transfer_write(port_num, endp_num - 1, buf)?; - // TODO - Ok(buf.len()) + &mut Handle::Endpoint(port_num, endp_num, EndpointHandleTy::Transfer(state)) => { + self.handle_transfer_write(fd, port_num, endp_num, state, buf) } &mut Handle::PortReq(port_num, ref mut st) => { let state = std::mem::replace(st, PortReqState::Tmp); diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index 91e35115ce..e6a92f7ba8 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -154,10 +154,15 @@ impl Trb { self.control.readf(TRB_CONTROL_EVENT_DATA_BIT) } pub fn event_data(&self) -> Option { - if self.event_data_bit() { Some(self.data.read()) } else { None } + if self.event_data_bit() { + Some(self.data.read()) + } else { + None + } } pub fn endpoint_id(&self) -> u8 { - ((self.control.read() & TRB_CONTROL_ENDPOINT_ID_MASK) >> TRB_CONTROL_ENDPOINT_ID_SHIFT) as u8 + ((self.control.read() & TRB_CONTROL_ENDPOINT_ID_MASK) >> TRB_CONTROL_ENDPOINT_ID_SHIFT) + as u8 } pub fn trb_type(&self) -> u8 { ((self.control.read() & TRB_CONTROL_TRB_TYPE_MASK) >> TRB_CONTROL_TRB_TYPE_SHIFT) as u8 @@ -205,8 +210,39 @@ impl Trb { 0, (u32::from(slot_id) << 24) | ((TrbType::ConfigureEndpoint as u32) << 10) - | (cycle as u32), - ) + | u32::from(cycle), + ); + } + pub fn reset_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, tsp: bool, cycle: bool) { + assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc); + self.set( + 0, + 0, + (u32::from(slot_id) << 24) + | (u32::from(endp_num_xhc) << 16) + | ((TrbType::ResetEndpoint as u32) << 10) + | (u32::from(tsp) << 9) + | u32::from(cycle), + ); + } + pub fn stop_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, suspend: bool, cycle: bool) { + assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc); + self.set( + 0, + 0, + (u32::from(slot_id) << 24) + | (u32::from(suspend) << 23) + | (u32::from(endp_num_xhc) << 16) + | ((TrbType::StopEndpoint as u32) << 10) + | u32::from(cycle), + ); + } + pub fn reset_device(&mut self, slot_id: u8, cycle: bool) { + self.set( + 0, + 0, + (u32::from(slot_id) << 24) | ((TrbType::ResetDevice as u32) << 10) | u32::from(cycle), + ); } pub fn setup(&mut self, setup: usb::Setup, transfer: TransferKind, cycle: bool) { @@ -238,13 +274,24 @@ impl Trb { | (cycle as u32), ); } - pub fn normal(&mut self, buffer: u64, len: u16, cycle: bool, estimated_td_size: u8, ent: bool, isp: bool, chain: bool, ioc: bool, idt: bool, bei: bool) { + pub fn normal( + &mut self, + buffer: u64, + len: u16, + cycle: bool, + estimated_td_size: u8, + ent: bool, + isp: bool, + chain: bool, + ioc: bool, + idt: bool, + bei: bool, + ) { assert_eq!(estimated_td_size & 0x1F, estimated_td_size); // 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(len) | (u32::from(estimated_td_size) << 17), u32::from(cycle) | (u32::from(ent) << 1) | (u32::from(isp) << 2) @@ -252,7 +299,7 @@ impl Trb { | (u32::from(ioc) << 5) | (u32::from(idt) << 6) | (u32::from(bei) << 9) - | ((TrbType::Normal as u32) << 10) + | ((TrbType::Normal as u32) << 10), ) } }