diff --git a/usbscsid/src/main.rs b/usbscsid/src/main.rs index 0c740af631..4dbd53b980 100644 --- a/usbscsid/src/main.rs +++ b/usbscsid/src/main.rs @@ -47,8 +47,11 @@ fn main() { 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(&inquiry) }, DeviceReqData::In(&mut buffer)).expect("Failed to send command"); + let mut buffer = [0u8; 5]; + let mut command_buffer = [0u8; 6]; + { + let mut inquiry = plain::from_mut_bytes(&mut command_buffer).unwrap(); + *inquiry = scsi::cmds::Inquiry::new(false, 0, 5, 0); + } + protocol.send_command(&command_buffer, DeviceReqData::In(&mut buffer)).expect("Failed to send command"); } diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index ab4c1085b7..dc2af73f54 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, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpStatusHandle, XhciEndpTransferHandle}; +use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle}; use super::{Protocol, ProtocolError}; @@ -77,10 +77,8 @@ impl CommandStatusWrapper { pub struct BulkOnlyTransport<'a> { handle: &'a XhciClientHandle, - bulk_in: XhciEndpTransferHandle, - bulk_out: XhciEndpTransferHandle, - bulk_in_status: XhciEndpStatusHandle, - bulk_out_status: XhciEndpStatusHandle, + bulk_in: XhciEndpHandle, + bulk_out: XhciEndpHandle, bulk_in_num: u8, bulk_out_num: u8, max_lun: u8, @@ -103,10 +101,8 @@ impl<'a> BulkOnlyTransport<'a> { println!("BOT_MAX_LUN {}", max_lun); Ok(Self { - bulk_in: handle.open_endpoint(bulk_in_num, PortReqDirection::DeviceToHost)?, - bulk_out: handle.open_endpoint(bulk_out_num, PortReqDirection::HostToDevice)?, - bulk_in_status: handle.open_endpoint_status(bulk_in_num)?, - bulk_out_status: handle.open_endpoint_status(bulk_out_num)?, + bulk_in: handle.open_endpoint(bulk_in_num)?, + bulk_out: handle.open_endpoint(bulk_out_num)?, bulk_in_num, bulk_out_num, handle, @@ -115,15 +111,20 @@ impl<'a> BulkOnlyTransport<'a> { interface_num: if_desc.number, }) } - fn clear_stall(&mut self, endp_num: u8) -> Result<(), XhciClientHandleError> { - self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(endp_num), FEATURE_ENDPOINT_HALT) + 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) + } + 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) } fn reset_recovery(&mut self) -> Result<(), ProtocolError> { bulk_only_mass_storage_reset(self.handle, self.interface_num.into())?; - self.clear_stall(self.bulk_in_num.into())?; - self.clear_stall(self.bulk_out_num.into())?; + self.clear_stall_in()?; + self.clear_stall_out()?; - if self.bulk_in_status.current_status()? == EndpointStatus::Halted || self.bulk_out_status.current_status()? == EndpointStatus::Halted { + if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.status()? == EndpointStatus::Halted { return Err(ProtocolError::RecoveryFailed) } Ok(()) @@ -135,25 +136,23 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { self.current_tag += 1; let tag = self.current_tag; - let mut command_block = [0u8; 16]; - if cb.len() > 16 { - return Err(ProtocolError::TooLargeCommandBlock(cb.len())); - } - command_block[..cb.len()].copy_from_slice(&cb); + println!("{}", base64::encode(cb)); + println!(); - let cbw = CommandBlockWrapper { - signature: CBW_SIGNATURE, - tag, - data_transfer_len: data.len() as u32, - lun: 0, // TODO - flags: u8::from(data.direction() == PortReqDirection::DeviceToHost) << 7, - cb_len: cb.len().try_into().or(Err(ProtocolError::TooLargeCommandBlock(cb.len())))?, - command_block, - }; - match self.bulk_out.transfer_write(unsafe { plain::as_bytes(&cbw) })? { + let mut cbw_bytes = [0u8; 31]; + let cbw = plain::from_mut_bytes::(&mut cbw_bytes).unwrap(); + + *cbw = CommandBlockWrapper::new(tag, data.len() as u32, data.direction().into(), 0, cb)?; + println!("{}", base64::encode(&cbw_bytes)); + + dbg!(self.bulk_in.status()?, self.bulk_out.status()?); + + match self.bulk_out.transfer_write(&cbw_bytes)? { PortTransferStatus::ShortPacket(31) => (), PortTransferStatus::Stalled => { - panic!("bulk out endpoint stalled when sending CBW"); + println!("bulk out endpoint stalled when sending CBW"); + self.reset_recovery()?; + dbg!(self.bulk_in.status()?, self.bulk_out.status()?); } _ => panic!("invalid number of CBW bytes written; expected a short packed of length 31 (0x1F)"), } @@ -165,7 +164,7 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { 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)?; + self.clear_stall_in()?; } PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))), }; @@ -173,27 +172,29 @@ impl<'a> Protocol for BulkOnlyTransport<'a> { } DeviceReqData::Out(ref buffer) => todo!(), DeviceReqData::NoData => todo!(), - }; + } - let mut csw = CommandStatusWrapper::default(); + let mut csw_buffer = [0u8; 13]; - match self.bulk_in.transfer_read(unsafe { plain::as_mut_bytes(&mut csw) })? { + match self.bulk_in.transfer_read(&mut csw_buffer)? { PortTransferStatus::ShortPacket(13) => (), PortTransferStatus::Stalled => { println!("bulk in endpoint stalled when reading CSW"); - self.clear_stall(self.bulk_in_num)?; + self.clear_stall_in()?; } _ => panic!("invalid number of CSW bytes read; expected a short packet of length 13 (0xD)"), }; + println!("{}", base64::encode(&csw_buffer)); + let csw = plain::from_bytes::(&csw_buffer).unwrap(); 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 { + if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.status()? == EndpointStatus::Halted { println!("Trying to recover from stall"); - dbg!(self.bulk_in_status.current_status()?, self.bulk_out_status.current_status()?); + dbg!(self.bulk_in.status()?, self.bulk_out.status()?); } Ok(()) @@ -204,7 +205,7 @@ pub fn bulk_only_mass_storage_reset(handle: &XhciClientHandle, if_num: u16) -> R 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 = 0; + 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))?; Ok(lun) diff --git a/usbscsid/src/scsi/cmds.rs b/usbscsid/src/scsi/cmds.rs index 582d2dd628..0b759886bb 100644 --- a/usbscsid/src/scsi/cmds.rs +++ b/usbscsid/src/scsi/cmds.rs @@ -13,6 +13,8 @@ pub struct ReportIdentInfo { pub info_ty: u8, pub control: u8, } +unsafe impl plain::Plain for ReportIdentInfo {} + impl ReportIdentInfo { pub fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self { Self { @@ -52,6 +54,8 @@ pub struct ReportSuppOpcodes { pub _rsvd: u8, pub control: u8, } +unsafe impl plain::Plain for ReportSuppOpcodes {} + impl ReportSuppOpcodes { pub const fn new(rep_opts: ReportSuppOpcodesOptions, rctd: bool, req_opcode: u8, req_serviceaction: u16, alloc_len: u32, control: u8) -> Self { Self { @@ -115,6 +119,7 @@ pub struct CommandDescriptor { /// little endian pub cdb_len: u16, } + #[repr(packed)] pub struct OneCommandParam { pub _rsvd: u8, @@ -133,6 +138,7 @@ pub struct Inquiry { pub alloc_len: u16, pub control: u8, } +unsafe impl plain::Plain for Inquiry {} impl Inquiry { pub fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self { diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index c11dc1dc01..709ce85882 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -79,6 +79,16 @@ pub enum EndpBinaryDirection { Out, In, } + +impl From for EndpBinaryDirection { + fn from(d: PortReqDirection) -> Self { + match d { + PortReqDirection::DeviceToHost => Self::In, + PortReqDirection::HostToDevice => Self::Out, + } + } +} + impl From for EndpDirection { fn from(b: EndpBinaryDirection) -> Self { match b { @@ -305,7 +315,7 @@ impl str::FromStr for PortState { } #[repr(u8)] -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum EndpointStatus { Disabled, Enabled, @@ -411,48 +421,28 @@ impl XhciClientHandle { let string = std::fs::read_to_string(path)?; Ok(string.parse()?) } - pub fn endpoint_onetime_status( + pub fn open_endpoint_ctl( &self, num: u8, - ) -> result::Result { - let path = format!("{}:port{}/endpoints/{}/status", self.scheme, self.port, num); - let string = std::fs::read_to_string(path)?; - Ok(string.parse()?) + ) -> result::Result { + let path = format!("{}:port{}/endpoints/{}/ctl", self.scheme, self.port, num); + Ok(File::open(path)?) } - pub fn open_endpoint_status( + pub fn open_endpoint_data( &self, 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)?, - )) - } - pub fn open_endpoint( - &self, - num: u8, - direction: PortReqDirection, - ) -> result::Result { + ) -> result::Result { let path = format!( - "{}:port{}/endpoints/{}/transfer", + "{}:port{}/endpoints/{}/data", 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)?, - })) + Ok(File::open(path)?) + } + pub fn open_endpoint(&self, num: u8) -> result::Result { + Ok(XhciEndpHandle { + ctl: self.open_endpoint_ctl(num)?, + data: self.open_endpoint_data(num)?, + }) } pub fn device_request<'a>( &self, @@ -546,68 +536,105 @@ impl XhciClientHandle { } #[derive(Debug)] -pub struct XhciEndpStatusHandle(File); - -impl XhciEndpStatusHandle { - pub fn current_status(&mut self) -> result::Result { - 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("non-utf8 endpoint state")), - ))?; - Ok(status - .parse::() - .or(Err(XhciClientHandleError::InvalidResponse(Invalid( - "malformed endpoint state", - ))))?) - } - pub fn into_inner(self) -> File { - self.0 - } +pub struct XhciEndpHandle { + data: File, + ctl: File, } -#[derive(Debug)] -pub struct XhciEndpTransferHandle(File); +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum XhciEndpCtlDirection { + Out, + In, + NoData, +} -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)?; +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum XhciEndpCtlReq { + // TODO: Reduce the number of direction enums from 5 to perhaps 2. + // 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. - let status = serde_json::from_slice(&status_buf[..status_bytes_read])?; + /// 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 + /// itself send a potential ClearFeature(ENDPOINT_HALT). + no_clear_feature: bool, + }, - 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"))); - } + /// Tells xhcid that the endpoint status is going to be retrieved from the Ctl interface file. + Status, +} +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum XhciEndpCtlRes { + /// Xhcid responded with the current state of an endpoint. + Status(EndpointStatus), + + /// Xhci sent the result of a transfer. + TransferResult(PortTransferStatus), + + /// Xhcid is waiting for data to be sent or received on the Data interface file. + Pending, + + /// No Ctl request is currently being processed by xhcid. + Idle, +} + +impl XhciEndpHandle { + fn ctl_req(&mut self, ctl_req: &XhciEndpCtlReq) -> result::Result<(), XhciClientHandleError> { + let ctl_buffer = serde_json::to_vec(ctl_req)?; + + let ctl_bytes_written = self.ctl.write(&ctl_buffer)?; + if ctl_bytes_written != ctl_buffer.len() { + return Err(Invalid("xhcid didn't process all of the ctl bytes").into()); } - Ok(status) + + Ok(()) } - pub fn transfer_write( - &mut self, - buffer: &[u8], - ) -> result::Result { - let bytes_transferred = self.0.write(buffer)?; - self.get_status(buffer.len(), bytes_transferred) + 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_res = serde_json::from_slice(&ctl_buffer[..ctl_bytes_read as usize])?; + Ok(ctl_res) } - 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 reset(&mut self, no_clear_feature: bool) -> result::Result<(), XhciClientHandleError> { + self.ctl_req(&XhciEndpCtlReq::Reset { no_clear_feature }) } - pub fn into_inner(self) -> File { - self.0 + pub fn status(&mut self) -> result::Result { + self.ctl_req(&XhciEndpCtlReq::Status)?; + match self.ctl_res()? { + 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(XhciEndpCtlDirection::Out); + self.ctl_req(&req)?; + + let bytes_read = f(&mut self.data)?; + let res = self.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(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_read(&mut self, buf: &mut [u8]) -> result::Result { + let len = buf.len(); + 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()) } } diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index bf9c6a251a..2d0fe8fde9 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -35,6 +35,8 @@ use self::ring::Ring; use self::runtime::{Interrupter, RuntimeRegs}; use self::trb::{TransferKind, TrbCompletionCode, TrbType}; +use self::scheme::EndpIfState; + use crate::driver_interface::*; struct Device<'a> { @@ -151,14 +153,14 @@ pub(crate) enum RingOrStreams { Streams(StreamContextArray), } -pub(crate) enum EndpointState { - Ready(RingOrStreams), - Init, +pub(crate) struct EndpointState { + pub transfer: RingOrStreams, + pub driver_if_state: EndpIfState, } impl EndpointState { fn ring(&mut self) -> Option<&mut Ring> { - match self { - Self::Ready(RingOrStreams::Ring(ring)) => Some(ring), + match self.transfer { + RingOrStreams::Ring(ref mut ring) => Some(ring), _ => None, } } @@ -294,10 +296,6 @@ impl Xhci { self.dbs[0].write(0); println!(" - XHCI initialized"); - - for (pointer, capability) in self.capabilities_iter() { - dbg!(pointer, capability); - } } pub fn enable_port_slot(cmd: &mut CommandRing, dbs: &mut [Doorbell]) -> u8 { @@ -365,7 +363,10 @@ impl Xhci { dev_desc, endpoint_states: std::iter::once(( 0, - EndpointState::Ready(RingOrStreams::Ring(ring)), + EndpointState { + transfer: RingOrStreams::Ring(ring), + driver_if_state: EndpIfState::Init, + } )) .collect::>(), }; diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index 4bdbc60d7a..2f78a222e2 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -30,14 +30,21 @@ use super::usb::endpoint::{EndpointTy, ENDP_ATTR_TY_MASK}; use crate::driver_interface::*; +#[derive(Clone, Copy, Debug)] +pub enum EndpIfState { + Init, + WaitingForDataPipe(XhciEndpCtlDirection), + WaitingForStatus, + WaitingForTransferResult(PortTransferStatus), +} + /// Subdirs of an endpoint pub enum EndpointHandleTy { - /// portX/endpoints/Y/transfer. Write calls transfer data to the device, and read calls - /// transfer data from the device. - Transfer(PortTransferState), + /// portX/endpoints/Y/data. Allows clients to read and write data associated with ctl requests. + Data, /// portX/endpoints/Y/status - Status(usize), // offset + Ctl, /// portX/endpoints/Y/ Root(usize, Vec), // offset, content @@ -469,7 +476,7 @@ impl Xhci { ); port_state.endpoint_states.insert( endp_num, - EndpointState::Ready(super::RingOrStreams::Streams(array)), + EndpointState { transfer: super::RingOrStreams::Streams(array), driver_if_state: EndpIfState::Init }, ); array_ptr @@ -484,7 +491,7 @@ impl Xhci { ); port_state.endpoint_states.insert( endp_num, - EndpointState::Ready(super::RingOrStreams::Ring(ring)), + EndpointState { transfer: super::RingOrStreams::Ring(ring), driver_if_state: EndpIfState::Init }, ); ring_ptr }; @@ -627,7 +634,6 @@ impl Xhci { } if EndpDirection::from(buf.direction()) != endp_desc.direction() { - dbg!(buf.direction(), endp_desc, endp_idx, endp_num); return Err(Error::new(EBADF)); } @@ -637,14 +643,13 @@ impl Xhci { .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)) => { + 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::Init => return Err(Error::new(EIO)), }; // TODO: Scatter-gather transfers, possibly allowing >64KiB sizes. let len = u16::try_from(buf.len()).or(Err(Error::new(ENOSYS)))?; @@ -671,6 +676,7 @@ impl Xhci { len, cycle, estimated_td_size, + 0, false, true, false, @@ -922,7 +928,7 @@ impl Xhci { .get_mut(&0) .ok_or(Error::new(EIO))? { - EndpointState::Ready(super::RingOrStreams::Ring(ref mut ring)) => ring, + EndpointState { transfer: super::RingOrStreams::Ring(ref mut ring), .. } => ring, // Control endpoints never use streams _ => return Err(Error::new(EIO)), @@ -1074,84 +1080,6 @@ 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 { @@ -1268,16 +1196,10 @@ impl SchemeMut for Xhci { /*if self.dev_ctx.contexts[port_state.slot as usize].endpoints.get(endpoint_num as usize).ok_or(Error::new(ENOENT))?.a.read() & 0b111 != 1 { return Err(Error::new(ENXIO)); // TODO: Find a proper error code for "endpoint not initialized". }*/ - let contents = match port_state - .endpoint_states - .get(&endpoint_num) - .ok_or(Error::new(ENOENT))? - { - EndpointState::Ready { .. } if endpoint_num != 0 => "transfer\nstatus\n", - EndpointState::Init | EndpointState::Ready { .. } => "status\n", + if !port_state.endpoint_states.contains_key(&endpoint_num) { + return Err(Error::new(ENOENT)); } - .as_bytes() - .to_owned(); + let contents = "ctl\ndata\n".as_bytes().to_owned(); Handle::Endpoint(port_num, endpoint_num, EndpointHandleTy::Root(0, contents)) } @@ -1296,37 +1218,8 @@ impl SchemeMut for Xhci { } let st = match sub { - "status" => { - // status is read-only - if flags & O_RDWR != O_RDONLY && flags & O_STAT == 0 { - return Err(Error::new(EACCES)); - } - EndpointHandleTy::Status(0) - } - "transfer" => { - if endpoint_num == 0 { - // Don't allow user programs to interface directly with the control - // 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))?; - if let EndpDirection::In = endp_desc.direction() { - if flags & O_RDWR != O_RDONLY && flags & O_STAT != 0 { - return Err(Error::new(EACCES)); - } - } - EndpointHandleTy::Transfer(PortTransferState::Ready) - } + "ctl" => EndpointHandleTy::Ctl, + "data" => EndpointHandleTy::Data, _ => return Err(Error::new(ENOENT)), }; Handle::Endpoint(port_num, endpoint_num, st) @@ -1385,9 +1278,7 @@ impl SchemeMut for Xhci { Handle::PortState(_, _) | Handle::PortReq(_, _) => stat.st_mode = MODE_CHR, Handle::Endpoint(_, _, st) => match st { - EndpointHandleTy::Status(_) | EndpointHandleTy::Transfer(_) => { - stat.st_mode = MODE_CHR - } + EndpointHandleTy::Ctl | EndpointHandleTy::Data => stat.st_mode = MODE_CHR, EndpointHandleTy::Root(_, ref buf) => { stat.st_mode = MODE_DIR; stat.st_size = buf.len() as u64; @@ -1421,8 +1312,8 @@ impl SchemeMut for Xhci { endp_num, match st { EndpointHandleTy::Root(_, _) => "", - EndpointHandleTy::Status(_) => "status", - EndpointHandleTy::Transfer(_) => "transfer", + EndpointHandleTy::Ctl => "ctl", + EndpointHandleTy::Data => "data", } ) .unwrap(), @@ -1451,13 +1342,11 @@ impl SchemeMut for Xhci { }; Ok(*offset) } - // Read-only unknown-length status strings - Handle::Endpoint(_, _, EndpointHandleTy::Status(ref mut offset)) - | Handle::PortState(_, ref mut offset) => { - *offset = match whence { - SEEK_SET => cmp::max(0, pos), - SEEK_CUR => cmp::max(0, *offset + pos), - SEEK_END => return Err(Error::new(ESPIPE)), + Handle::PortState(_, ref mut offset) => { + match whence { + SEEK_SET => *offset = pos, + SEEK_CUR => *offset = pos, + SEEK_END => *offset = pos, _ => return Err(Error::new(EINVAL)), }; Ok(*offset) @@ -1487,39 +1376,9 @@ impl SchemeMut for Xhci { Handle::ConfigureEndpoints(_) => return Err(Error::new(EBADF)), &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { - &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))?; - let status = self - .dev_ctx - .contexts - .get(ps.slot as usize) - .ok_or(Error::new(EBADF))? - .endpoints - .get(endp_num as usize) - .ok_or(Error::new(EBADF))? - .a - .read() - & ENDPOINT_CONTEXT_STATUS_MASK; - - let string = match status { - 0 => Some(EndpointStatus::Disabled), - 1 => Some(EndpointStatus::Enabled), - 2 => Some(EndpointStatus::Halted), - 3 => Some(EndpointStatus::Stopped), - 4 => Some(EndpointStatus::Error), - _ => None, - } - .as_ref() - .map(EndpointStatus::as_str) - .unwrap_or("unknown") - .as_bytes(); - - Ok(Self::write_dyn_string(string, buf, offset)) - } - EndpointHandleTy::Root(_, _) => unreachable!(), + EndpointHandleTy::Ctl => self.on_read_endp_ctl(port_num, endp_num, buf), + EndpointHandleTy::Data => self.on_read_endp_data(port_num, endp_num, buf), + EndpointHandleTy::Root(_, _) => return Err(Error::new(EBADF)), }, &mut Handle::PortState(port_num, ref mut offset) => { let ps = self.port_states.get(&port_num).ok_or(Error::new(EBADF))?; @@ -1557,8 +1416,10 @@ impl SchemeMut for Xhci { self.configure_endpoints(port_num, buf)?; 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::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)), } &mut Handle::PortReq(port_num, ref mut st) => { let state = std::mem::replace(st, PortReqState::Tmp); @@ -1576,3 +1437,116 @@ impl SchemeMut for Xhci { Ok(0) } } +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 raw = self.dev_ctx.contexts.get(slot as usize).ok_or(Error::new(EBADFD))?.endpoints.get(endp_num as usize).ok_or(Error::new(EBADFD))?.a.read() & super::context::ENDPOINT_CONTEXT_STATUS_MASK; + Ok(match raw { + 0 => EndpointStatus::Disabled, + 1 => EndpointStatus::Enabled, + 2 => EndpointStatus::Halted, + 3 => EndpointStatus::Stopped, + 4 => EndpointStatus::Error, + _ => return Err(Error::new(EIO)), + }) + } + pub fn on_req_reset_device(&mut self, port_num: usize, endp_num: u8, no_clear_feature: bool) { + } + 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; + let req = serde_json::from_slice::(buf).or(Err(Error::new(EBADMSG)))?; + 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) + } + _ => return Err(Error::new(EBADF)), + } + _ => return Err(Error::new(ENOSYS)), + } + Ok(buf.len()) + } + fn transfer_result(completion_code: u8, bytes_transferred: u32) -> 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 + } + } + 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); + + 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) + } + } + 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, + + state @ &mut EndpIfState::WaitingForStatus => { + *state = EndpIfState::Init; + XhciEndpCtlRes::Status(self.get_endp_status(port_num, endp_num)?) + } + &mut EndpIfState::WaitingForDataPipe(_) => XhciEndpCtlRes::Pending, + &mut EndpIfState::WaitingForTransferResult(status) => { + *ep_if_state = EndpIfState::Init; + XhciEndpCtlRes::TransferResult(status) + } + }; + + let mut cursor = io::Cursor::new(buf); + 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, buf)?; + let result = Self::transfer_result(completion_code, 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) + } + } +} diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index bc3e359f60..9af2b96ee0 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -297,6 +297,7 @@ impl Trb { len: u16, cycle: bool, estimated_td_size: u8, + interrupter: u8, ent: bool, isp: bool, chain: bool, @@ -308,11 +309,11 @@ 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(len) | (u32::from(estimated_td_size) << 17) | (u32::from(interrupter) << 21), u32::from(cycle) | (u32::from(ent) << 1) | (u32::from(isp) << 2) - | (u32::from(ent) << 4) + | (u32::from(chain) << 4) | (u32::from(ioc) << 5) | (u32::from(idt) << 6) | (u32::from(bei) << 9)