diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs index f2143676..74126f67 100644 --- a/drivers/usb/xhcid/src/xhci/mod.rs +++ b/drivers/usb/xhcid/src/xhci/mod.rs @@ -311,6 +311,22 @@ struct PortState { input_context: Mutex>>, dev_desc: Option, endpoint_states: BTreeMap, + quirks: crate::usb_quirks::UsbQuirkFlags, + pm_state: PortPmState, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum PortPmState { + Active, + Suspended, +} +impl PortPmState { + pub fn as_str(&self) -> &'static str { + match self { + Self::Active => "active", + Self::Suspended => "suspended", + } + } } impl PortState { @@ -809,6 +825,7 @@ impl Xhci { ); if flags.contains(port::PortFlags::CCS) { + let early_quirks = crate::usb_quirks::lookup_usb_quirks_early(port_id); let slot_ty = match self.supported_protocol(port_id) { Some(protocol) => protocol.proto_slot_ty(), None => { @@ -838,7 +855,15 @@ impl Xhci { debug!("Attempting to address the device"); let mut ring = match self - .address_device(&mut input, port_id, slot_ty, slot, protocol_speed, speed) + .address_device( + &mut input, + port_id, + slot_ty, + slot, + protocol_speed, + speed, + early_quirks, + ) .await { Ok(device_ring) => device_ring, @@ -866,6 +891,8 @@ impl Xhci { }, )) .collect::>(), + quirks: early_quirks, + pm_state: PortPmState::Active, }; self.port_states.insert(port_id, port_state); debug!("Got port states!"); @@ -884,8 +911,14 @@ impl Xhci { debug!("Got the 8 byte dev descriptor: {:X?}", dev_desc_8_byte); let dev_desc = self.get_desc(port_id, slot).await?; + let quirks = early_quirks + | crate::usb_quirks::lookup_usb_quirks(dev_desc.vendor, dev_desc.product); debug!("Got the full device descriptor!"); - self.port_states.get_mut(&port_id).unwrap().dev_desc = Some(dev_desc); + { + let mut port_state = self.port_states.get_mut(&port_id).unwrap(); + port_state.quirks = quirks; + port_state.dev_desc = Some(dev_desc); + } debug!("Got the port states again!"); { @@ -1052,6 +1085,7 @@ impl Xhci { slot: u8, protocol_speed: &ProtocolSpeed, speed: u8, + quirks: crate::usb_quirks::UsbQuirkFlags, ) -> Result { // Collect MTT, parent port number, parent slot ID let mut mtt = false; @@ -1162,11 +1196,16 @@ impl Xhci { let input_context_physical = input_context.physical(); - let (event_trb, _) = self - .execute_command(|trb, cycle| { - trb.address_device(slot, input_context_physical, false, cycle) - }) - .await; + let address_timeout = if quirks.contains(crate::usb_quirks::UsbQuirkFlags::SHORT_SET_ADDR_TIMEOUT) + { + Timeout::from_millis(100) + } else { + Timeout::from_secs(1) + }; + + let (event_trb, _) = self.execute_command_with_timeout(address_timeout, |trb, cycle| { + trb.address_device(slot, input_context_physical, false, cycle) + })?; if event_trb.completion_code() != TrbCompletionCode::Success as u8 { error!( diff --git a/drivers/usb/xhcid/src/xhci/scheme.rs b/drivers/usb/xhcid/src/xhci/scheme.rs index f2d439a4..dfe9fdec 100644 --- a/drivers/usb/xhcid/src/xhci/scheme.rs +++ b/drivers/usb/xhcid/src/xhci/scheme.rs @@ -24,6 +24,7 @@ use std::{cmp, fmt, io, mem, str}; use common::dma::Dma; use futures::executor::block_on; +use futures::FutureExt; use log::{debug, error, info, trace, warn}; use redox_scheme::scheme::SchemeSync; use smallvec::SmallVec; @@ -32,9 +33,9 @@ use common::io::Io; use redox_scheme::{CallerCtx, OpenResult}; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, Result, Stat, EACCES, EBADF, EBADFD, EBADMSG, EINVAL, EIO, EISDIR, ENOENT, ENOSYS, - ENOTDIR, EOPNOTSUPP, EPROTO, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, O_DIRECTORY, O_RDWR, - O_STAT, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, + Error, Result, Stat, EACCES, EBADF, EBADFD, EBADMSG, EBUSY, EINVAL, EIO, EISDIR, ENOENT, + ENOSYS, ENOTDIR, EOPNOTSUPP, EPROTO, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, O_DIRECTORY, + O_RDWR, O_STAT, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET, }; use super::{port, usb}; @@ -60,10 +61,16 @@ lazy_static! { .expect("Failed to create the regex for the port/attach scheme."); static ref REGEX_PORT_DETACH: Regex = Regex::new(r"^port([\d\.]+)/detach$") .expect("Failed to create the regex for the port/detach scheme."); + static ref REGEX_PORT_SUSPEND: Regex = Regex::new(r"^port([\d\.]+)/suspend$") + .expect("Failed to create the regex for the port/suspend scheme."); + static ref REGEX_PORT_RESUME: Regex = Regex::new(r"^port([\d\.]+)/resume$") + .expect("Failed to create the regex for the port/resume scheme."); static ref REGEX_PORT_DESCRIPTORS: Regex = Regex::new(r"^port([\d\.]+)/descriptors$") .expect("Failed to create the regex for the port/descriptors"); static ref REGEX_PORT_STATE: Regex = Regex::new(r"^port([\d\.]+)/state$") .expect("Failed to create the regex for the port/state scheme"); + static ref REGEX_PORT_PM_STATE: Regex = Regex::new(r"^port([\d\.]+)/pm_state$") + .expect("Failed to create the regex for the port/pm_state scheme"); static ref REGEX_PORT_REQUEST: Regex = Regex::new(r"^port([\d\.]+)/request$") .expect("Failed to create the regex for the port/request scheme"); static ref REGEX_PORT_ENDPOINTS: Regex = Regex::new(r"^port([\d\.]+)/endpoints$") @@ -137,12 +144,15 @@ pub enum Handle { Port(PortId, Vec), // port, contents PortDesc(PortId, Vec), // port, contents PortState(PortId), // port + PortPmState(PortId), // port PortReq(PortId, PortReqState), // port, state Endpoints(PortId, Vec), // port, contents Endpoint(PortId, u8, EndpointHandleTy), // port, endpoint, state ConfigureEndpoints(PortId), // port AttachDevice(PortId), // port DetachDevice(PortId), // port + SuspendDevice(PortId), // port + ResumeDevice(PortId), // port SchemeRoot, } @@ -172,6 +182,8 @@ enum SchemeParameters { PortDesc(PortId), // port number /// /port/state PortState(PortId), // port number + /// /port/pm_state + PortPmState(PortId), // port number /// /port/request PortReq(PortId), // port number /// /port/endpoints @@ -187,6 +199,10 @@ enum SchemeParameters { AttachDevice(PortId), // port number /// /port/detach DetachDevice(PortId), // port number + /// /port/suspend + SuspendDevice(PortId), // port number + /// /port/resume + ResumeDevice(PortId), // port number } impl Handle { @@ -209,6 +225,9 @@ impl Handle { Handle::PortState(port_num) => { format!("port{}/state", port_num) } + Handle::PortPmState(port_num) => { + format!("port{}/pm_state", port_num) + } Handle::PortReq(port_num, _) => { format!("port{}/request", port_num) } @@ -235,6 +254,12 @@ impl Handle { Handle::DetachDevice(port_num) => { format!("port{}/detach", port_num) } + Handle::SuspendDevice(port_num) => { + format!("port{}/suspend", port_num) + } + Handle::ResumeDevice(port_num) => { + format!("port{}/resume", port_num) + } Handle::SchemeRoot => String::from(""), } } @@ -258,10 +283,13 @@ impl Handle { &Handle::PortReq(_, PortReqState::Tmp) => unreachable!(), &Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(), &Handle::PortState(_) => HandleType::Character, + &Handle::PortPmState(_) => HandleType::Character, &Handle::PortReq(_, _) => HandleType::Character, &Handle::ConfigureEndpoints(_) => HandleType::Character, &Handle::AttachDevice(_) => HandleType::Character, &Handle::DetachDevice(_) => HandleType::Character, + &Handle::SuspendDevice(_) => HandleType::Character, + &Handle::ResumeDevice(_) => HandleType::Character, &Handle::Endpoint(_, _, ref st) => match st { EndpointHandleTy::Data => HandleType::Character, EndpointHandleTy::Ctl => HandleType::Character, @@ -289,10 +317,13 @@ impl Handle { &Handle::PortReq(_, PortReqState::Tmp) => None, &Handle::PortReq(_, PortReqState::TmpSetup(_)) => None, &Handle::PortState(_) => None, + &Handle::PortPmState(_) => None, &Handle::PortReq(_, _) => None, &Handle::ConfigureEndpoints(_) => None, &Handle::AttachDevice(_) => None, &Handle::DetachDevice(_) => None, + &Handle::SuspendDevice(_) => None, + &Handle::ResumeDevice(_) => None, &Handle::Endpoint(_, _, ref st) => match st { EndpointHandleTy::Data => None, EndpointHandleTy::Ctl => None, @@ -383,6 +414,14 @@ impl SchemeParameters { let port_num = get_port_id_from_regex(®EX_PORT_DETACH, scheme, 0)?; Ok(Self::DetachDevice(port_num)) + } else if REGEX_PORT_SUSPEND.is_match(scheme) { + let port_num = get_port_id_from_regex(®EX_PORT_SUSPEND, scheme, 0)?; + + Ok(Self::SuspendDevice(port_num)) + } else if REGEX_PORT_RESUME.is_match(scheme) { + let port_num = get_port_id_from_regex(®EX_PORT_RESUME, scheme, 0)?; + + Ok(Self::ResumeDevice(port_num)) } else if REGEX_PORT_DESCRIPTORS.is_match(scheme) { let port_num = get_port_id_from_regex(®EX_PORT_DESCRIPTORS, scheme, 0)?; @@ -391,6 +430,10 @@ impl SchemeParameters { let port_num = get_port_id_from_regex(®EX_PORT_STATE, scheme, 0)?; Ok(Self::PortState(port_num)) + } else if REGEX_PORT_PM_STATE.is_match(scheme) { + let port_num = get_port_id_from_regex(®EX_PORT_PM_STATE, scheme, 0)?; + + Ok(Self::PortPmState(port_num)) } else if REGEX_PORT_REQUEST.is_match(scheme) { let port_num = get_port_id_from_regex(®EX_PORT_REQUEST, scheme, 0)?; @@ -564,15 +607,22 @@ impl Xhci { endps: impl IntoIterator, hid_descs: impl IntoIterator, lang_id: u16, + quirks: crate::usb_quirks::UsbQuirkFlags, ) -> Result { Ok(IfDesc { alternate_setting: desc.alternate_setting, class: desc.class, interface_str: if desc.interface_str > 0 { - Some( + if quirks.contains(crate::usb_quirks::UsbQuirkFlags::BAD_DESCRIPTOR) { self.fetch_string_desc(port_id, slot, desc.interface_str, lang_id) - .await?, - ) + .await + .ok() + } else { + Some( + self.fetch_string_desc(port_id, slot, desc.interface_str, lang_id) + .await?, + ) + } } else { None }, @@ -628,6 +678,53 @@ impl Xhci { (event_trb, command_trb) } + pub fn execute_command_with_timeout( + &self, + timeout: common::timeout::Timeout, + f: F, + ) -> Result<(Trb, Trb)> { + if self.interrupt_is_pending(0) { + debug!("The EHB bit is already set!"); + } + + let next_event = { + let mut command_ring = self.cmd.lock().unwrap(); + let (cmd_index, cycle) = (command_ring.next_index(), command_ring.cycle); + + debug!("Sending command with cycle bit {}", cycle as u8); + + { + let command_trb = &mut command_ring.trbs[cmd_index]; + f(command_trb, cycle); + } + + let command_trb = &command_ring.trbs[cmd_index]; + self.next_command_completion_event_trb( + &*command_ring, + command_trb, + EventDoorbell::new(self, 0, 0), + ) + }; + + let mut next_event = Box::pin(next_event); + + loop { + if let Some(trbs) = next_event.as_mut().now_or_never() { + let event_trb = trbs.event_trb; + let command_trb = trbs.src_trb.ok_or(Error::new(EIO))?; + + assert_eq!( + event_trb.trb_type(), + TrbType::CommandCompletion as u8, + "The IRQ reactor (or the xHC) gave an invalid event TRB" + ); + + return Ok((event_trb, command_trb)); + } + + timeout.run().map_err(|()| Error::new(EIO))?; + } + } pub async fn execute_control_transfer( &self, port_num: PortId, @@ -639,6 +736,8 @@ impl Xhci { where D: FnMut(&mut Trb, bool) -> ControlFlow, { + self.ensure_port_active(port_num)?; + let future = { let mut port_state = self.port_state_mut(port_num)?; let slot = port_state.slot; @@ -690,6 +789,20 @@ impl Xhci { handle_transfer_event_trb("CONTROL_TRANSFER", &event_trb, &status_trb)?; + let delay_ctrl_msg = self + .port_states + .get(&port_num) + .map(|port_state| { + port_state + .quirks + .contains(crate::usb_quirks::UsbQuirkFlags::DELAY_CTRL_MSG) + }) + .unwrap_or(false); + + if delay_ctrl_msg { + std::thread::sleep(std::time::Duration::from_millis(20)); + } + //self.event_handler_finished(); Ok(event_trb) @@ -709,6 +822,8 @@ impl Xhci { where D: FnMut(&mut Trb, bool) -> ControlFlow, { + self.ensure_port_active(port_num)?; + let endp_idx = endp_num.checked_sub(1).ok_or(Error::new(EIO))?; let mut port_state = self.port_state_mut(port_num)?; @@ -785,7 +900,29 @@ impl Xhci { let event_trb = trbs.event_trb; let transfer_trb = trbs.src_trb.ok_or(Error::new(EIO))?; - handle_transfer_event_trb("EXECUTE_TRANSFER", &event_trb, &transfer_trb)?; + if let Err(err) = handle_transfer_event_trb("EXECUTE_TRANSFER", &event_trb, &transfer_trb) + { + let need_reset = self + .port_states + .get(&port_num) + .map(|port_state| { + port_state + .quirks + .contains(crate::usb_quirks::UsbQuirkFlags::NEED_RESET) + }) + .unwrap_or(false); + + if need_reset { + if let Err(reset_err) = self.reset_device_slot(port_num).await { + error!( + "EXECUTE_TRANSFER reset recovery failed for port {}: {}", + port_num, reset_err + ); + } + } + + return Err(err); + } // FIXME: EDTLA if event data was set if event_trb.completion_code() != TrbCompletionCode::ShortPacket as u8 @@ -861,6 +998,21 @@ impl Xhci { handle_event_trb("RESET_ENDPOINT", &event_trb, &command_trb) } + async fn reset_device_slot(&self, port_num: PortId) -> Result<()> { + let slot = self + .port_states + .get(&port_num) + .ok_or(Error::new(EBADF))? + .slot; + + let (event_trb, command_trb) = self + .execute_command(|trb, cycle| { + trb.reset_device(slot, cycle); + }) + .await; + + handle_event_trb("RESET_DEVICE", &event_trb, &command_trb) + } fn endp_ctx_interval(speed_id: &ProtocolSpeed, endp_desc: &EndpDesc) -> u8 { /// Logarithmic (base 2) 125 µs periods per millisecond. @@ -1205,7 +1357,19 @@ impl Xhci { } // Tell the device about this configuration. - self.set_configuration(port, configuration_value).await?; + let skip_set_configuration = self + .port_states + .get(&port) + .map(|port_state| { + port_state + .quirks + .contains(crate::usb_quirks::UsbQuirkFlags::NO_SET_CONFIG) + }) + .unwrap_or(false); + + if !skip_set_configuration { + self.set_configuration(port, configuration_value).await?; + } Ok(()) } @@ -1234,8 +1398,20 @@ impl Xhci { if let Some(interface_num) = req.interface_desc { if let Some(alternate_setting) = req.alternate_setting { - self.set_interface(port, interface_num, alternate_setting) - .await?; + let skip_set_interface = self + .port_states + .get(&port) + .map(|port_state| { + port_state + .quirks + .contains(crate::usb_quirks::UsbQuirkFlags::NO_SET_INTF) + }) + .unwrap_or(false); + + if !skip_set_interface { + self.set_interface(port, interface_num, alternate_setting) + .await?; + } } } @@ -1453,52 +1629,109 @@ impl Xhci { let raw_dd = self.fetch_dev_desc(port_id, slot).await?; log::debug!("port {} slot {} desc {:X?}", port_id, slot, raw_dd); + let vendor = raw_dd.vendor; + let product = raw_dd.product; + let quirks = crate::usb_quirks::lookup_usb_quirks(vendor, product); + if !quirks.is_empty() { + log::info!( + "port {}: USB quirks for {:04x}:{:04x}: {:?}", + port_id, vendor, product, quirks + ); + } + // Only fetch language IDs if we need to. Some devices will fail to return this descriptor //TODO: also check configurations and interfaces for defined strings? + let bad_descriptor = quirks.contains(crate::usb_quirks::UsbQuirkFlags::BAD_DESCRIPTOR); + let lang_id = - if raw_dd.manufacturer_str > 0 || raw_dd.product_str > 0 || raw_dd.serial_str > 0 { - let lang_ids = self.fetch_lang_ids_desc(port_id, slot).await?; - // Prefer US English, but fall back to first language ID, or zero - let en_us_id = 0x409; - if lang_ids.contains(&en_us_id) { - en_us_id - } else { - match lang_ids.first() { - Some(some) => *some, - None => 0, + if !quirks.contains(crate::usb_quirks::UsbQuirkFlags::NO_STRING_FETCH) + && (raw_dd.manufacturer_str > 0 + || raw_dd.product_str > 0 + || raw_dd.serial_str > 0) + { + match self.fetch_lang_ids_desc(port_id, slot).await { + Ok(lang_ids) => { + // Prefer US English, but fall back to first language ID, or zero + let en_us_id = 0x409; + if lang_ids.contains(&en_us_id) { + en_us_id + } else { + match lang_ids.first() { + Some(some) => *some, + None => 0, + } + } + } + Err(err) if bad_descriptor => { + log::warn!( + "port {} slot {}: failed to fetch language IDs with BAD_DESCRIPTOR set: {}", + port_id, + slot, + err + ); + 0 } + Err(err) => return Err(err), } } else { 0 }; log::debug!("port {} using language ID 0x{:04x}", port_id, lang_id); - let (manufacturer_str, product_str, serial_str) = ( - if raw_dd.manufacturer_str > 0 { - Some( - self.fetch_string_desc(port_id, slot, raw_dd.manufacturer_str, lang_id) - .await?, - ) - } else { - None - }, - if raw_dd.product_str > 0 { - Some( - self.fetch_string_desc(port_id, slot, raw_dd.product_str, lang_id) - .await?, - ) + let (manufacturer_str, product_str, serial_str) = + if quirks.contains(crate::usb_quirks::UsbQuirkFlags::NO_STRING_FETCH) { + (None, None, None) } else { - None - }, - if raw_dd.serial_str > 0 { - Some( - self.fetch_string_desc(port_id, slot, raw_dd.serial_str, lang_id) - .await?, + ( + if raw_dd.manufacturer_str > 0 { + if bad_descriptor { + self.fetch_string_desc(port_id, slot, raw_dd.manufacturer_str, lang_id) + .await + .ok() + } else { + Some( + self.fetch_string_desc( + port_id, + slot, + raw_dd.manufacturer_str, + lang_id, + ) + .await?, + ) + } + } else { + None + }, + if raw_dd.product_str > 0 { + if bad_descriptor { + self.fetch_string_desc(port_id, slot, raw_dd.product_str, lang_id) + .await + .ok() + } else { + Some( + self.fetch_string_desc(port_id, slot, raw_dd.product_str, lang_id) + .await?, + ) + } + } else { + None + }, + if raw_dd.serial_str > 0 { + if bad_descriptor { + self.fetch_string_desc(port_id, slot, raw_dd.serial_str, lang_id) + .await + .ok() + } else { + Some( + self.fetch_string_desc(port_id, slot, raw_dd.serial_str, lang_id) + .await?, + ) + } + } else { + None + }, ) - } else { - None - }, - ); + }; log::debug!( "manufacturer {:?} product {:?} serial {:?}", manufacturer_str, @@ -1508,14 +1741,39 @@ impl Xhci { //TODO let (bos_desc, bos_data) = self.fetch_bos_desc(port_id, slot).await?; - let supports_superspeed = false; - //TODO usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeed()); - let supports_superspeedplus = false; - //TODO usb::bos_capability_descs(bos_desc, &bos_data).any(|desc| desc.is_superspeedplus()); + let (supports_superspeed, supports_superspeedplus) = + if quirks.contains(crate::usb_quirks::UsbQuirkFlags::NO_BOS) { + (false, false) + } else { + match self.fetch_bos_desc(port_id, slot).await { + Ok((bos_desc, bos_data)) => ( + usb::bos_capability_descs(bos_desc, &bos_data) + .any(|desc| desc.is_superspeed()), + usb::bos_capability_descs(bos_desc, &bos_data) + .any(|desc| desc.is_superspeedplus()), + ), + Err(err) => { + log::debug!( + "port {} slot {}: failed to fetch BOS descriptor: {}", + port_id, + slot, + err + ); + (false, false) + } + } + }; let mut config_descs = SmallVec::new(); - for index in 0..raw_dd.configurations { + let configuration_indices: Vec = + if quirks.contains(crate::usb_quirks::UsbQuirkFlags::FORCE_ONE_CONFIG) { + vec![0] + } else { + (0..raw_dd.configurations).collect() + }; + + for index in configuration_indices { debug!("Fetching the config descriptor at index {}", index); let (desc, data) = self.fetch_config_desc(port_id, slot, index).await?; log::debug!( @@ -1541,6 +1799,12 @@ impl Xhci { let mut iter = descriptors.into_iter().peekable(); while let Some(item) = iter.next() { + if quirks.contains(crate::usb_quirks::UsbQuirkFlags::HONOR_BNUMINTERFACES) + && interface_descs.len() >= desc.interfaces as usize + { + break; + } + if let AnyDescriptor::Interface(idesc) = item { let mut endpoints = SmallVec::<[EndpDesc; 4]>::new(); let mut hid_descs = SmallVec::<[HidDesc; 1]>::new(); @@ -1554,6 +1818,9 @@ impl Xhci { } Some(unexpected) => { log::warn!("expected endpoint, got {:X?}", unexpected); + if bad_descriptor { + continue; + } break; } None => break, @@ -1578,8 +1845,16 @@ impl Xhci { } interface_descs.push( - self.new_if_desc(port_id, slot, idesc, endpoints, hid_descs, lang_id) - .await?, + self.new_if_desc( + port_id, + slot, + idesc, + endpoints, + hid_descs, + lang_id, + quirks, + ) + .await?, ); } else { log::warn!("expected interface, got {:?}", item); @@ -1590,11 +1865,20 @@ impl Xhci { config_descs.push(ConfDesc { kind: desc.kind, - configuration: if desc.configuration_str > 0 { - Some( + configuration: if quirks.contains(crate::usb_quirks::UsbQuirkFlags::NO_STRING_FETCH) + { + None + } else if desc.configuration_str > 0 { + if bad_descriptor { self.fetch_string_desc(port_id, slot, desc.configuration_str, lang_id) - .await?, - ) + .await + .ok() + } else { + Some( + self.fetch_string_desc(port_id, slot, desc.configuration_str, lang_id) + .await?, + ) + } } else { None }, @@ -1856,7 +2140,7 @@ impl Xhci { if (flags & O_DIRECTORY != 0) || (flags & O_STAT != 0) { let mut contents = Vec::new(); - write!(contents, "descriptors\nendpoints\n").unwrap(); + write!(contents, "descriptors\nendpoints\npm_state\nsuspend\nresume\n").unwrap(); if self.slot_state( self.port_states @@ -1893,6 +2177,14 @@ impl Xhci { Ok(Handle::PortState(port_num)) } + fn open_handle_port_pm_state(&self, port_num: PortId, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); + } + + Ok(Handle::PortPmState(port_num)) + } + /// implements open() for /port/endpoints /// /// # Arguments @@ -2087,6 +2379,30 @@ impl Xhci { Ok(Handle::DetachDevice(port_num)) } + fn open_handle_suspend_device(&self, port_num: PortId, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); + } + + if flags & O_RDWR != O_WRONLY && flags & O_STAT == 0 { + return Err(Error::new(EACCES)); + } + + Ok(Handle::SuspendDevice(port_num)) + } + + fn open_handle_resume_device(&self, port_num: PortId, flags: usize) -> Result { + if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 { + return Err(Error::new(ENOTDIR)); + } + + if flags & O_RDWR != O_WRONLY && flags & O_STAT == 0 { + return Err(Error::new(EACCES)); + } + + Ok(Handle::ResumeDevice(port_num)) + } + /// implements open() for /port/request /// /// # Arguments @@ -2155,6 +2471,9 @@ impl SchemeSync for &Xhci { SchemeParameters::PortState(port_number) => { self.open_handle_port_state(port_number, flags)? } + SchemeParameters::PortPmState(port_number) => { + self.open_handle_port_pm_state(port_number, flags)? + } SchemeParameters::PortReq(port_number) => { self.open_handle_port_request(port_number, flags)? } @@ -2173,6 +2492,12 @@ impl SchemeSync for &Xhci { SchemeParameters::DetachDevice(port_number) => { self.open_handle_detach_device(port_number, flags)? } + SchemeParameters::SuspendDevice(port_number) => { + self.open_handle_suspend_device(port_number, flags)? + } + SchemeParameters::ResumeDevice(port_number) => { + self.open_handle_resume_device(port_number, flags)? + } }; let fd = self.next_handle.fetch_add(1, atomic::Ordering::Relaxed); @@ -2203,7 +2528,11 @@ impl SchemeSync for &Xhci { //If we have a handle to the configure scheme, we need to mark it as write only. match &*guard { - Handle::ConfigureEndpoints(_) | Handle::AttachDevice(_) | Handle::DetachDevice(_) => { + Handle::ConfigureEndpoints(_) + | Handle::AttachDevice(_) + | Handle::DetachDevice(_) + | Handle::SuspendDevice(_) + | Handle::ResumeDevice(_) => { stat.st_mode = stat.st_mode | 0o200; } _ => {} @@ -2263,6 +2592,8 @@ impl SchemeSync for &Xhci { Handle::ConfigureEndpoints(_) => Err(Error::new(EBADF)), Handle::AttachDevice(_) => Err(Error::new(EBADF)), Handle::DetachDevice(_) => Err(Error::new(EBADF)), + Handle::SuspendDevice(_) => Err(Error::new(EBADF)), + Handle::ResumeDevice(_) => Err(Error::new(EBADF)), Handle::SchemeRoot => Err(Error::new(EBADF)), &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { @@ -2294,6 +2625,10 @@ impl SchemeSync for &Xhci { Ok(Xhci::::write_dyn_string(string, buf, offset)) } + &mut Handle::PortPmState(port_num) => { + let ps = self.port_states.get(&port_num).ok_or(Error::new(EBADF))?; + Ok(Xhci::::write_dyn_string(ps.pm_state.as_str().as_bytes(), buf, offset)) + } &mut Handle::PortReq(port_num, ref mut st) => { let state = std::mem::replace(st, PortReqState::Tmp); drop(guard); // release the lock @@ -2333,6 +2668,14 @@ impl SchemeSync for &Xhci { block_on(self.detach_device(port_num))?; Ok(buf.len()) } + &mut Handle::SuspendDevice(port_num) => { + block_on(self.suspend_device(port_num))?; + Ok(buf.len()) + } + &mut Handle::ResumeDevice(port_num) => { + block_on(self.resume_device(port_num))?; + Ok(buf.len()) + } &mut Handle::Endpoint(port_num, endp_num, ref ep_file_ty) => match ep_file_ty { EndpointHandleTy::Ctl => block_on(self.on_write_endp_ctl(port_num, endp_num, buf)), EndpointHandleTy::Data => { @@ -2356,6 +2699,38 @@ impl Xhci { self.handles.remove(&fd); } + fn ensure_port_active(&self, port_num: PortId) -> Result<()> { + let pm_state = self + .port_states + .get(&port_num) + .ok_or(Error::new(EBADFD))? + .pm_state; + match pm_state { + super::PortPmState::Active => Ok(()), + super::PortPmState::Suspended => Err(Error::new(EBUSY)), + } + } + + pub async fn suspend_device(&self, port_num: PortId) -> Result<()> { + let mut port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; + + if port_state + .quirks + .contains(crate::usb_quirks::UsbQuirkFlags::NO_SUSPEND) + { + return Err(Error::new(EOPNOTSUPP)); + } + + port_state.pm_state = super::PortPmState::Suspended; + Ok(()) + } + + pub async fn resume_device(&self, port_num: PortId) -> Result<()> { + let mut port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?; + port_state.pm_state = super::PortPmState::Active; + Ok(()) + } + pub fn get_endp_status(&self, port_num: PortId, endp_num: u8) -> Result { let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?; @@ -2406,6 +2781,8 @@ impl Xhci { endp_num: u8, clear_feature: bool, ) -> Result<()> { + self.ensure_port_active(port_num)?; + if self.get_endp_status(port_num, endp_num)? != EndpointStatus::Halted { return Err(Error::new(EPROTO)); } @@ -2562,6 +2939,7 @@ impl Xhci { }, XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state { EndpIfState::Init => { + self.ensure_port_active(port_num)?; self.on_req_reset_device(port_num, endp_num, !no_clear_feature) .await? } @@ -2571,6 +2949,7 @@ impl Xhci { }, XhciEndpCtlReq::Transfer { direction, count } => match ep_if_state { state @ EndpIfState::Init => { + self.ensure_port_active(port_num)?; if direction == XhciEndpCtlDirection::NoData { // Yield the result directly because no bytes have to be sent or received // beforehand. @@ -2631,6 +3010,8 @@ impl Xhci { endp_num: u8, buf: &[u8], ) -> Result { + self.ensure_port_active(port_num)?; + let mut port_state = self .port_states .get_mut(&port_num) @@ -2732,6 +3113,8 @@ impl Xhci { endp_num: u8, buf: &mut [u8], ) -> Result { + self.ensure_port_active(port_num)?; + let mut port_state = self .port_states .get_mut(&port_num)