Advance base patch with acpid and xhcid fixes
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,942 @@
|
||||
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<const N: usize> {
|
||||
input_context: Mutex<Dma<InputContext<N>>>,
|
||||
dev_desc: Option<DevDesc>,
|
||||
endpoint_states: BTreeMap<u8, EndpointState>,
|
||||
+ 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<const N: usize> PortState<N> {
|
||||
@@ -809,6 +825,7 @@ impl<const N: usize> Xhci<N> {
|
||||
);
|
||||
|
||||
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<const N: usize> Xhci<N> {
|
||||
|
||||
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<const N: usize> Xhci<N> {
|
||||
},
|
||||
))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
+ quirks: early_quirks,
|
||||
+ pm_state: PortPmState::Active,
|
||||
};
|
||||
self.port_states.insert(port_id, port_state);
|
||||
debug!("Got port states!");
|
||||
@@ -884,8 +911,14 @@ impl<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
slot: u8,
|
||||
protocol_speed: &ProtocolSpeed,
|
||||
speed: u8,
|
||||
+ quirks: crate::usb_quirks::UsbQuirkFlags,
|
||||
) -> Result<Ring> {
|
||||
// Collect MTT, parent port number, parent slot ID
|
||||
let mut mtt = false;
|
||||
@@ -1162,11 +1196,16 @@ impl<const N: usize> Xhci<N> {
|
||||
|
||||
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<n>/attach scheme.");
|
||||
static ref REGEX_PORT_DETACH: Regex = Regex::new(r"^port([\d\.]+)/detach$")
|
||||
.expect("Failed to create the regex for the port<n>/detach scheme.");
|
||||
+ static ref REGEX_PORT_SUSPEND: Regex = Regex::new(r"^port([\d\.]+)/suspend$")
|
||||
+ .expect("Failed to create the regex for the port<n>/suspend scheme.");
|
||||
+ static ref REGEX_PORT_RESUME: Regex = Regex::new(r"^port([\d\.]+)/resume$")
|
||||
+ .expect("Failed to create the regex for the port<n>/resume scheme.");
|
||||
static ref REGEX_PORT_DESCRIPTORS: Regex = Regex::new(r"^port([\d\.]+)/descriptors$")
|
||||
.expect("Failed to create the regex for the port<n>/descriptors");
|
||||
static ref REGEX_PORT_STATE: Regex = Regex::new(r"^port([\d\.]+)/state$")
|
||||
.expect("Failed to create the regex for the port<n>/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<n>/pm_state scheme");
|
||||
static ref REGEX_PORT_REQUEST: Regex = Regex::new(r"^port([\d\.]+)/request$")
|
||||
.expect("Failed to create the regex for the port<n>/request scheme");
|
||||
static ref REGEX_PORT_ENDPOINTS: Regex = Regex::new(r"^port([\d\.]+)/endpoints$")
|
||||
@@ -137,12 +144,15 @@ pub enum Handle {
|
||||
Port(PortId, Vec<u8>), // port, contents
|
||||
PortDesc(PortId, Vec<u8>), // port, contents
|
||||
PortState(PortId), // port
|
||||
+ PortPmState(PortId), // port
|
||||
PortReq(PortId, PortReqState), // port, state
|
||||
Endpoints(PortId, Vec<u8>), // 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<n>/state
|
||||
PortState(PortId), // port number
|
||||
+ /// /port<n>/pm_state
|
||||
+ PortPmState(PortId), // port number
|
||||
/// /port<n>/request
|
||||
PortReq(PortId), // port number
|
||||
/// /port<n>/endpoints
|
||||
@@ -187,6 +199,10 @@ enum SchemeParameters {
|
||||
AttachDevice(PortId), // port number
|
||||
/// /port<n>/detach
|
||||
DetachDevice(PortId), // port number
|
||||
+ /// /port<n>/suspend
|
||||
+ SuspendDevice(PortId), // port number
|
||||
+ /// /port<n>/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<const N: usize> Xhci<N> {
|
||||
endps: impl IntoIterator<Item = EndpDesc>,
|
||||
hid_descs: impl IntoIterator<Item = HidDesc>,
|
||||
lang_id: u16,
|
||||
+ quirks: crate::usb_quirks::UsbQuirkFlags,
|
||||
) -> Result<IfDesc> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
|
||||
(event_trb, command_trb)
|
||||
}
|
||||
+ pub fn execute_command_with_timeout<F: FnOnce(&mut Trb, bool)>(
|
||||
+ &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<D>(
|
||||
&self,
|
||||
port_num: PortId,
|
||||
@@ -639,6 +736,8 @@ impl<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
|
||||
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<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
|
||||
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<const N: usize> Xhci<N> {
|
||||
}
|
||||
|
||||
// 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<const N: usize> Xhci<N> {
|
||||
|
||||
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<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
|
||||
//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<u8> =
|
||||
+ 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<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
}
|
||||
Some(unexpected) => {
|
||||
log::warn!("expected endpoint, got {:X?}", unexpected);
|
||||
+ if bad_descriptor {
|
||||
+ continue;
|
||||
+ }
|
||||
break;
|
||||
}
|
||||
None => break,
|
||||
@@ -1578,8 +1845,16 @@ impl<const N: usize> Xhci<N> {
|
||||
}
|
||||
|
||||
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<const N: usize> Xhci<N> {
|
||||
|
||||
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<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
Ok(Handle::PortState(port_num))
|
||||
}
|
||||
|
||||
+ fn open_handle_port_pm_state(&self, port_num: PortId, flags: usize) -> Result<Handle> {
|
||||
+ if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 {
|
||||
+ return Err(Error::new(ENOTDIR));
|
||||
+ }
|
||||
+
|
||||
+ Ok(Handle::PortPmState(port_num))
|
||||
+ }
|
||||
+
|
||||
/// implements open() for /port<n>/endpoints
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -2087,6 +2379,30 @@ impl<const N: usize> Xhci<N> {
|
||||
Ok(Handle::DetachDevice(port_num))
|
||||
}
|
||||
|
||||
+ fn open_handle_suspend_device(&self, port_num: PortId, flags: usize) -> Result<Handle> {
|
||||
+ 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<Handle> {
|
||||
+ 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<n>/request
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -2155,6 +2471,9 @@ impl<const N: usize> SchemeSync for &Xhci<N> {
|
||||
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<const N: usize> SchemeSync for &Xhci<N> {
|
||||
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<const N: usize> SchemeSync for &Xhci<N> {
|
||||
|
||||
//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<const N: usize> SchemeSync for &Xhci<N> {
|
||||
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<const N: usize> SchemeSync for &Xhci<N> {
|
||||
|
||||
Ok(Xhci::<N>::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::<N>::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<const N: usize> SchemeSync for &Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
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<EndpointStatus> {
|
||||
let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?;
|
||||
|
||||
@@ -2406,6 +2781,8 @@ impl<const N: usize> Xhci<N> {
|
||||
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<const N: usize> Xhci<N> {
|
||||
},
|
||||
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<const N: usize> Xhci<N> {
|
||||
},
|
||||
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<const N: usize> Xhci<N> {
|
||||
endp_num: u8,
|
||||
buf: &[u8],
|
||||
) -> Result<usize> {
|
||||
+ self.ensure_port_active(port_num)?;
|
||||
+
|
||||
let mut port_state = self
|
||||
.port_states
|
||||
.get_mut(&port_num)
|
||||
@@ -2732,6 +3113,8 @@ impl<const N: usize> Xhci<N> {
|
||||
endp_num: u8,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize> {
|
||||
+ self.ensure_port_active(port_num)?;
|
||||
+
|
||||
let mut port_state = self
|
||||
.port_states
|
||||
.get_mut(&port_num)
|
||||
+5121
-2497
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ mkdir -pv "${COOKBOOK_STAGE}/usr/bin"
|
||||
for package in audiod ipcd ptyd dhcpd; do
|
||||
"${COOKBOOK_CARGO}" build \
|
||||
--manifest-path "${COOKBOOK_SOURCE}/${package}/Cargo.toml" \
|
||||
--target "${TARGET}" \
|
||||
${build_flags}
|
||||
cp -v \
|
||||
"target/${TARGET}/${build_type}/${package}" \
|
||||
@@ -17,6 +18,7 @@ done
|
||||
|
||||
"${COOKBOOK_CARGO}" build \
|
||||
--manifest-path "${COOKBOOK_SOURCE}/netstack/Cargo.toml" \
|
||||
--target "${TARGET}" \
|
||||
${build_flags}
|
||||
cp -v \
|
||||
"target/${TARGET}/${build_type}/smolnetd" \
|
||||
@@ -58,6 +60,7 @@ export CARGO_PROFILE_RELEASE_OPT_LEVEL=s
|
||||
export CARGO_PROFILE_RELEASE_PANIC=abort
|
||||
"${COOKBOOK_CARGO}" build ${build_flags} \
|
||||
--manifest-path "${COOKBOOK_SOURCE}/Cargo.toml" \
|
||||
--target "${TARGET}" \
|
||||
$(for bin in "${BINS[@]}"; do echo "-p" "${bin}"; done)
|
||||
for bin in "${BINS[@]}"
|
||||
do
|
||||
|
||||
Reference in New Issue
Block a user