From cff3cf0d28e944347b5847a70b0f70ae8911006f Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 10 Feb 2020 11:23:18 +0100 Subject: [PATCH] Add basic logic to usbscsid and support alternate interface. --- Cargo.lock | 7 +++ usbhidd/src/main.rs | 2 +- usbscsid/Cargo.toml | 1 + usbscsid/src/main.rs | 30 +++++++++- usbscsid/src/protocol/bot.rs | 102 ++++++++++++++++++++++++++++++---- usbscsid/src/protocol/mod.rs | 17 ++++-- usbscsid/src/scsi/cmds.rs | 20 +++++++ xhcid/src/driver_interface.rs | 35 +++++++++--- xhcid/src/usb/setup.rs | 13 ++++- xhcid/src/xhci/mod.rs | 4 ++ xhcid/src/xhci/operational.rs | 4 +- xhcid/src/xhci/scheme.rs | 60 ++++++++++++++------ 12 files changed, 248 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 99f7c69c29..acf300f158 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,11 @@ dependencies = [ "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "bgad" version = "0.1.0" @@ -1337,6 +1342,7 @@ dependencies = [ name = "usbscsid" version = "0.1.0" dependencies = [ + "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "xhcid 0.1.0", @@ -1470,6 +1476,7 @@ dependencies = [ "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" diff --git a/usbhidd/src/main.rs b/usbhidd/src/main.rs index 8871b471fc..f862989fab 100644 --- a/usbhidd/src/main.rs +++ b/usbhidd/src/main.rs @@ -107,7 +107,7 @@ fn main() { println!("{:?}", item); } - handle.configure_endpoints(&ConfigureEndpointsReq { config_desc: 0 }).expect("Failed to configure endpoints"); + handle.configure_endpoints(&ConfigureEndpointsReq { config_desc: 0, interface_desc: None, alternate_setting: None }).expect("Failed to configure endpoints"); let (mut global_state, mut local_state, mut stack) = (GlobalItemsState::default(), LocalItemsState::default(), Vec::new()); diff --git a/usbscsid/Cargo.toml b/usbscsid/Cargo.toml index 8347ceb0e5..7d5f1abf88 100644 --- a/usbscsid/Cargo.toml +++ b/usbscsid/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +base64 = "0.11" # Only for debugging plain = "0.2" thiserror = "1" xhcid = { path = "../xhcid" } diff --git a/usbscsid/src/main.rs b/usbscsid/src/main.rs index 9f427f6a52..0d1be15988 100644 --- a/usbscsid/src/main.rs +++ b/usbscsid/src/main.rs @@ -18,9 +18,35 @@ fn main() { let handle = XhciClientHandle::new(scheme, port); + 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"); + handle.configure_endpoints(&ConfigureEndpointsReq { - config_desc: 0, + config_desc: configuration_value, + interface_desc: Some(interface_num), + alternate_setting: Some(alternate_setting), }).expect("Failed to configure endpoints"); - let protocol = protocol::setup(&handle, protocol); + let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc).expect("Failed to setup protocol"); + + 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) + }; + use protocol::Protocol; + protocol.send_command(unsafe { plain::as_bytes(&get_info) }).expect("Failed to send command"); } diff --git a/usbscsid/src/protocol/bot.rs b/usbscsid/src/protocol/bot.rs index 8db631e35d..d4f3021457 100644 --- a/usbscsid/src/protocol/bot.rs +++ b/usbscsid/src/protocol/bot.rs @@ -1,7 +1,10 @@ -use std::slice; +use std::convert::TryInto; +use std::fs::File; +use std::io::prelude::*; +use std::{io, slice}; use thiserror::Error; -use xhcid_interface::{DeviceReqData, PortReqTy, PortReqRecipient, XhciClientHandle, XhciClientHandleError}; +use xhcid_interface::{ConfDesc, DeviceReqData, EndpDirection, EndpointStatus, IfDesc, PortReqDirection, PortReqTy, PortReqRecipient, XhciClientHandle, XhciClientHandleError, XhciEndpStatusHandle}; use super::{Protocol, ProtocolError}; @@ -12,6 +15,7 @@ pub const CBW_FLAGS_DIRECTION_BIT: u8 = 1 << CBW_FLAGS_DIRECTION_SHIFT; pub const CBW_FLAGS_DIRECTION_SHIFT: u8 = 7; #[repr(packed)] +#[derive(Clone, Copy, Debug, Default)] pub struct CommandBlockWrapper { pub signature: u32, pub tag: u32, @@ -21,6 +25,7 @@ pub struct CommandBlockWrapper { pub cb_len: u8, pub command_block: [u8; 16], } +unsafe impl plain::Plain for CommandBlockWrapper {} pub const CSW_SIGNATURE: u32 = 0x53425355; @@ -33,33 +38,110 @@ pub enum CswStatus { } #[repr(packed)] +#[derive(Clone, Copy, Debug, Default)] pub struct CommandStatusWrapper { pub signature: u32, pub tag: u32, pub data_residue: u32, pub status: u8, } +unsafe impl plain::Plain for CommandStatusWrapper {} pub struct BulkOnlyTransport<'a> { handle: &'a XhciClientHandle, + bulk_in: File, + bulk_out: File, + bulk_in_status: XhciEndpStatusHandle, + bulk_out_status: XhciEndpStatusHandle, + bulk_in_num: u8, + bulk_out_num: u8, + max_lun: u8, + current_tag: u32, } impl<'a> BulkOnlyTransport<'a> { - pub fn init(handle: &'a XhciClientHandle) -> Result { - let lun = get_max_lun(handle, 0)?; - println!("BOT_MAX_LUN {}", lun); + 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 max_lun = get_max_lun(handle, 0)?; + 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_num, + bulk_out_num, handle, + max_lun, + current_tag: 0, }) } + fn recover_from_stall(&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)?; + + if self.bulk_in_status.current_status()? == EndpointStatus::Halted || self.bulk_out_status.current_status()? == EndpointStatus::Halted { + return Err(ProtocolError::RecoveryFailed) + } + Ok(()) + } } impl<'a> Protocol for BulkOnlyTransport<'a> { - fn send_command_block(&mut self, cb: &[u8]) -> Result<(), ProtocolError> { - todo!() - } - fn recv_command_block(&mut self, cb: &mut [u8]) -> Result<(), ProtocolError> { - todo!() + fn send_command(&mut self, cb: &[u8]) -> Result<(), ProtocolError> { + dbg!(self.bulk_in_status.current_status()?); + dbg!(self.bulk_out_status.current_status()?); + 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); + + let cbw = CommandBlockWrapper { + signature: CBW_SIGNATURE, + tag, + data_transfer_len: 256, // TODO + lun: 0, // TODO + flags: 1 << 7, // TODO + 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"); + } + 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[..])); + + 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"); + } + 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()?); + } + + Ok(()) } } diff --git a/usbscsid/src/protocol/mod.rs b/usbscsid/src/protocol/mod.rs index 986f7801d6..87a0b8eb69 100644 --- a/usbscsid/src/protocol/mod.rs +++ b/usbscsid/src/protocol/mod.rs @@ -1,5 +1,7 @@ +use std::io; + use thiserror::Error; -use xhcid_interface::{XhciClientHandle, XhciClientHandleError}; +use xhcid_interface::{DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError}; #[derive(Debug, Error)] pub enum ProtocolError { @@ -8,11 +10,16 @@ pub enum ProtocolError { #[error("xhcid connection error: {0}")] XhciError(#[from] XhciClientHandleError), + + #[error("i/o error")] + IoError(#[from] io::Error), + + #[error("attempted recovery failed")] + RecoveryFailed, } pub trait Protocol { - fn send_command_block(&mut self, cb: &[u8]) -> Result<(), ProtocolError>; - fn recv_command_block(&mut self, cb: &mut [u8]) -> Result<(), ProtocolError>; + fn send_command(&mut self, command: &[u8]) -> Result<(), ProtocolError>; } /// Bulk-only transport @@ -24,9 +31,9 @@ mod uas { use bot::BulkOnlyTransport; -pub fn setup<'a>(handle: &'a XhciClientHandle, protocol: u8) -> 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).unwrap())), + 0x50 => Some(Box::new(BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap())), _ => None, } } diff --git a/usbscsid/src/scsi/cmds.rs b/usbscsid/src/scsi/cmds.rs index dbd7f83fce..476ed5b758 100644 --- a/usbscsid/src/scsi/cmds.rs +++ b/usbscsid/src/scsi/cmds.rs @@ -13,6 +13,26 @@ pub struct ReportIdentInfo { pub info_ty: u8, pub control: u8, } +impl ReportIdentInfo { + pub fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self { + Self { + opcode: Opcode::ServiceActionA3 as u8, + serviceaction: ServiceActionA3::ReportIdentInfo as u8, + _rsvd: 0, + restricted: 0, + alloc_len: u32::to_le(alloc_len), + info_ty: (info_ty as u8) << REP_ID_INFO_INFO_TY_SHIFT, + control, + } + } +} +#[repr(u8)] +pub enum ReportIdInfoInfoTy { + PeripheralDevIdInfo = 0b000_0000, + PeripheralDevTextIdInfo = 0b000_0010, + IdentInfoSupp = 0b111_1111, + // every other number ending with a 1 is restricted +} pub const REP_ID_INFO_INFO_TY_MASK: u8 = 0xFE; pub const REP_ID_INFO_INFO_TY_SHIFT: u8 = 1; diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index d019d420f9..48c9c2fb0e 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -16,8 +16,9 @@ pub use crate::usb::{EndpointTy, ENDP_ATTR_TY_MASK}; #[derive(Serialize, Deserialize)] pub struct ConfigureEndpointsReq { /// Index into the configuration descriptors of the device descriptor. - pub config_desc: usize, - // TODO: Support multiple alternate interfaces as well. + pub config_desc: u8, + pub interface_desc: Option, + pub alternate_setting: Option, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -65,8 +66,8 @@ pub enum EndpDirection { impl From for EndpDirection { fn from(d: PortReqDirection) -> Self { match d { - PortReqDirection::DeviceToHost => Self::In, PortReqDirection::HostToDevice => Self::Out, + PortReqDirection::DeviceToHost => Self::In, } } } @@ -305,9 +306,6 @@ impl DeviceReqData<'_> { _ => None, } } -} - -impl<'a> DeviceReqData<'a> { pub fn direction(&self) -> PortReqDirection { match self { DeviceReqData::Out(_) => PortReqDirection::HostToDevice, @@ -345,7 +343,7 @@ impl XhciClientHandle { let string = std::fs::read_to_string(path)?; Ok(string.parse()?) } - pub fn endpoint_status( + pub fn endpoint_onetime_status( &self, num: u8, ) -> result::Result { @@ -353,6 +351,13 @@ impl XhciClientHandle { let string = std::fs::read_to_string(path)?; Ok(string.parse()?) } + pub fn open_endpoint_status( + &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 { let path = format!("{}:port{}/endpoints/{}/transfer", self.scheme, self.port, num); Ok(match direction { @@ -413,6 +418,22 @@ impl XhciClientHandle { 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) + } +} + +#[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)))?; + Ok(status.parse::().or(Err(XhciClientHandleError::InvalidResponse(Invalid)))?) + } } #[derive(Debug, Error)] diff --git a/xhcid/src/usb/setup.rs b/xhcid/src/usb/setup.rs index ff793c133f..d62cb5b988 100644 --- a/xhcid/src/usb/setup.rs +++ b/xhcid/src/usb/setup.rs @@ -172,13 +172,22 @@ impl Setup { } } - pub const fn set_configuration(value: u16) -> Self { + pub const fn set_configuration(value: u8) -> Self { Self { kind: 0b0000_0000, request: 0x09, - value: value, + value: value as u16, index: 0, length: 0, } } + pub const fn set_interface(interface: u8, alternate_setting: u8) -> Self { + Self { + kind: 0b0000_0001, + request: 0x09, + value: alternate_setting as u16, + index: interface as u16, + length: 0, + } + } } diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index ea50ba7aff..77b3150142 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -398,6 +398,10 @@ impl Xhci { .collect::>(), }; + if self.cap.cic() { + self.op.set_cie(true); + } + /*match self.spawn_drivers(i, &mut port_state) { Ok(()) => (), Err(err) => println!("Failed to spawn driver for port {}: `{}`", i, err), diff --git a/xhcid/src/xhci/operational.rs b/xhcid/src/xhci/operational.rs index 213cae4185..d7cc3dfc1a 100644 --- a/xhcid/src/xhci/operational.rs +++ b/xhcid/src/xhci/operational.rs @@ -16,10 +16,10 @@ pub struct OperationalRegs { pub const OP_CONFIG_CIE_BIT: u32 = 1 << 9; impl OperationalRegs { - fn cie(&self) -> bool { + pub fn cie(&self) -> bool { self.config.readf(OP_CONFIG_CIE_BIT) } - fn set_cie(&mut self, value: bool) { + pub fn set_cie(&mut self, value: bool) { self.config.writef(OP_CONFIG_CIE_BIT, value) } } diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index 5ba1810407..098c9d4670 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -8,8 +8,8 @@ use smallvec::{smallvec, SmallVec}; use syscall::io::{Dma, Io}; use syscall::scheme::SchemeMut; use syscall::{ - Error, Result, Stat, EACCES, EBADF, EBADMSG, EEXIST, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, - ENOTDIR, ENXIO, EOVERFLOW, EPERM, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, O_CREAT, O_DIRECTORY, + 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, }; @@ -168,7 +168,7 @@ impl AnyDescriptor { } impl Xhci { - fn set_configuration(&mut self, port: usize, config: u16) -> Result<()> { + fn device_req_no_data(&mut self, port: usize, req: usb::Setup) -> Result<()> { let ps = self.port_states.get_mut(&port).ok_or(Error::new(EIO))?; let ring = ps .endpoint_states @@ -180,7 +180,7 @@ impl Xhci { { let (cmd, cycle) = ring.next(); cmd.setup( - usb::Setup::set_configuration(config), + req, TransferKind::NoData, cycle, ); @@ -201,13 +201,13 @@ impl Xhci { if (status >> 24) != TrbCompletionCode::Success as u32 { println!( - "SET_CONFIGURATION ERROR, COMPLETION CODE {:#0x}", + "DEVICE_REQ ERROR, COMPLETION CODE {:#0x}", (status >> 24) ); } println!( - "SET_CONFIGURATION EVENT {:#0x} {:#0x} {:#0x}", + "DEVICE_REQ EVENT {:#0x} {:#0x} {:#0x}", event.data.read(), status, control @@ -218,11 +218,27 @@ impl Xhci { Ok(()) } + 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 configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> { - let req: ConfigureEndpointsReq = + 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) { + //return Err(Error::new(EOPNOTSUPP)); + req.config_desc = 0; + req.alternate_setting = None; + req.interface_desc = None; + } + if req.interface_desc.is_some() != req.alternate_setting.is_some() { + return Err(Error::new(EBADMSG)); + } + let port_state = self.port_states.get_mut(&port).ok_or(Error::new(ENOENT))?; let input_context: &mut Dma = &mut port_state.input_context; @@ -236,7 +252,7 @@ impl Xhci { let current_slot_a = input_context.device.slot.a.read(); let endpoints = - &port_state.dev_desc.config_descs[req.config_desc].interface_descs[0].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)); @@ -249,13 +265,15 @@ 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)); let lec = self.cap.lec(); for index in 0..endpoints.len() as u8 { let endp_num = index + 1; + let xhc_endp_num = endp_num + 1; - input_context.add_context.writef(1 << (endp_num + 1), true); + input_context.add_context.writef(1 << xhc_endp_num, true); let endp_ctx = input_context .device @@ -394,10 +412,14 @@ impl Xhci { let configuration_value = port_state .dev_desc .config_descs - .get(req.config_desc) + .get(req.config_desc as usize) .ok_or(Error::new(EIO))? .configuration_value; - self.set_configuration(port, configuration_value.into())?; + self.set_configuration(port, configuration_value)?; + + if let (Some(interface_num), Some(alternate_setting)) = (req.interface_desc, req.alternate_setting) { + self.set_interface(port, interface_num, alternate_setting)?; + } Ok(()) } @@ -410,6 +432,7 @@ impl Xhci { // TODO: Rename DeviceReqData to something more general. fn transfer(&mut self, port_num: usize, endp_idx: u8, mut buf: DeviceReqData) -> Result { 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), // UB. let dma_buffer = match buf { @@ -424,18 +447,19 @@ impl Xhci { DeviceReqData::NoData => None, }; - let port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?; - 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(EBADF))?; + 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)); } if EndpDirection::from(buf.direction()) != endp_desc.direction() { + dbg!(buf.direction(), endp_desc, endp_idx, endp_num); return Err(Error::new(EBADF)); } - let endp_state = port_state.endpoint_states.get_mut(&endp_idx).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, @@ -462,7 +486,7 @@ impl Xhci { } let stream_id = 1u16; - self.dbs[port_state.slot as usize].write(u32::from(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 event = self.cmd.next_event(); @@ -924,7 +948,7 @@ 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).ok_or(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 { return Err(Error::new(EACCES)); @@ -1092,7 +1116,7 @@ impl SchemeMut for Xhci { &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { EndpointHandleTy::Transfer => { - self.transfer_read(port_num, endp_num, buf)?; + 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()) @@ -1172,7 +1196,7 @@ impl SchemeMut for Xhci { Ok(buf.len()) } &mut Handle::Endpoint(port_num, endp_num, EndpointHandleTy::Transfer) => { - self.transfer_write(port_num, endp_num, buf)?; + self.transfer_write(port_num, endp_num - 1, buf)?; // TODO Ok(buf.len()) }