36afba773a
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
3805 lines
150 KiB
Diff
3805 lines
150 KiB
Diff
diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs
|
|
index 94a1eb17..a735a4e7 100644
|
|
--- a/drivers/acpid/src/acpi.rs
|
|
+++ b/drivers/acpid/src/acpi.rs
|
|
@@ -25,6 +25,8 @@ use amlserde::{AmlSerde, AmlSerdeValue};
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
pub mod dmar;
|
|
+#[cfg(target_arch = "x86_64")]
|
|
+use self::dmar::Dmar;
|
|
use crate::aml_physmem::{AmlPageCache, AmlPhysMemHandler};
|
|
|
|
/// The raw SDT header struct, as defined by the ACPI specification.
|
|
@@ -379,6 +381,12 @@ pub struct AcpiContext {
|
|
tables: Vec<Sdt>,
|
|
dsdt: Option<Dsdt>,
|
|
fadt: Option<Fadt>,
|
|
+ pm1a_cnt_blk: u64,
|
|
+ pm1b_cnt_blk: u64,
|
|
+ slp_typa_s5: u8,
|
|
+ slp_typb_s5: u8,
|
|
+ reset_reg: Option<GenericAddress>,
|
|
+ reset_value: u8,
|
|
|
|
aml_symbols: RwLock<AmlSymbols>,
|
|
|
|
@@ -424,6 +432,63 @@ impl AcpiContext {
|
|
.flatten()
|
|
}
|
|
|
|
+ pub fn evaluate_acpi_method(
|
|
+ &mut self,
|
|
+ path: &str,
|
|
+ method: &str,
|
|
+ args: &[u64],
|
|
+ ) -> Result<Vec<u64>, AmlEvalError> {
|
|
+ let full_path = format!("{path}.{method}");
|
|
+ let aml_name =
|
|
+ AmlName::from_str(&full_path).map_err(|_| AmlEvalError::DeserializationError)?;
|
|
+ let args = args
|
|
+ .iter()
|
|
+ .copied()
|
|
+ .map(AmlSerdeValue::Integer)
|
|
+ .collect::<Vec<_>>();
|
|
+
|
|
+ match self.aml_eval(aml_name, args)? {
|
|
+ AmlSerdeValue::Integer(value) => Ok(vec![value]),
|
|
+ AmlSerdeValue::Package { contents } => contents
|
|
+ .into_iter()
|
|
+ .map(|value| match value {
|
|
+ AmlSerdeValue::Integer(value) => Ok(value),
|
|
+ _ => Err(AmlEvalError::DeserializationError),
|
|
+ })
|
|
+ .collect(),
|
|
+ _ => Err(AmlEvalError::DeserializationError),
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pub fn device_power_on(&mut self, device_path: &str) {
|
|
+ match self.evaluate_acpi_method(device_path, "_PS0", &[]) {
|
|
+ Ok(values) => {
|
|
+ log::debug!("{}._PS0 => {:?}", device_path, values);
|
|
+ }
|
|
+ Err(error) => {
|
|
+ log::warn!("Failed to power on {} with _PS0: {:?}", device_path, error);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pub fn device_power_off(&mut self, device_path: &str) {
|
|
+ match self.evaluate_acpi_method(device_path, "_PS3", &[]) {
|
|
+ Ok(values) => {
|
|
+ log::debug!("{}._PS3 => {:?}", device_path, values);
|
|
+ }
|
|
+ Err(error) => {
|
|
+ log::warn!("Failed to power off {} with _PS3: {:?}", device_path, error);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pub fn device_get_performance(&mut self, device_path: &str) -> Result<u64, AmlEvalError> {
|
|
+ self.evaluate_acpi_method(device_path, "_PPC", &[])?
|
|
+ .into_iter()
|
|
+ .next()
|
|
+ .ok_or(AmlEvalError::DeserializationError)
|
|
+ }
|
|
+
|
|
pub fn init(
|
|
rxsdt_physaddrs: impl Iterator<Item = u64>,
|
|
ec: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
|
|
@@ -444,6 +509,12 @@ impl AcpiContext {
|
|
tables,
|
|
dsdt: None,
|
|
fadt: None,
|
|
+ pm1a_cnt_blk: 0,
|
|
+ pm1b_cnt_blk: 0,
|
|
+ slp_typa_s5: 0,
|
|
+ slp_typb_s5: 0,
|
|
+ reset_reg: None,
|
|
+ reset_value: 0,
|
|
|
|
// Temporary values
|
|
aml_symbols: RwLock::new(AmlSymbols::new(ec)),
|
|
@@ -458,7 +529,10 @@ impl AcpiContext {
|
|
}
|
|
|
|
Fadt::init(&mut this);
|
|
- //TODO (hangs on real hardware): Dmar::init(&this);
|
|
+ // DMAR (Intel VT-d) init — previously disabled due to iterator bug (type_bytes copied
|
|
+ // instead of len_bytes in DmarRawIter). Safe to call now: on AMD systems, no DMAR table
|
|
+ // exists and this returns early with a warning.
|
|
+ Dmar::init(&this);
|
|
|
|
this
|
|
}
|
|
@@ -562,92 +636,83 @@ impl AcpiContext {
|
|
aml_symbols.symbol_cache = FxHashMap::default();
|
|
}
|
|
|
|
- /// Set Power State
|
|
- /// See https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf
|
|
- /// - search for PM1a
|
|
- /// See https://forum.osdev.org/viewtopic.php?t=16990 for practical details
|
|
- pub fn set_global_s_state(&self, state: u8) {
|
|
- if state != 5 {
|
|
- return;
|
|
- }
|
|
- let fadt = match self.fadt() {
|
|
- Some(fadt) => fadt,
|
|
- None => {
|
|
- log::error!("Cannot set global S-state due to missing FADT.");
|
|
- return;
|
|
- }
|
|
- };
|
|
-
|
|
- let port = fadt.pm1a_control_block as u16;
|
|
- let mut val = 1 << 13;
|
|
-
|
|
- let aml_symbols = self.aml_symbols.read();
|
|
+ pub fn acpi_shutdown(&self) {
|
|
+ let pm1a_value = (u16::from(self.slp_typa_s5) << 10) | 0x2000;
|
|
+ let pm1b_value = (u16::from(self.slp_typb_s5) << 10) | 0x2000;
|
|
|
|
- let s5_aml_name = match acpi::aml::namespace::AmlName::from_str("\\_S5") {
|
|
- Ok(aml_name) => aml_name,
|
|
- Err(error) => {
|
|
- log::error!("Could not build AmlName for \\_S5, {:?}", error);
|
|
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
+ {
|
|
+ let Ok(pm1a_port) = u16::try_from(self.pm1a_cnt_blk) else {
|
|
+ log::error!("PM1a_CNT_BLK address is invalid: {:#X}", self.pm1a_cnt_blk);
|
|
return;
|
|
- }
|
|
- };
|
|
+ };
|
|
|
|
- let s5 = match &aml_symbols.aml_context {
|
|
- Some(aml_context) => match aml_context.namespace.lock().get(s5_aml_name) {
|
|
- Ok(s5) => s5,
|
|
- Err(error) => {
|
|
- log::error!("Cannot set S-state, missing \\_S5, {:?}", error);
|
|
- return;
|
|
+ log::warn!(
|
|
+ "Shutdown with ACPI PM1a_CNT outw(0x{:X}, 0x{:X})",
|
|
+ pm1a_port,
|
|
+ pm1a_value
|
|
+ );
|
|
+ Pio::<u16>::new(pm1a_port).write(pm1a_value);
|
|
+
|
|
+ if self.pm1b_cnt_blk != 0 {
|
|
+ match u16::try_from(self.pm1b_cnt_blk) {
|
|
+ Ok(pm1b_port) => {
|
|
+ log::warn!(
|
|
+ "Shutdown with ACPI PM1b_CNT outw(0x{:X}, 0x{:X})",
|
|
+ pm1b_port,
|
|
+ pm1b_value
|
|
+ );
|
|
+ Pio::<u16>::new(pm1b_port).write(pm1b_value);
|
|
+ }
|
|
+ Err(_) => {
|
|
+ log::error!("PM1b_CNT_BLK address is invalid: {:#X}", self.pm1b_cnt_blk);
|
|
+ }
|
|
}
|
|
- },
|
|
- None => {
|
|
- log::error!("Cannot set S-state, AML context not initialized");
|
|
- return;
|
|
}
|
|
- };
|
|
+ }
|
|
|
|
- let package = match s5.deref() {
|
|
- acpi::aml::object::Object::Package(package) => package,
|
|
- _ => {
|
|
- log::error!("Cannot set S-state, \\_S5 is not a package");
|
|
- return;
|
|
- }
|
|
- };
|
|
+ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
+ {
|
|
+ log::error!(
|
|
+ "Cannot shutdown with ACPI PM1_CNT writes on this architecture (PM1a={:#X}, PM1b={:#X})",
|
|
+ self.pm1a_cnt_blk,
|
|
+ self.pm1b_cnt_blk
|
|
+ );
|
|
+ }
|
|
+ }
|
|
|
|
- let slp_typa = match package[0].deref() {
|
|
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
|
|
- _ => {
|
|
- log::error!("typa is not an Integer");
|
|
- return;
|
|
+ pub fn acpi_reboot(&self) {
|
|
+ match self.reset_reg {
|
|
+ Some(reset_reg) => {
|
|
+ log::warn!(
|
|
+ "Reboot with ACPI reset register {:?} value {:#X}",
|
|
+ reset_reg,
|
|
+ self.reset_value
|
|
+ );
|
|
+ reset_reg.write_u8(self.reset_value);
|
|
}
|
|
- };
|
|
- let slp_typb = match package[1].deref() {
|
|
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
|
|
- _ => {
|
|
- log::error!("typb is not an Integer");
|
|
- return;
|
|
+ None => {
|
|
+ log::error!("Cannot reboot with ACPI: no reset register present in FADT");
|
|
}
|
|
- };
|
|
-
|
|
- log::trace!("Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", slp_typa, slp_typb);
|
|
- val |= slp_typa as u16;
|
|
-
|
|
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
- {
|
|
- log::warn!("Shutdown with ACPI outw(0x{:X}, 0x{:X})", port, val);
|
|
- Pio::<u16>::new(port).write(val);
|
|
}
|
|
+ }
|
|
|
|
- // TODO: Handle SLP_TYPb
|
|
+ /// Set Power State
|
|
+ /// See https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf
|
|
+ /// - search for PM1a
|
|
+ /// See https://forum.osdev.org/viewtopic.php?t=16990 for practical details
|
|
+ pub fn set_global_s_state(&self, state: u8) {
|
|
+ if state != 5 {
|
|
+ return;
|
|
+ }
|
|
|
|
- #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
- {
|
|
- log::error!(
|
|
- "Cannot shutdown with ACPI outw(0x{:X}, 0x{:X}) on this architecture",
|
|
- port,
|
|
- val
|
|
- );
|
|
+ if self.fadt().is_none() {
|
|
+ log::error!("Cannot set global S-state due to missing FADT.");
|
|
+ return;
|
|
}
|
|
|
|
+ self.acpi_shutdown();
|
|
+
|
|
loop {
|
|
core::hint::spin_loop();
|
|
}
|
|
@@ -707,7 +772,7 @@ unsafe impl plain::Plain for FadtStruct {}
|
|
|
|
#[repr(C, packed)]
|
|
#[derive(Clone, Copy, Debug, Default)]
|
|
-pub struct GenericAddressStructure {
|
|
+pub struct GenericAddress {
|
|
address_space: u8,
|
|
bit_width: u8,
|
|
bit_offset: u8,
|
|
@@ -715,11 +780,77 @@ pub struct GenericAddressStructure {
|
|
address: u64,
|
|
}
|
|
|
|
+impl GenericAddress {
|
|
+ pub fn is_empty(&self) -> bool {
|
|
+ self.address == 0
|
|
+ }
|
|
+
|
|
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
+ pub fn write_u8(&self, value: u8) {
|
|
+ match self.address_space {
|
|
+ 0 => {
|
|
+ let raw_address = self.address;
|
|
+ let Ok(address) = usize::try_from(raw_address) else {
|
|
+ log::error!(
|
|
+ "Reset register physical address is invalid: {:#X}",
|
|
+ raw_address
|
|
+ );
|
|
+ return;
|
|
+ };
|
|
+ let page = address / PAGE_SIZE * PAGE_SIZE;
|
|
+ let offset = address % PAGE_SIZE;
|
|
+ let virt = unsafe {
|
|
+ common::physmap(
|
|
+ page,
|
|
+ PAGE_SIZE,
|
|
+ common::Prot::RW,
|
|
+ common::MemoryType::default(),
|
|
+ )
|
|
+ };
|
|
+
|
|
+ match virt {
|
|
+ Ok(virt) => unsafe {
|
|
+ (virt as *mut u8).add(offset).write_volatile(value);
|
|
+ let _ = libredox::call::munmap(virt, PAGE_SIZE);
|
|
+ },
|
|
+ Err(error) => {
|
|
+ log::error!("Failed to map ACPI reset register: {}", error);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ 1 => match u16::try_from(self.address) {
|
|
+ Ok(port) => {
|
|
+ Pio::<u8>::new(port).write(value);
|
|
+ }
|
|
+ Err(_) => {
|
|
+ let raw_address = self.address;
|
|
+ log::error!("Reset register I/O port is invalid: {:#X}", raw_address);
|
|
+ }
|
|
+ },
|
|
+ address_space => {
|
|
+ log::warn!(
|
|
+ "Unsupported ACPI reset register address space {} for {:?}",
|
|
+ address_space,
|
|
+ self
|
|
+ );
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
+ pub fn write_u8(&self, _value: u8) {
|
|
+ log::error!(
|
|
+ "Cannot access ACPI reset register {:?} on this architecture",
|
|
+ self
|
|
+ );
|
|
+ }
|
|
+}
|
|
+
|
|
#[repr(C, packed)]
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct FadtAcpi2Struct {
|
|
// 12 byte structure; see below for details
|
|
- pub reset_reg: GenericAddressStructure,
|
|
+ pub reset_reg: GenericAddress,
|
|
|
|
pub reset_value: u8,
|
|
reserved3: [u8; 3],
|
|
@@ -728,14 +859,14 @@ pub struct FadtAcpi2Struct {
|
|
pub x_firmware_control: u64,
|
|
pub x_dsdt: u64,
|
|
|
|
- pub x_pm1a_event_block: GenericAddressStructure,
|
|
- pub x_pm1b_event_block: GenericAddressStructure,
|
|
- pub x_pm1a_control_block: GenericAddressStructure,
|
|
- pub x_pm1b_control_block: GenericAddressStructure,
|
|
- pub x_pm2_control_block: GenericAddressStructure,
|
|
- pub x_pm_timer_block: GenericAddressStructure,
|
|
- pub x_gpe0_block: GenericAddressStructure,
|
|
- pub x_gpe1_block: GenericAddressStructure,
|
|
+ pub x_pm1a_event_block: GenericAddress,
|
|
+ pub x_pm1b_event_block: GenericAddress,
|
|
+ pub x_pm1a_control_block: GenericAddress,
|
|
+ pub x_pm1b_control_block: GenericAddress,
|
|
+ pub x_pm2_control_block: GenericAddress,
|
|
+ pub x_pm_timer_block: GenericAddress,
|
|
+ pub x_gpe0_block: GenericAddress,
|
|
+ pub x_gpe1_block: GenericAddress,
|
|
}
|
|
unsafe impl plain::Plain for FadtAcpi2Struct {}
|
|
|
|
@@ -793,9 +924,27 @@ impl Fadt {
|
|
None => usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize"),
|
|
};
|
|
|
|
- log::debug!("FACP at {:X}", { dsdt_ptr });
|
|
+ let pm1a_evt_blk = u64::from(fadt.pm1a_event_block);
|
|
+ let pm1b_evt_blk = u64::from(fadt.pm1b_event_block);
|
|
+ let pm1a_cnt_blk = u64::from(fadt.pm1a_control_block);
|
|
+ let pm1b_cnt_blk = u64::from(fadt.pm1b_control_block);
|
|
+ let (reset_reg, reset_value) = match fadt.acpi_2_struct() {
|
|
+ Some(fadt2) if !fadt2.reset_reg.is_empty() => {
|
|
+ (Some(fadt2.reset_reg), fadt2.reset_value)
|
|
+ }
|
|
+ _ => (None, 0),
|
|
+ };
|
|
|
|
- let dsdt_sdt = match Sdt::load_from_physical(fadt.dsdt as usize) {
|
|
+ log::debug!("FACP at {:X}", { dsdt_ptr });
|
|
+ log::debug!(
|
|
+ "FADT power blocks: PM1a_EVT={:#X}, PM1b_EVT={:#X}, PM1a_CNT={:#X}, PM1b_CNT={:#X}",
|
|
+ pm1a_evt_blk,
|
|
+ pm1b_evt_blk,
|
|
+ pm1a_cnt_blk,
|
|
+ pm1b_cnt_blk
|
|
+ );
|
|
+
|
|
+ let dsdt_sdt = match Sdt::load_from_physical(dsdt_ptr) {
|
|
Ok(dsdt) => dsdt,
|
|
Err(error) => {
|
|
log::error!("Failed to load DSDT: {}", error);
|
|
@@ -803,8 +952,48 @@ impl Fadt {
|
|
}
|
|
};
|
|
|
|
+ let (slp_typa_s5, slp_typb_s5) = match AmlName::from_str("\\_S5") {
|
|
+ Ok(s5_name) => match context.aml_eval(s5_name, Vec::new()) {
|
|
+ Ok(AmlSerdeValue::Package { contents }) => match (contents.get(0), contents.get(1))
|
|
+ {
|
|
+ (
|
|
+ Some(AmlSerdeValue::Integer(slp_typa)),
|
|
+ Some(AmlSerdeValue::Integer(slp_typb)),
|
|
+ ) => match (u8::try_from(*slp_typa), u8::try_from(*slp_typb)) {
|
|
+ (Ok(slp_typa_s5), Ok(slp_typb_s5)) => (slp_typa_s5, slp_typb_s5),
|
|
+ _ => {
|
|
+ log::warn!("\\_S5 values do not fit in u8: {:?}", contents);
|
|
+ (0, 0)
|
|
+ }
|
|
+ },
|
|
+ _ => {
|
|
+ log::warn!("\\_S5 package did not contain two integers: {:?}", contents);
|
|
+ (0, 0)
|
|
+ }
|
|
+ },
|
|
+ Ok(value) => {
|
|
+ log::warn!("\\_S5 returned unexpected AML value: {:?}", value);
|
|
+ (0, 0)
|
|
+ }
|
|
+ Err(error) => {
|
|
+ log::warn!("Failed to evaluate \\_S5: {:?}", error);
|
|
+ (0, 0)
|
|
+ }
|
|
+ },
|
|
+ Err(error) => {
|
|
+ log::warn!("Could not build AmlName for \\_S5: {:?}", error);
|
|
+ (0, 0)
|
|
+ }
|
|
+ };
|
|
+
|
|
context.fadt = Some(fadt.clone());
|
|
context.dsdt = Some(Dsdt(dsdt_sdt.clone()));
|
|
+ context.pm1a_cnt_blk = pm1a_cnt_blk;
|
|
+ context.pm1b_cnt_blk = pm1b_cnt_blk;
|
|
+ context.slp_typa_s5 = slp_typa_s5;
|
|
+ context.slp_typb_s5 = slp_typb_s5;
|
|
+ context.reset_reg = reset_reg;
|
|
+ context.reset_value = reset_value;
|
|
|
|
context.tables.push(dsdt_sdt);
|
|
}
|
|
diff --git a/drivers/acpid/src/acpi/dmar/mod.rs b/drivers/acpid/src/acpi/dmar/mod.rs
|
|
index c42b379a..f4dff276 100644
|
|
--- a/drivers/acpid/src/acpi/dmar/mod.rs
|
|
+++ b/drivers/acpid/src/acpi/dmar/mod.rs
|
|
@@ -474,10 +474,13 @@ impl<'sdt> Iterator for DmarRawIter<'sdt> {
|
|
let len_bytes = <[u8; 2]>::try_from(type_bytes)
|
|
.expect("expected a 2-byte slice to be convertible to [u8; 2]");
|
|
|
|
- let ty = u16::from_ne_bytes(type_bytes);
|
|
- let len = u16::from_ne_bytes(len_bytes);
|
|
+ let len = u16::from_ne_bytes(len_bytes) as usize;
|
|
|
|
- let len = usize::try_from(len).expect("expected u16 to fit within usize");
|
|
+ if len < 4 {
|
|
+ return None;
|
|
+ }
|
|
+
|
|
+ let ty = u16::from_ne_bytes(type_bytes);
|
|
|
|
if len > remainder.len() {
|
|
log::warn!("DMAR remapping structure length was smaller than the remaining length of the table.");
|
|
diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs
|
|
index 15c5b778..68f8689c 100644
|
|
--- a/drivers/input/usbhidd/src/main.rs
|
|
+++ b/drivers/input/usbhidd/src/main.rs
|
|
@@ -247,7 +247,13 @@ fn main() -> Result<()> {
|
|
reqs::set_idle(&handle, 1, 0, interface_num as u16).context("Failed to set idle")?;
|
|
|
|
let report_desc_len = hid_desc.desc_len;
|
|
- assert_eq!(hid_desc.desc_ty, REPORT_DESC_TY);
|
|
+ if hid_desc.desc_ty != REPORT_DESC_TY {
|
|
+ anyhow::bail!(
|
|
+ "unexpected HID descriptor type {:X}, expected {:X}",
|
|
+ hid_desc.desc_ty,
|
|
+ REPORT_DESC_TY
|
|
+ );
|
|
+ }
|
|
|
|
let mut report_desc_bytes = vec![0u8; report_desc_len as usize];
|
|
handle
|
|
@@ -261,8 +267,8 @@ fn main() -> Result<()> {
|
|
)
|
|
.context("Failed to retrieve report descriptor")?;
|
|
|
|
- let mut handler =
|
|
- ReportHandler::new(&report_desc_bytes).expect("failed to parse report descriptor");
|
|
+ let mut handler = ReportHandler::new(&report_desc_bytes)
|
|
+ .map_err(|e| anyhow::anyhow!("failed to parse report descriptor: {}", e))?;
|
|
|
|
let report_len = match endp_desc_opt {
|
|
Some((_endp_num, endp_desc)) => endp_desc.max_packet_size as usize,
|
|
@@ -318,10 +324,14 @@ fn main() -> Result<()> {
|
|
let mut mouse_dy = 0i32;
|
|
let mut scroll_y = 0i32;
|
|
let mut buttons = last_buttons;
|
|
- for event in handler
|
|
- .handle(&report_buffer)
|
|
- .expect("failed to parse report")
|
|
- {
|
|
+ let events = match handler.handle(&report_buffer) {
|
|
+ Ok(events) => events,
|
|
+ Err(err) => {
|
|
+ log::warn!("failed to parse report: {}", err);
|
|
+ continue;
|
|
+ }
|
|
+ };
|
|
+ for event in events {
|
|
log::debug!("{}", event);
|
|
if event.usage_page == UsagePage::GenericDesktop as u16 {
|
|
if event.usage == GenericDesktopUsage::X as u16 {
|
|
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
|
|
index c2caf804..95acdb57 100644
|
|
--- a/drivers/pcid/src/scheme.rs
|
|
+++ b/drivers/pcid/src/scheme.rs
|
|
@@ -21,6 +21,7 @@ enum Handle {
|
|
TopLevel { entries: Vec<String> },
|
|
Access,
|
|
Device,
|
|
+ Config { addr: PciAddress },
|
|
Channel { addr: PciAddress, st: ChannelState },
|
|
SchemeRoot,
|
|
}
|
|
@@ -30,14 +31,20 @@ struct HandleWrapper {
|
|
}
|
|
impl Handle {
|
|
fn is_file(&self) -> bool {
|
|
- matches!(self, Self::Access | Self::Channel { .. })
|
|
+ matches!(
|
|
+ self,
|
|
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
|
|
+ )
|
|
}
|
|
fn is_dir(&self) -> bool {
|
|
!self.is_file()
|
|
}
|
|
// TODO: capability rather than root
|
|
fn requires_root(&self) -> bool {
|
|
- matches!(self, Self::Access | Self::Channel { .. })
|
|
+ matches!(
|
|
+ self,
|
|
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
|
|
+ )
|
|
}
|
|
fn is_scheme_root(&self) -> bool {
|
|
matches!(self, Self::SchemeRoot)
|
|
@@ -132,6 +139,7 @@ impl SchemeSync for PciScheme {
|
|
let (len, mode) = match handle.inner {
|
|
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
|
|
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
|
|
+ Handle::Config { .. } => (256, MODE_CHR | 0o600),
|
|
Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
|
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
|
};
|
|
@@ -156,6 +164,18 @@ impl SchemeSync for PciScheme {
|
|
match handle.inner {
|
|
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
|
|
Handle::Device => Err(Error::new(EISDIR)),
|
|
+ Handle::Config { addr } => {
|
|
+ let offset = _offset as u16;
|
|
+ let dword_offset = offset & !0x3;
|
|
+ let byte_offset = (offset & 0x3) as usize;
|
|
+ let bytes_to_read = buf.len().min(4 - byte_offset);
|
|
+
|
|
+ let dword = unsafe { self.pcie.read(addr, dword_offset) };
|
|
+ let bytes = dword.to_le_bytes();
|
|
+ buf[..bytes_to_read]
|
|
+ .copy_from_slice(&bytes[byte_offset..byte_offset + bytes_to_read]);
|
|
+ Ok(bytes_to_read)
|
|
+ }
|
|
Handle::Channel {
|
|
addr: _,
|
|
ref mut st,
|
|
@@ -193,7 +213,9 @@ impl SchemeSync for PciScheme {
|
|
return Ok(buf);
|
|
}
|
|
Handle::Device => DEVICE_CONTENTS,
|
|
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
|
|
+ Handle::Access | Handle::Config { .. } | Handle::Channel { .. } => {
|
|
+ return Err(Error::new(ENOTDIR));
|
|
+ }
|
|
Handle::SchemeRoot => return Err(Error::new(EBADF)),
|
|
};
|
|
|
|
@@ -223,6 +245,20 @@ impl SchemeSync for PciScheme {
|
|
}
|
|
|
|
match handle.inner {
|
|
+ Handle::Config { addr } => {
|
|
+ let offset = _offset as u16;
|
|
+ let dword_offset = offset & !0x3;
|
|
+ let byte_offset = (offset & 0x3) as usize;
|
|
+ let bytes_to_write = buf.len().min(4 - byte_offset);
|
|
+
|
|
+ let mut dword = unsafe { self.pcie.read(addr, dword_offset) };
|
|
+ let mut bytes = dword.to_le_bytes();
|
|
+ bytes[byte_offset..byte_offset + bytes_to_write]
|
|
+ .copy_from_slice(&buf[..bytes_to_write]);
|
|
+ dword = u32::from_le_bytes(bytes);
|
|
+ unsafe { self.pcie.write(addr, dword_offset, dword) };
|
|
+ Ok(buf.len())
|
|
+ }
|
|
Handle::Channel { addr, ref mut st } => {
|
|
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
|
|
}
|
|
@@ -318,6 +354,10 @@ impl PciScheme {
|
|
func.enabled = false;
|
|
}
|
|
}
|
|
+ Some(HandleWrapper {
|
|
+ inner: Handle::Config { .. },
|
|
+ ..
|
|
+ }) => {}
|
|
_ => {}
|
|
}
|
|
}
|
|
@@ -343,6 +383,7 @@ impl PciScheme {
|
|
let path = &after[1..];
|
|
|
|
match path {
|
|
+ "config" => Handle::Config { addr },
|
|
"channel" => {
|
|
if func.enabled {
|
|
return Err(Error::new(ENOLCK));
|
|
diff --git a/drivers/storage/usbscsid/src/main.rs b/drivers/storage/usbscsid/src/main.rs
|
|
index 5382d118..803b30fa 100644
|
|
--- a/drivers/storage/usbscsid/src/main.rs
|
|
+++ b/drivers/storage/usbscsid/src/main.rs
|
|
@@ -17,37 +17,55 @@ fn main() {
|
|
fn daemon(daemon: daemon::Daemon) -> ! {
|
|
let mut args = env::args().skip(1);
|
|
|
|
- const USAGE: &'static str = "usbscsid <scheme> <port> <protocol>";
|
|
+ const USAGE: &str = "usbscsid <scheme> <port> <protocol>";
|
|
|
|
- let scheme = args.next().expect(USAGE);
|
|
- let port = args
|
|
+ let scheme = args.next().unwrap_or_else(|| {
|
|
+ eprintln!("usbscsid: {USAGE}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
+ let port: PortId = args
|
|
.next()
|
|
- .expect(USAGE)
|
|
- .parse::<PortId>()
|
|
- .expect("Expected port ID");
|
|
- let protocol = args
|
|
+ .unwrap_or_else(|| {
|
|
+ eprintln!("usbscsid: {USAGE}");
|
|
+ std::process::exit(1);
|
|
+ })
|
|
+ .parse()
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: invalid port ID: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
+ let protocol_num: u8 = args
|
|
.next()
|
|
- .expect(USAGE)
|
|
- .parse::<u8>()
|
|
- .expect("protocol has to be a number 0-255");
|
|
+ .unwrap_or_else(|| {
|
|
+ eprintln!("usbscsid: {USAGE}");
|
|
+ std::process::exit(1);
|
|
+ })
|
|
+ .parse()
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: protocol must be a number 0-255: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
println!(
|
|
"USB SCSI driver spawned with scheme `{}`, port {}, protocol {}",
|
|
- scheme, port, protocol
|
|
+ scheme, port, protocol_num
|
|
);
|
|
|
|
let disk_scheme_name = format!("disk.usb-{scheme}+{port}-scsi");
|
|
|
|
- // TODO: Use eventfds.
|
|
- let handle =
|
|
- XhciClientHandle::new(scheme.to_owned(), port).expect("Failed to open XhciClientHandle");
|
|
+ let handle = XhciClientHandle::new(scheme.to_owned(), port)
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: failed to open XhciClientHandle: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
let desc = handle
|
|
.get_standard_descs()
|
|
- .expect("Failed to get standard descriptors");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: failed to get standard descriptors: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
- // 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()
|
|
@@ -65,7 +83,10 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
interface_desc,
|
|
))
|
|
})
|
|
- .expect("Failed to find suitable configuration");
|
|
+ .unwrap_or_else(|| {
|
|
+ eprintln!("usbscsid: failed to find suitable SCSI BOT configuration");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
handle
|
|
.configure_endpoints(&ConfigureEndpointsReq {
|
|
@@ -74,20 +95,32 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
alternate_setting: Some(alternate_setting),
|
|
hub_ports: None,
|
|
})
|
|
- .expect("Failed to configure endpoints");
|
|
-
|
|
- let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc)
|
|
- .expect("Failed to setup protocol");
|
|
-
|
|
- // TODO: Let all of the USB drivers fork or be managed externally, and xhcid won't have to keep
|
|
- // track of all the drivers.
|
|
- let mut scsi = Scsi::new(&mut *protocol).expect("usbscsid: failed to setup SCSI");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: failed to configure endpoints: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
+
|
|
+ let mut protocol = protocol::setup(&handle, protocol_num, &desc, &conf_desc, &if_desc)
|
|
+ .unwrap_or_else(|| {
|
|
+ eprintln!("usbscsid: failed to setup protocol (protocol 0x{:02x})", protocol_num);
|
|
+ std::process::exit(1);
|
|
+ });
|
|
+
|
|
+ let mut scsi = Scsi::new(&mut *protocol).unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: failed to setup SCSI: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
println!("SCSI initialized");
|
|
let mut buffer = [0u8; 512];
|
|
- scsi.read(&mut *protocol, 0, &mut buffer).unwrap();
|
|
- println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
|
|
+ match scsi.read(&mut *protocol, 0, &mut buffer) {
|
|
+ Ok(_) => println!("DISK CONTENT: {}", base64::encode(&buffer[..])),
|
|
+ Err(e) => eprintln!("usbscsid: initial sector read failed: {e}"),
|
|
+ }
|
|
|
|
- let event_queue = event::EventQueue::new().unwrap();
|
|
+ let event_queue = event::EventQueue::new().unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: failed to create event queue: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
event::user_data! {
|
|
enum Event {
|
|
@@ -119,13 +152,25 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
|
Event::Scheme,
|
|
event::EventFlags::READ,
|
|
)
|
|
- .unwrap();
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbscsid: failed to subscribe to scheme events: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
for event in event_queue {
|
|
- match event.unwrap().user_data {
|
|
- Event::Scheme => driver_block::FuturesExecutor
|
|
- .block_on(scheme.tick())
|
|
- .unwrap(),
|
|
+ match event {
|
|
+ Ok(ev) => match ev.user_data {
|
|
+ Event::Scheme => {
|
|
+ if let Err(e) = driver_block::FuturesExecutor.block_on(scheme.tick()) {
|
|
+ eprintln!("usbscsid: scheme tick error: {e}");
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ },
|
|
+ Err(e) => {
|
|
+ eprintln!("usbscsid: event queue error: {e}");
|
|
+ break;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/storage/usbscsid/src/protocol/bot.rs b/drivers/storage/usbscsid/src/protocol/bot.rs
|
|
index b751d51a..87885653 100644
|
|
--- a/drivers/storage/usbscsid/src/protocol/bot.rs
|
|
+++ b/drivers/storage/usbscsid/src/protocol/bot.rs
|
|
@@ -88,6 +88,8 @@ pub struct BulkOnlyTransport<'a> {
|
|
bulk_out: XhciEndpHandle,
|
|
bulk_in_num: u8,
|
|
bulk_out_num: u8,
|
|
+ bulk_in_addr: u8,
|
|
+ bulk_out_addr: u8,
|
|
max_lun: u8,
|
|
current_tag: u32,
|
|
interface_num: u8,
|
|
@@ -98,23 +100,28 @@ pub const FEATURE_ENDPOINT_HALT: u16 = 0;
|
|
impl<'a> BulkOnlyTransport<'a> {
|
|
pub fn init(
|
|
handle: &'a XhciClientHandle,
|
|
- config_desc: &ConfDesc,
|
|
+ _config_desc: &ConfDesc,
|
|
if_desc: &IfDesc,
|
|
) -> Result<Self, ProtocolError> {
|
|
let endpoints = &if_desc.endpoints;
|
|
|
|
- let bulk_in_num = (endpoints
|
|
+ let (bulk_in_idx, bulk_in_desc) = endpoints
|
|
.iter()
|
|
- .position(|endpoint| endpoint.direction() == EndpDirection::In)
|
|
- .unwrap()
|
|
- + 1) as u8;
|
|
- let bulk_out_num = (endpoints
|
|
+ .enumerate()
|
|
+ .find(|(_, endpoint)| endpoint.direction() == EndpDirection::In)
|
|
+ .ok_or(ProtocolError::ProtocolError("no bulk IN endpoint found"))?;
|
|
+ let (bulk_out_idx, bulk_out_desc) = endpoints
|
|
.iter()
|
|
- .position(|endpoint| endpoint.direction() == EndpDirection::Out)
|
|
- .unwrap()
|
|
- + 1) as u8;
|
|
+ .enumerate()
|
|
+ .find(|(_, endpoint)| endpoint.direction() == EndpDirection::Out)
|
|
+ .ok_or(ProtocolError::ProtocolError("no bulk OUT endpoint found"))?;
|
|
|
|
- let max_lun = get_max_lun(handle, 0)?;
|
|
+ let bulk_in_num = (bulk_in_idx + 1) as u8;
|
|
+ let bulk_out_num = (bulk_out_idx + 1) as u8;
|
|
+ let bulk_in_addr = bulk_in_desc.address;
|
|
+ let bulk_out_addr = bulk_out_desc.address;
|
|
+
|
|
+ let max_lun = get_max_lun(handle, if_desc.number.into())?;
|
|
println!("BOT_MAX_LUN {}", max_lun);
|
|
|
|
Ok(Self {
|
|
@@ -122,6 +129,8 @@ impl<'a> BulkOnlyTransport<'a> {
|
|
bulk_out: handle.open_endpoint(bulk_out_num)?,
|
|
bulk_in_num,
|
|
bulk_out_num,
|
|
+ bulk_in_addr,
|
|
+ bulk_out_addr,
|
|
handle,
|
|
max_lun,
|
|
current_tag: 0,
|
|
@@ -133,7 +142,7 @@ impl<'a> BulkOnlyTransport<'a> {
|
|
self.bulk_in.reset(false)?;
|
|
self.handle.clear_feature(
|
|
PortReqRecipient::Endpoint,
|
|
- u16::from(self.bulk_in_num),
|
|
+ u16::from(self.bulk_in_addr),
|
|
FEATURE_ENDPOINT_HALT,
|
|
)?;
|
|
}
|
|
@@ -144,7 +153,7 @@ impl<'a> BulkOnlyTransport<'a> {
|
|
self.bulk_out.reset(false)?;
|
|
self.handle.clear_feature(
|
|
PortReqRecipient::Endpoint,
|
|
- u16::from(self.bulk_out_num),
|
|
+ u16::from(self.bulk_out_addr),
|
|
FEATURE_ENDPOINT_HALT,
|
|
)?;
|
|
}
|
|
@@ -162,38 +171,59 @@ impl<'a> BulkOnlyTransport<'a> {
|
|
}
|
|
Ok(())
|
|
}
|
|
- fn read_csw_raw(
|
|
- &mut self,
|
|
- csw_buffer: &mut [u8; 13],
|
|
- already: bool,
|
|
- ) -> Result<(), ProtocolError> {
|
|
- match self.bulk_in.transfer_read(&mut csw_buffer[..])? {
|
|
- PortTransferStatus {
|
|
- kind: PortTransferStatusKind::Stalled,
|
|
- ..
|
|
- } => {
|
|
- if already {
|
|
+ fn read_csw(&mut self, csw_buffer: &mut [u8; 13]) -> Result<(), ProtocolError> {
|
|
+ let mut attempts = 0u8;
|
|
+ loop {
|
|
+ let status = self.bulk_in.transfer_read(&mut csw_buffer[..])?;
|
|
+ match status {
|
|
+ PortTransferStatus {
|
|
+ kind: PortTransferStatusKind::Stalled,
|
|
+ ..
|
|
+ } => {
|
|
+ attempts += 1;
|
|
+ if attempts >= 2 {
|
|
+ self.reset_recovery()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "bulk IN stalled repeatedly when reading CSW",
|
|
+ ));
|
|
+ }
|
|
+ eprintln!("usbscsid: bulk IN stalled when reading CSW, clearing stall");
|
|
+ self.clear_stall_in()?;
|
|
+ continue;
|
|
+ }
|
|
+ PortTransferStatus {
|
|
+ kind: PortTransferStatusKind::ShortPacket,
|
|
+ bytes_transferred,
|
|
+ } if bytes_transferred != 13 => {
|
|
+ eprintln!(
|
|
+ "usbscsid: short packet when reading CSW ({} != 13)",
|
|
+ bytes_transferred
|
|
+ );
|
|
self.reset_recovery()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "short packet when reading CSW",
|
|
+ ));
|
|
+ }
|
|
+ PortTransferStatus {
|
|
+ kind: PortTransferStatusKind::Success,
|
|
+ ..
|
|
+ }
|
|
+ | PortTransferStatus {
|
|
+ kind: PortTransferStatusKind::ShortPacket,
|
|
+ bytes_transferred: 13,
|
|
+ } => return Ok(()),
|
|
+ _ => {
|
|
+ eprintln!(
|
|
+ "usbscsid: unexpected transfer status when reading CSW: {:?}",
|
|
+ status
|
|
+ );
|
|
+ self.reset_recovery()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "unexpected transfer status when reading CSW",
|
|
+ ));
|
|
}
|
|
- println!("bulk in endpoint stalled when reading CSW");
|
|
- self.clear_stall_in()?;
|
|
- self.read_csw_raw(csw_buffer, true)?;
|
|
- }
|
|
- PortTransferStatus {
|
|
- kind: PortTransferStatusKind::ShortPacket,
|
|
- bytes_transferred,
|
|
- } if bytes_transferred != 13 => {
|
|
- panic!(
|
|
- "received a short packet when reading CSW ({} != 13)",
|
|
- bytes_transferred
|
|
- )
|
|
}
|
|
- _ => (),
|
|
}
|
|
- Ok(())
|
|
- }
|
|
- fn read_csw(&mut self, csw_buffer: &mut [u8; 13]) -> Result<(), ProtocolError> {
|
|
- self.read_csw_raw(csw_buffer, false)
|
|
}
|
|
}
|
|
|
|
@@ -207,7 +237,8 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
|
let tag = self.current_tag;
|
|
|
|
let mut cbw_bytes = [0u8; 31];
|
|
- let cbw = plain::from_mut_bytes::<CommandBlockWrapper>(&mut cbw_bytes).unwrap();
|
|
+ let cbw = plain::from_mut_bytes::<CommandBlockWrapper>(&mut cbw_bytes)
|
|
+ .map_err(|_| ProtocolError::ProtocolError("CBW buffer size mismatch"))?;
|
|
*cbw = CommandBlockWrapper::new(tag, data.len() as u32, data.direction().into(), 0, cb)?;
|
|
let cbw = *cbw;
|
|
|
|
@@ -216,22 +247,48 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
|
kind: PortTransferStatusKind::Stalled,
|
|
..
|
|
} => {
|
|
- // TODO: Error handling
|
|
- panic!("bulk out endpoint stalled when sending CBW {:?}", cbw);
|
|
- //self.clear_stall_out()?;
|
|
- //dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
|
|
+ eprintln!(
|
|
+ "usbscsid: bulk OUT endpoint stalled when sending CBW {:?}",
|
|
+ cbw
|
|
+ );
|
|
+ self.clear_stall_out()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "bulk OUT endpoint stalled when sending CBW",
|
|
+ ));
|
|
}
|
|
PortTransferStatus {
|
|
bytes_transferred, ..
|
|
} if bytes_transferred != 31 => {
|
|
- panic!(
|
|
- "received short packet when sending CBW ({} != 31)",
|
|
+ eprintln!(
|
|
+ "usbscsid: short packet when sending CBW ({} != 31)",
|
|
bytes_transferred
|
|
);
|
|
+ self.reset_recovery()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "short packet when sending CBW",
|
|
+ ));
|
|
+ }
|
|
+ PortTransferStatus {
|
|
+ kind: PortTransferStatusKind::Success,
|
|
+ ..
|
|
+ } => (),
|
|
+ PortTransferStatus {
|
|
+ kind: PortTransferStatusKind::ShortPacket,
|
|
+ bytes_transferred: 31,
|
|
+ } => (),
|
|
+ status => {
|
|
+ eprintln!(
|
|
+ "usbscsid: unexpected transfer status {:?} when sending CBW",
|
|
+ status
|
|
+ );
|
|
+ self.reset_recovery()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "unexpected transfer status when sending CBW",
|
|
+ ));
|
|
}
|
|
- _ => (),
|
|
}
|
|
|
|
+ let data_len = data.len() as u32;
|
|
let early_residue: Option<NonZeroU32> = match data {
|
|
DeviceReqData::In(buffer) => match self.bulk_in.transfer_read(buffer)? {
|
|
PortTransferStatus {
|
|
@@ -240,15 +297,19 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
|
} => match kind {
|
|
PortTransferStatusKind::Success => None,
|
|
PortTransferStatusKind::ShortPacket => {
|
|
- println!(
|
|
- "received short packet (len {}) when transferring data",
|
|
- bytes_transferred
|
|
+ let residue = data_len.saturating_sub(bytes_transferred);
|
|
+ eprintln!(
|
|
+ "usbscsid: short packet ({} of {} bytes) during data read",
|
|
+ bytes_transferred, data_len
|
|
);
|
|
- NonZeroU32::new(bytes_transferred)
|
|
+ NonZeroU32::new(residue)
|
|
}
|
|
PortTransferStatusKind::Stalled => {
|
|
- panic!("bulk in endpoint stalled when reading data");
|
|
- //self.clear_stall_in()?;
|
|
+ eprintln!("usbscsid: bulk IN endpoint stalled when reading data");
|
|
+ self.clear_stall_in()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "bulk IN endpoint stalled during data read",
|
|
+ ));
|
|
}
|
|
PortTransferStatusKind::Unknown => {
|
|
return Err(ProtocolError::XhciError(
|
|
@@ -266,15 +327,19 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
|
} => match kind {
|
|
PortTransferStatusKind::Success => None,
|
|
PortTransferStatusKind::ShortPacket => {
|
|
- println!(
|
|
- "received short packet (len {}) when transferring data",
|
|
- bytes_transferred
|
|
+ let residue = data_len.saturating_sub(bytes_transferred);
|
|
+ eprintln!(
|
|
+ "usbscsid: short packet ({} of {} bytes) during data write",
|
|
+ bytes_transferred, data_len
|
|
);
|
|
- NonZeroU32::new(bytes_transferred)
|
|
+ NonZeroU32::new(residue)
|
|
}
|
|
PortTransferStatusKind::Stalled => {
|
|
- panic!("bulk out endpoint stalled when reading data");
|
|
- //self.clear_stall_out()?;
|
|
+ eprintln!("usbscsid: bulk OUT endpoint stalled when writing data");
|
|
+ self.clear_stall_out()?;
|
|
+ return Err(ProtocolError::ProtocolError(
|
|
+ "bulk OUT endpoint stalled during data write",
|
|
+ ));
|
|
}
|
|
PortTransferStatusKind::Unknown => {
|
|
return Err(ProtocolError::XhciError(
|
|
@@ -290,7 +355,8 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
|
|
|
let mut csw_buffer = [0u8; 13];
|
|
self.read_csw(&mut csw_buffer)?;
|
|
- let csw = plain::from_bytes::<CommandStatusWrapper>(&csw_buffer).unwrap();
|
|
+ let csw = plain::from_bytes::<CommandStatusWrapper>(&csw_buffer)
|
|
+ .map_err(|_| ProtocolError::ProtocolError("CSW buffer size mismatch"))?;
|
|
|
|
let residue = early_residue.or(NonZeroU32::new(csw.data_residue));
|
|
|
|
diff --git a/drivers/storage/usbscsid/src/protocol/mod.rs b/drivers/storage/usbscsid/src/protocol/mod.rs
|
|
index a580765f..952268c7 100644
|
|
--- a/drivers/storage/usbscsid/src/protocol/mod.rs
|
|
+++ b/drivers/storage/usbscsid/src/protocol/mod.rs
|
|
@@ -59,22 +59,18 @@ pub trait Protocol {
|
|
/// Bulk-only transport
|
|
pub mod bot;
|
|
|
|
-mod uas {
|
|
- // TODO
|
|
-}
|
|
-
|
|
use bot::BulkOnlyTransport;
|
|
|
|
pub fn setup<'a>(
|
|
handle: &'a XhciClientHandle,
|
|
protocol: u8,
|
|
- dev_desc: &DevDesc,
|
|
+ _dev_desc: &DevDesc,
|
|
conf_desc: &ConfDesc,
|
|
if_desc: &IfDesc,
|
|
) -> Option<Box<dyn Protocol + 'a>> {
|
|
match protocol {
|
|
0x50 => Some(Box::new(
|
|
- BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap(),
|
|
+ BulkOnlyTransport::init(handle, conf_desc, if_desc).ok()?,
|
|
)),
|
|
_ => None,
|
|
}
|
|
diff --git a/drivers/storage/usbscsid/src/scsi/mod.rs b/drivers/storage/usbscsid/src/scsi/mod.rs
|
|
index 790abea6..fbba4d00 100644
|
|
--- a/drivers/storage/usbscsid/src/scsi/mod.rs
|
|
+++ b/drivers/storage/usbscsid/src/scsi/mod.rs
|
|
@@ -146,8 +146,15 @@ impl Scsi {
|
|
&self.command_buffer[..10],
|
|
DeviceReqData::In(&mut self.data_buffer[..initial_alloc_len as usize]),
|
|
)? {
|
|
- self.get_ff_sense(protocol, 252)?;
|
|
- panic!("{:?}", self.res_ff_sense_data());
|
|
+ if let Ok(()) = self.get_ff_sense(protocol, 252) {
|
|
+ eprintln!(
|
|
+ "usbscsid: MODE SENSE(10) failed: {:?}",
|
|
+ self.res_ff_sense_data()
|
|
+ );
|
|
+ }
|
|
+ return Err(ScsiError::ProtocolError(ProtocolError::ProtocolError(
|
|
+ "MODE SENSE(10) command failed",
|
|
+ )));
|
|
}
|
|
|
|
let optimal_alloc_len = self.res_mode_param_header10().mode_data_len() + 2; // the length of the mode data field itself
|
|
@@ -161,7 +168,7 @@ impl Scsi {
|
|
)?;
|
|
Ok((
|
|
self.res_mode_param_header10(),
|
|
- self.res_blkdesc_mode10(),
|
|
+ self.res_blkdesc_mode10()?,
|
|
self.res_mode_pages10(),
|
|
))
|
|
}
|
|
@@ -199,44 +206,50 @@ impl Scsi {
|
|
pub fn res_mode_param_header10(&self) -> &cmds::ModeParamHeader10 {
|
|
plain::from_bytes(&self.data_buffer).unwrap()
|
|
}
|
|
- pub fn res_blkdesc_mode6(&self) -> &[cmds::ShortLbaModeParamBlkDesc] {
|
|
+ pub fn res_blkdesc_mode6(&self) -> Result<&[cmds::ShortLbaModeParamBlkDesc]> {
|
|
let header = self.res_mode_param_header6();
|
|
let descs_start = mem::size_of::<cmds::ModeParamHeader6>();
|
|
- plain::slice_from_bytes(
|
|
- &self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len)],
|
|
+ let desc_len = usize::from(header.block_desc_len);
|
|
+ if descs_start + desc_len > self.data_buffer.len() {
|
|
+ return Err(ScsiError::Overflow(
|
|
+ "block descriptor length exceeds data buffer",
|
|
+ ));
|
|
+ }
|
|
+ Ok(
|
|
+ plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + desc_len])
|
|
+ .map_err(|_| ScsiError::Overflow("block descriptor alignment mismatch"))?,
|
|
)
|
|
- .unwrap()
|
|
}
|
|
- pub fn res_blkdesc_mode10(&self) -> BlkDescSlice<'_> {
|
|
+ pub fn res_blkdesc_mode10(&self) -> Result<BlkDescSlice<'_>> {
|
|
let header = self.res_mode_param_header10();
|
|
let descs_start = mem::size_of::<cmds::ModeParamHeader10>();
|
|
+ let desc_range = descs_start..descs_start + usize::from(header.block_desc_len());
|
|
+ if desc_range.end > self.data_buffer.len() {
|
|
+ return Err(ScsiError::Overflow(
|
|
+ "block descriptor length exceeds data buffer",
|
|
+ ));
|
|
+ }
|
|
if header.longlba() {
|
|
- BlkDescSlice::Long(
|
|
- plain::slice_from_bytes(
|
|
- &self.data_buffer
|
|
- [descs_start..descs_start + usize::from(header.block_desc_len())],
|
|
- )
|
|
- .unwrap(),
|
|
- )
|
|
+ Ok(BlkDescSlice::Long(
|
|
+ plain::slice_from_bytes(&self.data_buffer[desc_range]).map_err(|_| {
|
|
+ ScsiError::Overflow("long LBA block descriptor alignment mismatch")
|
|
+ })?,
|
|
+ ))
|
|
} else if self.res_standard_inquiry_data().periph_dev_ty()
|
|
!= cmds::PeriphDeviceType::DirectAccess as u8
|
|
&& self.res_standard_inquiry_data().version() == cmds::InquiryVersion::Spc3 as u8
|
|
{
|
|
- BlkDescSlice::General(
|
|
- plain::slice_from_bytes(
|
|
- &self.data_buffer
|
|
- [descs_start..descs_start + usize::from(header.block_desc_len())],
|
|
- )
|
|
- .unwrap(),
|
|
- )
|
|
+ Ok(BlkDescSlice::General(
|
|
+ plain::slice_from_bytes(&self.data_buffer[desc_range]).map_err(|_| {
|
|
+ ScsiError::Overflow("general block descriptor alignment mismatch")
|
|
+ })?,
|
|
+ ))
|
|
} else {
|
|
- BlkDescSlice::Short(
|
|
- plain::slice_from_bytes(
|
|
- &self.data_buffer
|
|
- [descs_start..descs_start + usize::from(header.block_desc_len())],
|
|
- )
|
|
- .unwrap(),
|
|
- )
|
|
+ Ok(BlkDescSlice::Short(
|
|
+ plain::slice_from_bytes(&self.data_buffer[desc_range]).map_err(|_| {
|
|
+ ScsiError::Overflow("short LBA block descriptor alignment mismatch")
|
|
+ })?,
|
|
+ ))
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/usb/usbhubd/src/main.rs b/drivers/usb/usbhubd/src/main.rs
|
|
index 2c8b9876..eab690dd 100644
|
|
--- a/drivers/usb/usbhubd/src/main.rs
|
|
+++ b/drivers/usb/usbhubd/src/main.rs
|
|
@@ -2,26 +2,41 @@ use std::{env, thread, time};
|
|
|
|
use xhcid_interface::{
|
|
plain, usb, ConfigureEndpointsReq, DevDesc, DeviceReqData, PortId, PortReqRecipient, PortReqTy,
|
|
- XhciClientHandle,
|
|
+ UsbSpeed, XhciClientHandle,
|
|
};
|
|
|
|
fn main() {
|
|
common::init();
|
|
let mut args = env::args().skip(1);
|
|
|
|
- const USAGE: &'static str = "usbhubd <scheme> <port> <interface>";
|
|
+ const USAGE: &str = "usbhubd <scheme> <port> <interface>";
|
|
|
|
- let scheme = args.next().expect(USAGE);
|
|
+ let scheme = args.next().unwrap_or_else(|| {
|
|
+ eprintln!("usbhubd: {USAGE}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
let port_id = args
|
|
.next()
|
|
- .expect(USAGE)
|
|
+ .unwrap_or_else(|| {
|
|
+ eprintln!("usbhubd: {USAGE}");
|
|
+ std::process::exit(1);
|
|
+ })
|
|
.parse::<PortId>()
|
|
- .expect("Expected port ID");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: invalid port ID: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
let interface_num = args
|
|
.next()
|
|
- .expect(USAGE)
|
|
+ .unwrap_or_else(|| {
|
|
+ eprintln!("usbhubd: {USAGE}");
|
|
+ std::process::exit(1);
|
|
+ })
|
|
.parse::<u8>()
|
|
- .expect("Expected integer as input of interface");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: interface number must be 0-255: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
log::info!(
|
|
"USB HUB driver spawned with scheme `{}`, port {}, interface {}",
|
|
@@ -39,11 +54,14 @@ fn main() {
|
|
common::file_level(),
|
|
);
|
|
|
|
- let handle =
|
|
- XhciClientHandle::new(scheme.clone(), port_id).expect("Failed to open XhciClientHandle");
|
|
- let desc: DevDesc = handle
|
|
- .get_standard_descs()
|
|
- .expect("Failed to get standard descriptors");
|
|
+ let handle = XhciClientHandle::new(scheme.clone(), port_id).unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: failed to open XhciClientHandle: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
+ let desc: DevDesc = handle.get_standard_descs().unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: failed to get standard descriptors: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
let (conf_desc, if_desc) = desc
|
|
.config_descs
|
|
@@ -58,11 +76,13 @@ fn main() {
|
|
})?;
|
|
Some((conf_desc.clone(), if_desc))
|
|
})
|
|
- .expect("Failed to find suitable configuration");
|
|
+ .unwrap_or_else(|| {
|
|
+ eprintln!("usbhubd: failed to find configuration with interface {interface_num}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
// Read hub descriptor
|
|
let (ports, usb_3) = if desc.major_version() >= 3 {
|
|
- // USB 3.0 hubs
|
|
let mut hub_desc = usb::HubDescriptorV3::default();
|
|
handle
|
|
.device_request(
|
|
@@ -73,10 +93,12 @@ fn main() {
|
|
0,
|
|
DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut hub_desc) }),
|
|
)
|
|
- .expect("Failed to read hub descriptor");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: failed to read USB 3 hub descriptor: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
(hub_desc.ports, true)
|
|
} else {
|
|
- // USB 2.0 and earlier hubs
|
|
let mut hub_desc = usb::HubDescriptorV2::default();
|
|
handle
|
|
.device_request(
|
|
@@ -87,7 +109,10 @@ fn main() {
|
|
0,
|
|
DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut hub_desc) }),
|
|
)
|
|
- .expect("Failed to read hub descriptor");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: failed to read USB 2 hub descriptor: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
(hub_desc.ports, false)
|
|
};
|
|
|
|
@@ -95,25 +120,55 @@ fn main() {
|
|
handle
|
|
.configure_endpoints(&ConfigureEndpointsReq {
|
|
config_desc: conf_desc.configuration_value,
|
|
- interface_desc: None, //TODO: stalls on USB 3 hub: Some(interface_num),
|
|
- alternate_setting: None, //TODO: stalls on USB 3 hub: Some(if_desc.alternate_setting),
|
|
+ interface_desc: Some(interface_num),
|
|
+ alternate_setting: Some(if_desc.alternate_setting),
|
|
hub_ports: Some(ports),
|
|
})
|
|
- .expect("Failed to configure endpoints after reading hub descriptor");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: failed to configure endpoints: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
|
|
if usb_3 {
|
|
handle
|
|
.device_request(
|
|
PortReqTy::Class,
|
|
PortReqRecipient::Device,
|
|
- 0x0c, // SET_HUB_DEPTH
|
|
+ 0x0c,
|
|
port_id.hub_depth().into(),
|
|
0,
|
|
DeviceReqData::NoData,
|
|
)
|
|
- .expect("Failed to set hub depth");
|
|
+ .unwrap_or_else(|e| {
|
|
+ eprintln!("usbhubd: failed to set hub depth: {e}");
|
|
+ std::process::exit(1);
|
|
+ });
|
|
}
|
|
|
|
+ let status_change_len = (usize::from(ports) + 8) / 8;
|
|
+ let mut status_change_buf = vec![0u8; status_change_len];
|
|
+
|
|
+ let mut interrupt_ep = if_desc
|
|
+ .endpoints
|
|
+ .iter()
|
|
+ .find(|ep| {
|
|
+ ep.ty() == xhcid_interface::EndpointTy::Interrupt
|
|
+ && ep.direction() == xhcid_interface::EndpDirection::In
|
|
+ })
|
|
+ .and_then(|ep| {
|
|
+ let ep_num = ep.address & 0x0F;
|
|
+ match handle.open_endpoint(ep_num) {
|
|
+ Ok(h) => {
|
|
+ log::info!("hub interrupt endpoint {} opened", ep_num);
|
|
+ Some(h)
|
|
+ }
|
|
+ Err(err) => {
|
|
+ log::warn!("failed to open hub interrupt endpoint {}: {}", ep_num, err);
|
|
+ None
|
|
+ }
|
|
+ }
|
|
+ });
|
|
+
|
|
// Initialize states
|
|
struct PortState {
|
|
port_id: PortId,
|
|
@@ -129,111 +184,297 @@ fn main() {
|
|
}
|
|
|
|
if attached {
|
|
- self.handle.attach().expect("Failed to attach");
|
|
+ let speed = match &self.port_sts {
|
|
+ usb::HubPortStatus::V2(v2) => UsbSpeed::from_v2_port_status(*v2),
|
|
+ usb::HubPortStatus::V3(v3) => UsbSpeed::from_v3_port_status(*v3),
|
|
+ };
|
|
+ let res = match speed {
|
|
+ Some(s) => self.handle.attach_with_speed(s),
|
|
+ None => self.handle.attach(),
|
|
+ };
|
|
+ if let Err(err) = res {
|
|
+ log::error!("failed to attach port {}: {}", self.port_id, err);
|
|
+ return;
|
|
+ }
|
|
} else {
|
|
- self.handle.detach().expect("Failed to detach");
|
|
+ if let Err(err) = self.handle.detach() {
|
|
+ log::error!("failed to detach port {}: {}", self.port_id, err);
|
|
+ return;
|
|
+ }
|
|
}
|
|
|
|
self.attached = attached;
|
|
}
|
|
}
|
|
|
|
- let mut states = Vec::new();
|
|
- for port in 1..=ports {
|
|
- let child_port_id = port_id.child(port).expect("Cannot get child port ID");
|
|
- states.push(PortState {
|
|
- port_id: child_port_id,
|
|
- port_sts: if usb_3 {
|
|
- usb::HubPortStatus::V3(usb::HubPortStatusV3::default())
|
|
- } else {
|
|
- usb::HubPortStatus::V2(usb::HubPortStatusV2::default())
|
|
- },
|
|
- handle: XhciClientHandle::new(scheme.clone(), child_port_id)
|
|
- .expect("Failed to open XhciClientHandle"),
|
|
- attached: false,
|
|
- });
|
|
+ let mut states: Vec<Option<PortState>> = (1..=ports)
|
|
+ .map(|port| {
|
|
+ let child_port_id = match port_id.child(port) {
|
|
+ Ok(id) => id,
|
|
+ Err(e) => {
|
|
+ log::error!("port {}: cannot compute child port ID: {}", port, e);
|
|
+ return None;
|
|
+ }
|
|
+ };
|
|
+ let child_handle = match XhciClientHandle::new(scheme.clone(), child_port_id) {
|
|
+ Ok(h) => h,
|
|
+ Err(e) => {
|
|
+ log::error!("port {}: failed to open XhciClientHandle: {}", port, e);
|
|
+ return None;
|
|
+ }
|
|
+ };
|
|
+ Some(PortState {
|
|
+ port_id: child_port_id,
|
|
+ port_sts: if usb_3 {
|
|
+ usb::HubPortStatus::V3(usb::HubPortStatusV3::default())
|
|
+ } else {
|
|
+ usb::HubPortStatus::V2(usb::HubPortStatusV2::default())
|
|
+ },
|
|
+ handle: child_handle,
|
|
+ attached: false,
|
|
+ })
|
|
+ })
|
|
+ .collect();
|
|
+
|
|
+ macro_rules! hub_req {
|
|
+ ($handle:expr, $port:expr, $msg:expr, $($arg:expr),*) => {
|
|
+ if let Err(err) = $handle.device_request($($arg),*) {
|
|
+ log::error!("port {}: {} failed: {}", $port, $msg, err);
|
|
+ continue;
|
|
+ }
|
|
+ };
|
|
}
|
|
|
|
- //TODO: use change flags?
|
|
- loop {
|
|
- for port in 1..=ports {
|
|
- let port_idx: usize = port.checked_sub(1).unwrap().into();
|
|
- let state = states.get_mut(port_idx).unwrap();
|
|
+ let clear_port_changes = |handle: &XhciClientHandle, port: u8, is_usb3: bool| {
|
|
+ let mut features: Vec<usb::HubPortFeature> = vec![
|
|
+ usb::HubPortFeature::CPortConnection,
|
|
+ usb::HubPortFeature::CPortReset,
|
|
+ usb::HubPortFeature::CPortOverCurrent,
|
|
+ usb::HubPortFeature::CPortEnable,
|
|
+ ];
|
|
+ if is_usb3 {
|
|
+ features.push(usb::HubPortFeature::CPortLinkState);
|
|
+ features.push(usb::HubPortFeature::CPortConfigError);
|
|
+ }
|
|
+ for feature in &features {
|
|
+ if let Err(err) = handle.device_request(
|
|
+ PortReqTy::Class,
|
|
+ PortReqRecipient::Other,
|
|
+ usb::SetupReq::ClearFeature as u8,
|
|
+ *feature as u16,
|
|
+ port as u16,
|
|
+ DeviceReqData::NoData,
|
|
+ ) {
|
|
+ log::debug!("port {}: clear feature {:?} failed: {}", port, feature, err);
|
|
+ }
|
|
+ }
|
|
+ };
|
|
|
|
- let port_sts = if usb_3 {
|
|
- let mut port_sts = usb::HubPortStatusV3::default();
|
|
- handle
|
|
- .device_request(
|
|
+ let check_all_ports =
|
|
+ |states: &mut Vec<Option<PortState>>, handle: &XhciClientHandle, usb_3: bool, ports: u8| {
|
|
+ for port in 1..=ports {
|
|
+ let port_idx: usize = (port - 1) as usize;
|
|
+ let state = match states.get_mut(port_idx) {
|
|
+ Some(Some(s)) => s,
|
|
+ _ => continue,
|
|
+ };
|
|
+
|
|
+ let port_sts = if usb_3 {
|
|
+ let mut port_sts = usb::HubPortStatusV3::default();
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "get status",
|
|
PortReqTy::Class,
|
|
PortReqRecipient::Other,
|
|
usb::SetupReq::GetStatus as u8,
|
|
0,
|
|
port as u16,
|
|
- DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }),
|
|
- )
|
|
- .expect("Failed to retrieve port status");
|
|
- usb::HubPortStatus::V3(port_sts)
|
|
- } else {
|
|
- let mut port_sts = usb::HubPortStatusV2::default();
|
|
- handle
|
|
- .device_request(
|
|
+ DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) })
|
|
+ );
|
|
+ usb::HubPortStatus::V3(port_sts)
|
|
+ } else {
|
|
+ let mut port_sts = usb::HubPortStatusV2::default();
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "get status",
|
|
PortReqTy::Class,
|
|
PortReqRecipient::Other,
|
|
usb::SetupReq::GetStatus as u8,
|
|
0,
|
|
port as u16,
|
|
- DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }),
|
|
- )
|
|
- .expect("Failed to retrieve port status");
|
|
+ DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) })
|
|
+ );
|
|
+ usb::HubPortStatus::V2(port_sts)
|
|
+ };
|
|
+ if state.port_sts != port_sts {
|
|
+ state.port_sts = port_sts;
|
|
+ log::info!("port {} status {:X?}", port, port_sts);
|
|
+ }
|
|
+ clear_port_changes(handle, port, usb_3);
|
|
+
|
|
+ if !port_sts.is_powered() {
|
|
+ log::info!("power on port {port}");
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "set port power",
|
|
+ PortReqTy::Class,
|
|
+ PortReqRecipient::Other,
|
|
+ usb::SetupReq::SetFeature as u8,
|
|
+ usb::HubPortFeature::PortPower as u16,
|
|
+ port as u16,
|
|
+ DeviceReqData::NoData
|
|
+ );
|
|
+ state.ensure_attached(false);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if !port_sts.is_connected() {
|
|
+ state.ensure_attached(false);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if port_sts.is_resetting() {
|
|
+ state.ensure_attached(false);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if !port_sts.is_enabled() {
|
|
+ log::info!("reset port {port}");
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "set port reset",
|
|
+ PortReqTy::Class,
|
|
+ PortReqRecipient::Other,
|
|
+ usb::SetupReq::SetFeature as u8,
|
|
+ usb::HubPortFeature::PortReset as u16,
|
|
+ port as u16,
|
|
+ DeviceReqData::NoData
|
|
+ );
|
|
+ state.ensure_attached(false);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ state.ensure_attached(true);
|
|
+ }
|
|
+ };
|
|
+
|
|
+ check_all_ports(&mut states, &handle, usb_3, ports);
|
|
+
|
|
+ loop {
|
|
+ let ports_to_check: Vec<u8> = if let Some(ref mut ep) = interrupt_ep {
|
|
+ match ep.transfer_read(&mut status_change_buf) {
|
|
+ Ok(_) => {
|
|
+ let mut changed = Vec::new();
|
|
+ for port in 1..=ports {
|
|
+ let status_change_bit = usize::from(port);
|
|
+ let byte_idx = status_change_bit / 8;
|
|
+ let bit_idx = status_change_bit % 8;
|
|
+ if byte_idx < status_change_buf.len()
|
|
+ && (status_change_buf[byte_idx] & (1 << bit_idx)) != 0
|
|
+ {
|
|
+ changed.push(port);
|
|
+ }
|
|
+ }
|
|
+ if changed.is_empty() {
|
|
+ (1..=ports).collect()
|
|
+ } else {
|
|
+ changed
|
|
+ }
|
|
+ }
|
|
+ Err(err) => {
|
|
+ log::warn!("hub interrupt read failed: {}, falling back to poll", err);
|
|
+ (1..=ports).collect()
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ (1..=ports).collect()
|
|
+ };
|
|
+
|
|
+ for port in ports_to_check {
|
|
+ let port_idx: usize = (port - 1) as usize;
|
|
+ let state = match states.get_mut(port_idx) {
|
|
+ Some(Some(s)) => s,
|
|
+ _ => continue,
|
|
+ };
|
|
+
|
|
+ let port_sts = if usb_3 {
|
|
+ let mut port_sts = usb::HubPortStatusV3::default();
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "get status",
|
|
+ PortReqTy::Class,
|
|
+ PortReqRecipient::Other,
|
|
+ usb::SetupReq::GetStatus as u8,
|
|
+ 0,
|
|
+ port as u16,
|
|
+ DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) })
|
|
+ );
|
|
+ usb::HubPortStatus::V3(port_sts)
|
|
+ } else {
|
|
+ let mut port_sts = usb::HubPortStatusV2::default();
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "get status",
|
|
+ PortReqTy::Class,
|
|
+ PortReqRecipient::Other,
|
|
+ usb::SetupReq::GetStatus as u8,
|
|
+ 0,
|
|
+ port as u16,
|
|
+ DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) })
|
|
+ );
|
|
usb::HubPortStatus::V2(port_sts)
|
|
};
|
|
if state.port_sts != port_sts {
|
|
state.port_sts = port_sts;
|
|
log::info!("port {} status {:X?}", port, port_sts);
|
|
}
|
|
+ clear_port_changes(&handle, port, usb_3);
|
|
|
|
- // Ensure port is powered on
|
|
if !port_sts.is_powered() {
|
|
log::info!("power on port {port}");
|
|
- handle
|
|
- .device_request(
|
|
- PortReqTy::Class,
|
|
- PortReqRecipient::Other,
|
|
- usb::SetupReq::SetFeature as u8,
|
|
- usb::HubPortFeature::PortPower as u16,
|
|
- port as u16,
|
|
- DeviceReqData::NoData,
|
|
- )
|
|
- .expect("Failed to set port power");
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "set port power",
|
|
+ PortReqTy::Class,
|
|
+ PortReqRecipient::Other,
|
|
+ usb::SetupReq::SetFeature as u8,
|
|
+ usb::HubPortFeature::PortPower as u16,
|
|
+ port as u16,
|
|
+ DeviceReqData::NoData
|
|
+ );
|
|
state.ensure_attached(false);
|
|
continue;
|
|
}
|
|
|
|
- // Ignore disconnected port
|
|
if !port_sts.is_connected() {
|
|
state.ensure_attached(false);
|
|
continue;
|
|
}
|
|
|
|
- // Ignore port in reset
|
|
if port_sts.is_resetting() {
|
|
state.ensure_attached(false);
|
|
continue;
|
|
}
|
|
|
|
- // Ensure port is enabled
|
|
if !port_sts.is_enabled() {
|
|
log::info!("reset port {port}");
|
|
- handle
|
|
- .device_request(
|
|
- PortReqTy::Class,
|
|
- PortReqRecipient::Other,
|
|
- usb::SetupReq::SetFeature as u8,
|
|
- usb::HubPortFeature::PortReset as u16,
|
|
- port as u16,
|
|
- DeviceReqData::NoData,
|
|
- )
|
|
- .expect("Failed to set port enable");
|
|
+ hub_req!(
|
|
+ handle,
|
|
+ port,
|
|
+ "set port reset",
|
|
+ PortReqTy::Class,
|
|
+ PortReqRecipient::Other,
|
|
+ usb::SetupReq::SetFeature as u8,
|
|
+ usb::HubPortFeature::PortReset as u16,
|
|
+ port as u16,
|
|
+ DeviceReqData::NoData
|
|
+ );
|
|
state.ensure_attached(false);
|
|
continue;
|
|
}
|
|
@@ -241,9 +482,8 @@ fn main() {
|
|
state.ensure_attached(true);
|
|
}
|
|
|
|
- //TODO: use interrupts or poll faster?
|
|
- thread::sleep(time::Duration::new(1, 0));
|
|
+ if interrupt_ep.is_none() {
|
|
+ thread::sleep(time::Duration::from_millis(100));
|
|
+ }
|
|
}
|
|
-
|
|
- //TODO: read interrupt port for changes
|
|
}
|
|
diff --git a/drivers/usb/xhcid/src/driver_interface.rs b/drivers/usb/xhcid/src/driver_interface.rs
|
|
index 727f8d7e..bd5b7735 100644
|
|
--- a/drivers/usb/xhcid/src/driver_interface.rs
|
|
+++ b/drivers/usb/xhcid/src/driver_interface.rs
|
|
@@ -16,6 +16,63 @@ use thiserror::Error;
|
|
|
|
pub use crate::usb::{EndpointTy, ENDP_ATTR_TY_MASK};
|
|
|
|
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
|
+#[repr(u8)]
|
|
+pub enum UsbSpeed {
|
|
+ Low = 0,
|
|
+ Full = 1,
|
|
+ High = 2,
|
|
+ Super = 3,
|
|
+ SuperPlus = 4,
|
|
+}
|
|
+
|
|
+impl TryFrom<u8> for UsbSpeed {
|
|
+ type Error = ();
|
|
+
|
|
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
+ match value {
|
|
+ 0 => Ok(Self::Low),
|
|
+ 1 => Ok(Self::Full),
|
|
+ 2 => Ok(Self::High),
|
|
+ 3 => Ok(Self::Super),
|
|
+ 4 => Ok(Self::SuperPlus),
|
|
+ _ => Err(()),
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+impl UsbSpeed {
|
|
+ pub fn from_v2_port_status(status: crate::usb::HubPortStatusV2) -> Option<Self> {
|
|
+ if status.contains(crate::usb::HubPortStatusV2::HIGH_SPEED) {
|
|
+ Some(Self::High)
|
|
+ } else if status.contains(crate::usb::HubPortStatusV2::LOW_SPEED) {
|
|
+ Some(Self::Low)
|
|
+ } else if status.contains(crate::usb::HubPortStatusV2::CONNECTION) {
|
|
+ Some(Self::Full)
|
|
+ } else {
|
|
+ None
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /// Map from USB 3 hub port status speed bits (wPortStatus bits 12:10) to a speed category.
|
|
+ ///
|
|
+ /// Per USB 3.2 spec Table 10-12: 0=undefined, 1=Full, 2=High, 3=SuperSpeed.
|
|
+ /// SuperSpeedPlus requires reading Extended Port Status via a separate device request
|
|
+ /// and is not decoded here; values 4-7 are treated as SuperSpeedPlus as a conservative
|
|
+ /// upper bound.
|
|
+ pub fn from_v3_port_status(status: crate::usb::HubPortStatusV3) -> Option<Self> {
|
|
+ let speed_bits = (status.bits() >> 10) & 0x7;
|
|
+ match speed_bits {
|
|
+ 0 => None,
|
|
+ 1 => Some(Self::Full),
|
|
+ 2 => Some(Self::High),
|
|
+ 3 => Some(Self::Super),
|
|
+ 4..=7 => Some(Self::SuperPlus),
|
|
+ _ => None,
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
|
pub struct ConfigureEndpointsReq {
|
|
/// Index into the configuration descriptors of the device descriptor.
|
|
@@ -40,6 +97,8 @@ pub struct DevDesc {
|
|
pub product_str: Option<String>,
|
|
pub serial_str: Option<String>,
|
|
pub config_descs: SmallVec<[ConfDesc; 1]>,
|
|
+ pub supports_superspeed: bool,
|
|
+ pub supports_superspeedplus: bool,
|
|
}
|
|
|
|
impl DevDesc {
|
|
@@ -555,6 +614,11 @@ impl XhciClientHandle {
|
|
let _bytes_written = file.write(&[])?;
|
|
Ok(())
|
|
}
|
|
+ pub fn attach_with_speed(&self, speed: UsbSpeed) -> result::Result<(), XhciClientHandleError> {
|
|
+ let file = self.fd.openat("attach", libredox::flag::O_WRONLY, 0)?;
|
|
+ file.write(&[speed as u8])?;
|
|
+ Ok(())
|
|
+ }
|
|
pub fn detach(&self) -> result::Result<(), XhciClientHandleError> {
|
|
let file = self.fd.openat("detach", libredox::flag::O_WRONLY, 0)?;
|
|
let _bytes_written = file.write(&[])?;
|
|
@@ -832,7 +896,7 @@ impl XhciEndpHandle {
|
|
TransferStream {
|
|
bytes_to_transfer: total_len,
|
|
bytes_transferred: 0,
|
|
- bytes_per_transfer: 32768, // TODO
|
|
+ bytes_per_transfer: 32768,
|
|
endp_handle: self,
|
|
}
|
|
}
|
|
diff --git a/drivers/usb/xhcid/src/main.rs b/drivers/usb/xhcid/src/main.rs
|
|
index 25b2fdd6..97354ffe 100644
|
|
--- a/drivers/usb/xhcid/src/main.rs
|
|
+++ b/drivers/usb/xhcid/src/main.rs
|
|
@@ -140,8 +140,7 @@ fn daemon_with_context_size<const N: usize>(
|
|
|
|
let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize;
|
|
|
|
- let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
|
|
- //TODO: Fix interrupts.
|
|
+ let (irq_file, interrupt_method) = get_int_method(&mut pcid_handle);
|
|
|
|
log::info!("XHCI {}", pci_config.func.display());
|
|
|
|
diff --git a/drivers/usb/xhcid/src/usb/hub.rs b/drivers/usb/xhcid/src/usb/hub.rs
|
|
index 9dab55e8..69168bfc 100644
|
|
--- a/drivers/usb/xhcid/src/usb/hub.rs
|
|
+++ b/drivers/usb/xhcid/src/usb/hub.rs
|
|
@@ -88,8 +88,12 @@ pub enum HubPortFeature {
|
|
PortLinkState = 5,
|
|
PortPower = 8,
|
|
CPortConnection = 16,
|
|
+ CPortEnable = 17,
|
|
+ CPortSuspend = 18,
|
|
CPortOverCurrent = 19,
|
|
CPortReset = 20,
|
|
+ CPortLinkState = 25,
|
|
+ CPortConfigError = 26,
|
|
}
|
|
|
|
bitflags::bitflags! {
|
|
diff --git a/drivers/usb/xhcid/src/xhci/device_enumerator.rs b/drivers/usb/xhcid/src/xhci/device_enumerator.rs
|
|
index 74b9f732..32d7f640 100644
|
|
--- a/drivers/usb/xhcid/src/xhci/device_enumerator.rs
|
|
+++ b/drivers/usb/xhcid/src/xhci/device_enumerator.rs
|
|
@@ -28,7 +28,8 @@ impl<const N: usize> DeviceEnumerator<N> {
|
|
let request = match self.request_queue.recv() {
|
|
Ok(req) => req,
|
|
Err(err) => {
|
|
- panic!("Failed to received an enumeration request! error: {}", err)
|
|
+ log::error!("channel closed, device enumerator exiting: {}", err);
|
|
+ return;
|
|
}
|
|
};
|
|
|
|
@@ -38,7 +39,7 @@ impl<const N: usize> DeviceEnumerator<N> {
|
|
debug!("Device Enumerator request for port {}", port_id);
|
|
|
|
let (len, flags) = {
|
|
- let ports = self.hci.ports.lock().unwrap();
|
|
+ let ports = self.hci.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
let len = ports.len();
|
|
|
|
@@ -68,10 +69,11 @@ impl<const N: usize> DeviceEnumerator<N> {
|
|
&& !flags.contains(PortFlags::PR);
|
|
|
|
if !disabled_state {
|
|
- panic!(
|
|
- "Port {} isn't in the disabled state! Current flags: {:?}",
|
|
+ warn!(
|
|
+ "Port {} isn't in the disabled state! Current flags: {:?}. Continuing.",
|
|
port_id, flags
|
|
);
|
|
+ continue;
|
|
} else {
|
|
debug!("Port {} has entered the disabled state.", port_id);
|
|
}
|
|
@@ -80,7 +82,7 @@ impl<const N: usize> DeviceEnumerator<N> {
|
|
debug!("Received a device connect on port {}, but it's not enabled. Resetting the port.", port_id);
|
|
let _ = self.hci.reset_port(port_id);
|
|
|
|
- let mut ports = self.hci.ports.lock().unwrap();
|
|
+ let mut ports = self.hci.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
let port = &mut ports[port_array_index];
|
|
|
|
port.clear_prc();
|
|
diff --git a/drivers/usb/xhcid/src/xhci/event.rs b/drivers/usb/xhcid/src/xhci/event.rs
|
|
index 83af1209..4121b0ae 100644
|
|
--- a/drivers/usb/xhcid/src/xhci/event.rs
|
|
+++ b/drivers/usb/xhcid/src/xhci/event.rs
|
|
@@ -4,6 +4,7 @@ use syscall::error::Result;
|
|
use common::dma::Dma;
|
|
|
|
use super::ring::Ring;
|
|
+use super::runtime::RuntimeRegs;
|
|
use super::trb::Trb;
|
|
use super::Xhci;
|
|
|
|
@@ -24,9 +25,13 @@ pub struct EventRing {
|
|
|
|
impl EventRing {
|
|
pub fn new<const N: usize>(ac64: bool) -> Result<EventRing> {
|
|
+ Self::new_with_size::<N>(ac64, 256)
|
|
+ }
|
|
+
|
|
+ pub fn new_with_size<const N: usize>(ac64: bool, size: usize) -> Result<EventRing> {
|
|
let mut ring = EventRing {
|
|
ste: unsafe { Xhci::<N>::alloc_dma_zeroed_unsized_raw(ac64, 1)? },
|
|
- ring: Ring::new::<N>(ac64, 256, false)?,
|
|
+ ring: Ring::new::<N>(ac64, size, false)?,
|
|
};
|
|
|
|
ring.ste[0]
|
|
@@ -43,9 +48,14 @@ impl EventRing {
|
|
pub fn next(&mut self) -> &mut Trb {
|
|
self.ring.next().0
|
|
}
|
|
- pub fn erdp(&self) -> u64 {
|
|
+ pub fn dequeue_ptr(&self) -> u64 {
|
|
self.ring.register() & 0xFFFF_FFFF_FFFF_FFF0
|
|
}
|
|
+ pub fn erdp(&self, runtime_regs: &RuntimeRegs) -> u64 {
|
|
+ ((u64::from(runtime_regs.ints[0].erdp_high.read()) << 32)
|
|
+ | u64::from(runtime_regs.ints[0].erdp_low.read()))
|
|
+ & 0xFFFF_FFFF_FFFF_FFF0
|
|
+ }
|
|
pub fn erstba(&self) -> u64 {
|
|
self.ste.physical() as u64
|
|
}
|
|
diff --git a/drivers/usb/xhcid/src/xhci/irq_reactor.rs b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
|
index ac492d5b..ed193477 100644
|
|
--- a/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
|
+++ b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
|
|
@@ -9,6 +9,7 @@ use std::os::unix::io::AsRawFd;
|
|
|
|
use crossbeam_channel::{Receiver, Sender};
|
|
use log::{debug, error, info, trace, warn};
|
|
+use syscall::error::{Error, Result, EIO};
|
|
|
|
use super::doorbell::Doorbell;
|
|
use super::event::EventRing;
|
|
@@ -32,7 +33,7 @@ pub struct State {
|
|
|
|
impl State {
|
|
fn finish(self, message: Option<NextEventTrb>) {
|
|
- *self.message.lock().unwrap() = message;
|
|
+ *self.message.lock().unwrap_or_else(|e| e.into_inner()) = message;
|
|
trace!("Waking up future with waker: {:?}", self.waker);
|
|
self.waker.wake();
|
|
}
|
|
@@ -129,7 +130,7 @@ impl<const N: usize> IrqReactor<N> {
|
|
hci_clone
|
|
.primary_event_ring
|
|
.lock()
|
|
- .unwrap()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
.ring
|
|
.next_index()
|
|
};
|
|
@@ -137,7 +138,10 @@ impl<const N: usize> IrqReactor<N> {
|
|
'trb_loop: loop {
|
|
self.pause();
|
|
|
|
- let mut event_ring = hci_clone.primary_event_ring.lock().unwrap();
|
|
+ let mut event_ring = hci_clone
|
|
+ .primary_event_ring
|
|
+ .lock()
|
|
+ .unwrap_or_else(|e| e.into_inner());
|
|
|
|
let event_trb = &mut event_ring.ring.trbs[event_trb_index];
|
|
|
|
@@ -182,7 +186,7 @@ impl<const N: usize> IrqReactor<N> {
|
|
}
|
|
|
|
fn mask_interrupts(&mut self) {
|
|
- let mut run = self.hci.run.lock().unwrap();
|
|
+ let mut run = self.hci.run.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
debug!("Masking interrupts!");
|
|
|
|
@@ -194,7 +198,7 @@ impl<const N: usize> IrqReactor<N> {
|
|
}
|
|
|
|
fn unmask_interrupts(&mut self) {
|
|
- let mut run = self.hci.run.lock().unwrap();
|
|
+ let mut run = self.hci.run.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
debug!("unmasking interrupts!");
|
|
if run.ints[0].iman.readf(1 << 1) {
|
|
@@ -208,35 +212,72 @@ impl<const N: usize> IrqReactor<N> {
|
|
debug!("Running IRQ reactor with IRQ file and event queue");
|
|
|
|
let hci_clone = Arc::clone(&self.hci);
|
|
- let event_queue =
|
|
- RawEventQueue::new().expect("xhcid irq_reactor: failed to create IRQ event queue");
|
|
- let irq_fd = self.irq_file.as_ref().unwrap().as_raw_fd();
|
|
- event_queue
|
|
- .subscribe(irq_fd as usize, 0, event::EventFlags::READ)
|
|
- .unwrap();
|
|
+ let event_queue = match RawEventQueue::new() {
|
|
+ Ok(event_queue) => event_queue,
|
|
+ Err(err) => {
|
|
+ error!(
|
|
+ "xhcid irq_reactor: failed to create IRQ event queue: {}",
|
|
+ err
|
|
+ );
|
|
+ return self.run_polling();
|
|
+ }
|
|
+ };
|
|
+ let irq_fd = match self.irq_file.as_ref() {
|
|
+ Some(irq_file) => irq_file.as_raw_fd(),
|
|
+ None => {
|
|
+ error!("xhcid irq_reactor: missing IRQ file, falling back to polling mode");
|
|
+ return self.run_polling();
|
|
+ }
|
|
+ };
|
|
+ if let Err(err) = event_queue.subscribe(irq_fd as usize, 0, event::EventFlags::READ) {
|
|
+ error!(
|
|
+ "xhcid irq_reactor: failed to subscribe IRQ fd {}: {}",
|
|
+ irq_fd, err
|
|
+ );
|
|
+ return self.run_polling();
|
|
+ }
|
|
|
|
trace!("IRQ Reactor has created its event queue.");
|
|
let mut event_trb_index = {
|
|
hci_clone
|
|
.primary_event_ring
|
|
.lock()
|
|
- .unwrap()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
.ring
|
|
.next_index()
|
|
};
|
|
|
|
trace!("IRQ reactor has grabbed the next index in the event ring.");
|
|
'trb_loop: loop {
|
|
- let _event = event_queue.next_event().unwrap();
|
|
+ let _event = match event_queue.next_event() {
|
|
+ Ok(event) => event,
|
|
+ Err(err) => {
|
|
+ error!("xhcid irq_reactor: failed to read next IRQ event: {}", err);
|
|
+ continue 'trb_loop;
|
|
+ }
|
|
+ };
|
|
trace!("IRQ event queue notified");
|
|
let mut buffer = [0u8; 8];
|
|
|
|
- let _ = self
|
|
- .irq_file
|
|
- .as_mut()
|
|
- .unwrap()
|
|
- .read(&mut buffer)
|
|
- .expect("Failed to read from irq scheme");
|
|
+ {
|
|
+ let irq_file = match self.irq_file.as_mut() {
|
|
+ Some(irq_file) => irq_file,
|
|
+ None => {
|
|
+ error!(
|
|
+ "xhcid irq_reactor: IRQ file disappeared, falling back to polling mode"
|
|
+ );
|
|
+ return self.run_polling();
|
|
+ }
|
|
+ };
|
|
+
|
|
+ let _ = match irq_file.read(&mut buffer) {
|
|
+ Ok(n) => n,
|
|
+ Err(err) => {
|
|
+ log::error!("failed to read from irq scheme: {}", err);
|
|
+ continue 'trb_loop;
|
|
+ }
|
|
+ };
|
|
+ }
|
|
|
|
if !self.hci.received_irq() {
|
|
// continue only when an IRQ to this device was received
|
|
@@ -248,11 +289,19 @@ impl<const N: usize> IrqReactor<N> {
|
|
|
|
trace!("IRQ reactor received an IRQ");
|
|
|
|
- let _ = self.irq_file.as_mut().unwrap().write(&buffer);
|
|
+ if let Some(irq_file) = self.irq_file.as_mut() {
|
|
+ let _ = irq_file.write(&buffer);
|
|
+ } else {
|
|
+ error!("xhcid irq_reactor: IRQ file disappeared before IRQ acknowledgement");
|
|
+ return self.run_polling();
|
|
+ }
|
|
|
|
// TODO: More event rings, probably even with different IRQs.
|
|
|
|
- let mut event_ring = hci_clone.primary_event_ring.lock().unwrap();
|
|
+ let mut event_ring = hci_clone
|
|
+ .primary_event_ring
|
|
+ .lock()
|
|
+ .unwrap_or_else(|e| e.into_inner());
|
|
|
|
let mut count = 0;
|
|
|
|
@@ -321,17 +370,18 @@ impl<const N: usize> IrqReactor<N> {
|
|
route_string: 0,
|
|
};
|
|
trace!("Received Port Status Change Request on port {}", port_id);
|
|
- self.device_enumerator_sender
|
|
+ if let Err(err) = self
|
|
+ .device_enumerator_sender
|
|
.send(DeviceEnumerationRequest { port_id })
|
|
- .expect(
|
|
- format!(
|
|
- "Failed to transmit device numeration request on port {}",
|
|
- port_id
|
|
- )
|
|
- .as_str(),
|
|
+ {
|
|
+ log::error!(
|
|
+ "port {}: failed to send enumeration request: {}",
|
|
+ port_id,
|
|
+ err
|
|
);
|
|
+ }
|
|
{
|
|
- let mut ports = self.hci.ports.lock().unwrap();
|
|
+ let mut ports = self.hci.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
let root_port_index = port_id.root_hub_port_index();
|
|
if root_port_index >= ports.len() {
|
|
warn!(
|
|
@@ -353,7 +403,7 @@ impl<const N: usize> IrqReactor<N> {
|
|
}
|
|
|
|
fn update_erdp(&self, event_ring: &EventRing) {
|
|
- let dequeue_pointer_and_dcs = event_ring.erdp();
|
|
+ let dequeue_pointer_and_dcs = event_ring.dequeue_ptr();
|
|
let dequeue_pointer = dequeue_pointer_and_dcs & 0xFFFF_FFFF_FFFF_FFFE;
|
|
assert_eq!(
|
|
dequeue_pointer & 0xFFFF_FFFF_FFFF_FFF0,
|
|
@@ -363,10 +413,10 @@ impl<const N: usize> IrqReactor<N> {
|
|
|
|
trace!("Updated ERDP to {:#0x}", dequeue_pointer);
|
|
|
|
- self.hci.run.lock().unwrap().ints[0]
|
|
+ self.hci.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0]
|
|
.erdp_low
|
|
.write(dequeue_pointer as u32);
|
|
- self.hci.run.lock().unwrap().ints[0]
|
|
+ self.hci.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0]
|
|
.erdp_high
|
|
.write((dequeue_pointer >> 32) as u32);
|
|
}
|
|
@@ -400,7 +450,7 @@ impl<const N: usize> IrqReactor<N> {
|
|
.hci
|
|
.cmd
|
|
.lock()
|
|
- .unwrap()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
.phys_addr_to_entry_mut(self.hci.cap.ac64(), phys_ptr)
|
|
{
|
|
Some(command_trb) => {
|
|
@@ -533,8 +583,84 @@ impl<const N: usize> IrqReactor<N> {
|
|
}
|
|
/// Grows the event ring
|
|
fn grow_event_ring(&mut self) {
|
|
- // TODO
|
|
- error!("TODO: grow event ring");
|
|
+ let current_dequeue = {
|
|
+ let event_ring = self
|
|
+ .hci
|
|
+ .primary_event_ring
|
|
+ .lock()
|
|
+ .unwrap_or_else(|e| e.into_inner());
|
|
+ event_ring.ring.i
|
|
+ };
|
|
+ let current_size = {
|
|
+ let event_ring = self
|
|
+ .hci
|
|
+ .primary_event_ring
|
|
+ .lock()
|
|
+ .unwrap_or_else(|e| e.into_inner());
|
|
+ event_ring.ring.trbs.len()
|
|
+ };
|
|
+ let new_size = current_size * 2;
|
|
+ if new_size > 4096 {
|
|
+ log::error!("event ring growth capped at 4096 entries, skipping growth");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ log::info!(
|
|
+ "growing event ring from {} to {} entries",
|
|
+ current_size,
|
|
+ new_size
|
|
+ );
|
|
+
|
|
+ let ac64 = {
|
|
+ let event_ring = self
|
|
+ .hci
|
|
+ .primary_event_ring
|
|
+ .lock()
|
|
+ .unwrap_or_else(|e| e.into_inner());
|
|
+ let old_ste = &event_ring.ste;
|
|
+ old_ste[0].address_high.read() != 0
|
|
+ };
|
|
+
|
|
+ let mut new_event_ring = match EventRing::new_with_size::<N>(ac64, new_size) {
|
|
+ Ok(ring) => ring,
|
|
+ Err(err) => {
|
|
+ log::error!(
|
|
+ "failed to allocate larger event ring ({} entries): {}",
|
|
+ new_size,
|
|
+ err
|
|
+ );
|
|
+ return;
|
|
+ }
|
|
+ };
|
|
+
|
|
+ new_event_ring.ring.i = current_dequeue.min(new_size - 1);
|
|
+
|
|
+ let erdp = new_event_ring.ring.register();
|
|
+ let erstba = new_event_ring.erstba();
|
|
+
|
|
+ {
|
|
+ let mut event_ring = self
|
|
+ .hci
|
|
+ .primary_event_ring
|
|
+ .lock()
|
|
+ .unwrap_or_else(|e| e.into_inner());
|
|
+ *event_ring = new_event_ring;
|
|
+ }
|
|
+
|
|
+ {
|
|
+ let int = &mut self.hci.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0];
|
|
+ int.erdp_low.write(erdp as u32 | (1 << 3));
|
|
+ int.erdp_high.write((erdp as u64 >> 32) as u32);
|
|
+ int.erstba_low.write(erstba as u32);
|
|
+ int.erstba_high.write((erstba as u64 >> 32) as u32);
|
|
+ }
|
|
+
|
|
+ log::info!(
|
|
+ "event ring grown to {} entries, ERDP={:X}, ERSTBA={:X}",
|
|
+ new_size,
|
|
+ erdp,
|
|
+ erstba
|
|
+ );
|
|
}
|
|
|
|
pub fn run(self) -> ! {
|
|
@@ -570,7 +696,7 @@ impl EventDoorbell {
|
|
|
|
pub fn ring(self) {
|
|
trace!("Ring doorbell {} with data {}", self.index, self.data);
|
|
- self.dbs.lock().unwrap()[self.index].write(self.data);
|
|
+ self.dbs.lock().unwrap_or_else(|e| e.into_inner())[self.index].write(self.data);
|
|
trace!("Doorbell was rung.");
|
|
}
|
|
}
|
|
@@ -595,20 +721,26 @@ impl Future for EventTrbFuture {
|
|
ref state,
|
|
ref sender,
|
|
ref mut doorbell_opt,
|
|
- } => match state.message.lock().unwrap().take() {
|
|
+ } => match state
|
|
+ .message
|
|
+ .lock()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .take()
|
|
+ {
|
|
Some(message) => message,
|
|
|
|
None => {
|
|
// Register state with IRQ reactor
|
|
trace!("Send state {:X?}", state.state_kind);
|
|
- sender
|
|
- .send(State {
|
|
- message: Arc::clone(&state.message),
|
|
- is_isoch_or_vf: state.is_isoch_or_vf,
|
|
- kind: state.state_kind,
|
|
- waker: context.waker().clone(),
|
|
- })
|
|
- .expect("IRQ reactor thread unexpectedly stopped");
|
|
+ if let Err(err) = sender.send(State {
|
|
+ message: Arc::clone(&state.message),
|
|
+ is_isoch_or_vf: state.is_isoch_or_vf,
|
|
+ kind: state.state_kind,
|
|
+ waker: context.waker().clone(),
|
|
+ }) {
|
|
+ log::error!("IRQ reactor state channel closed: {}", err);
|
|
+ panic!("IRQ reactor state channel closed: {err}");
|
|
+ }
|
|
|
|
// Doorbell must be rung after sending state
|
|
if let Some(doorbell) = doorbell_opt.take() {
|
|
@@ -667,15 +799,20 @@ impl<const N: usize> Xhci<N> {
|
|
first_trb: &Trb,
|
|
last_trb: &Trb,
|
|
doorbell: EventDoorbell,
|
|
- ) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
|
+ ) -> Result<impl Future<Output = NextEventTrb> + Send + Sync + 'static> {
|
|
if !last_trb.is_transfer_trb() {
|
|
- panic!("Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}. Expected transfer TRB.", last_trb.trb_type(), last_trb)
|
|
+ error!(
|
|
+ "Invalid TRB type given to next_transfer_event_trb(): {} (TRB {:?}). Expected transfer TRB.",
|
|
+ last_trb.trb_type(),
|
|
+ last_trb
|
|
+ );
|
|
+ return Err(Error::new(EIO));
|
|
}
|
|
|
|
let is_isoch_or_vf = last_trb.trb_type() == TrbType::Isoch as u8;
|
|
- let first_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), first_trb);
|
|
- let last_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), last_trb);
|
|
- EventTrbFuture::Pending {
|
|
+ let first_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), first_trb)?;
|
|
+ let last_phys_ptr = ring.trb_phys_ptr(self.cap.ac64(), last_trb)?;
|
|
+ Ok(EventTrbFuture::Pending {
|
|
state: FutureState {
|
|
is_isoch_or_vf,
|
|
state_kind: StateKind::Transfer {
|
|
@@ -687,38 +824,39 @@ impl<const N: usize> Xhci<N> {
|
|
},
|
|
sender: self.irq_reactor_sender.clone(),
|
|
doorbell_opt: Some(doorbell),
|
|
- }
|
|
+ })
|
|
}
|
|
pub fn next_command_completion_event_trb(
|
|
&self,
|
|
command_ring: &Ring,
|
|
trb: &Trb,
|
|
doorbell: EventDoorbell,
|
|
- ) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
|
- trace!(
|
|
- "Sending command at phys_ptr {:X}",
|
|
- command_ring.trb_phys_ptr(self.cap.ac64(), trb)
|
|
- );
|
|
+ ) -> Result<impl Future<Output = NextEventTrb> + Send + Sync + 'static> {
|
|
if !trb.is_command_trb() {
|
|
- panic!("Invalid TRB type given to next_command_completion_event_trb(): {} (TRB {:?}. Expected command TRB.", trb.trb_type(), trb)
|
|
+ error!(
|
|
+ "Invalid TRB type given to next_command_completion_event_trb(): {} (TRB {:?}). Expected command TRB.",
|
|
+ trb.trb_type(),
|
|
+ trb
|
|
+ );
|
|
+ return Err(Error::new(EIO));
|
|
}
|
|
- EventTrbFuture::Pending {
|
|
+ let phys_ptr = command_ring.trb_phys_ptr(self.cap.ac64(), trb)?;
|
|
+ trace!("Sending command at phys_ptr {:X}", phys_ptr);
|
|
+ Ok(EventTrbFuture::Pending {
|
|
state: FutureState {
|
|
// This is only possible for transfers if they are isochronous, or for Force Event TRBs (virtualization).
|
|
is_isoch_or_vf: false,
|
|
- state_kind: StateKind::CommandCompletion {
|
|
- phys_ptr: command_ring.trb_phys_ptr(self.cap.ac64(), trb),
|
|
- },
|
|
+ state_kind: StateKind::CommandCompletion { phys_ptr },
|
|
message: Arc::new(Mutex::new(None)),
|
|
},
|
|
sender: self.irq_reactor_sender.clone(),
|
|
doorbell_opt: Some(doorbell),
|
|
- }
|
|
+ })
|
|
}
|
|
pub fn next_misc_event_trb(
|
|
&self,
|
|
trb_type: TrbType,
|
|
- ) -> impl Future<Output = NextEventTrb> + Send + Sync + 'static {
|
|
+ ) -> Result<impl Future<Output = NextEventTrb> + Send + Sync + 'static> {
|
|
let valid_trb_types = [
|
|
TrbType::PortStatusChange as u8,
|
|
TrbType::BandwidthRequest as u8,
|
|
@@ -728,9 +866,13 @@ impl<const N: usize> Xhci<N> {
|
|
TrbType::MfindexWrap as u8,
|
|
];
|
|
if !valid_trb_types.contains(&(trb_type as u8)) {
|
|
- panic!("Invalid TRB type given to next_misc_event_trb(): {:?}. Only event TRB types that are neither transfer events or command completion events can be used.", trb_type)
|
|
+ error!(
|
|
+ "Invalid TRB type given to next_misc_event_trb(): {:?}. Only event TRB types that are neither transfer events or command completion events can be used.",
|
|
+ trb_type
|
|
+ );
|
|
+ return Err(Error::new(EIO));
|
|
}
|
|
- EventTrbFuture::Pending {
|
|
+ Ok(EventTrbFuture::Pending {
|
|
state: FutureState {
|
|
is_isoch_or_vf: false,
|
|
state_kind: StateKind::Other(trb_type),
|
|
@@ -738,6 +880,6 @@ impl<const N: usize> Xhci<N> {
|
|
},
|
|
sender: self.irq_reactor_sender.clone(),
|
|
doorbell_opt: None,
|
|
- }
|
|
+ })
|
|
}
|
|
}
|
|
diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs
|
|
index f2143676..a51b98c1 100644
|
|
--- a/drivers/usb/xhcid/src/xhci/mod.rs
|
|
+++ b/drivers/usb/xhcid/src/xhci/mod.rs
|
|
@@ -110,7 +110,7 @@ impl<const N: usize> Xhci<N> {
|
|
.get_mut(&0)
|
|
.ok_or(Error::new(EIO))?
|
|
.ring()
|
|
- .expect("no ring for the default control pipe");
|
|
+ .ok_or(Error::new(EIO))?;
|
|
|
|
let first_index = ring.next_index();
|
|
let (cmd, cycle) = (&mut ring.trbs[first_index], ring.cycle);
|
|
@@ -140,7 +140,7 @@ impl<const N: usize> Xhci<N> {
|
|
&ring.trbs[first_index],
|
|
&ring.trbs[last_index],
|
|
EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell()),
|
|
- )
|
|
+ )?
|
|
};
|
|
|
|
debug!("Waiting for the next transfer event TRB...");
|
|
@@ -485,7 +485,11 @@ impl<const N: usize> Xhci<N> {
|
|
pub fn init(&mut self, max_slots: u8) -> Result<()> {
|
|
// Set run/stop to 0
|
|
debug!("Stopping xHC.");
|
|
- self.op.get_mut().unwrap().usb_cmd.writef(USB_CMD_RS, false);
|
|
+ self.op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .usb_cmd
|
|
+ .writef(USB_CMD_RS, false);
|
|
|
|
// Warm reset
|
|
{
|
|
@@ -493,10 +497,16 @@ impl<const N: usize> Xhci<N> {
|
|
let timeout = Timeout::from_secs(1);
|
|
self.op
|
|
.get_mut()
|
|
- .unwrap()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
.usb_cmd
|
|
.writef(USB_CMD_HCRST, true);
|
|
- while self.op.get_mut().unwrap().usb_cmd.readf(USB_CMD_HCRST) {
|
|
+ while self
|
|
+ .op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .usb_cmd
|
|
+ .readf(USB_CMD_HCRST)
|
|
+ {
|
|
timeout.run().map_err(|()| {
|
|
log::error!("timeout on USB_CMD_HCRST");
|
|
Error::new(EIO)
|
|
@@ -506,51 +516,76 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
// Set enabled slots
|
|
debug!("Setting enabled slots to {}.", max_slots);
|
|
- self.op.get_mut().unwrap().config.write(max_slots as u32);
|
|
+ self.op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .config
|
|
+ .write(max_slots as u32);
|
|
debug!(
|
|
"Enabled Slots: {}",
|
|
- self.op.get_mut().unwrap().config.read() & 0xFF
|
|
+ self.op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .config
|
|
+ .read()
|
|
+ & 0xFF
|
|
);
|
|
|
|
// Set device context address array pointer
|
|
let dcbaap = self.dev_ctx.dcbaap();
|
|
debug!("Writing DCBAAP: {:X}", dcbaap);
|
|
- self.op.get_mut().unwrap().dcbaap_low.write(dcbaap as u32);
|
|
self.op
|
|
.get_mut()
|
|
- .unwrap()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .dcbaap_low
|
|
+ .write(dcbaap as u32);
|
|
+ self.op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
.dcbaap_high
|
|
.write((dcbaap as u64 >> 32) as u32);
|
|
|
|
// Set command ring control register
|
|
- let crcr = self.cmd.get_mut().unwrap().register();
|
|
+ let crcr = self.cmd.get_mut().unwrap_or_else(|e| e.into_inner()).register();
|
|
assert_eq!(crcr & 0xFFFF_FFFF_FFFF_FFC1, crcr, "unaligned CRCR");
|
|
debug!("Writing CRCR: {:X}", crcr);
|
|
- self.op.get_mut().unwrap().crcr_low.write(crcr as u32);
|
|
self.op
|
|
.get_mut()
|
|
- .unwrap()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .crcr_low
|
|
+ .write(crcr as u32);
|
|
+ self.op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
.crcr_high
|
|
.write((crcr as u64 >> 32) as u32);
|
|
|
|
// Set event ring segment table registers
|
|
debug!(
|
|
"Interrupter 0: {:p}",
|
|
- self.run.get_mut().unwrap().ints.as_ptr()
|
|
+ self.run.get_mut().unwrap_or_else(|e| e.into_inner()).ints.as_ptr()
|
|
);
|
|
{
|
|
- let int = &mut self.run.get_mut().unwrap().ints[0];
|
|
+ let int = &mut self.run.get_mut().unwrap_or_else(|e| e.into_inner()).ints[0];
|
|
|
|
let erstz = 1;
|
|
debug!("Writing ERSTZ: {}", erstz);
|
|
int.erstsz.write(erstz);
|
|
|
|
- let erdp = self.primary_event_ring.get_mut().unwrap().erdp();
|
|
+ let erdp = self
|
|
+ .primary_event_ring
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .dequeue_ptr();
|
|
debug!("Writing ERDP: {:X}", erdp);
|
|
int.erdp_low.write(erdp as u32 | (1 << 3));
|
|
int.erdp_high.write((erdp as u64 >> 32) as u32);
|
|
|
|
- let erstba = self.primary_event_ring.get_mut().unwrap().erstba();
|
|
+ let erstba = self
|
|
+ .primary_event_ring
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .erstba();
|
|
debug!("Writing ERSTBA: {:X}", erstba);
|
|
int.erstba_low.write(erstba as u32);
|
|
int.erstba_high.write((erstba as u64 >> 32) as u32);
|
|
@@ -563,7 +598,7 @@ impl<const N: usize> Xhci<N> {
|
|
}
|
|
self.op
|
|
.get_mut()
|
|
- .unwrap()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
.usb_cmd
|
|
.writef(USB_CMD_INTE, true);
|
|
|
|
@@ -572,12 +607,22 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
// Set run/stop to 1
|
|
debug!("Starting xHC.");
|
|
- self.op.get_mut().unwrap().usb_cmd.writef(USB_CMD_RS, true);
|
|
+ self.op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .usb_cmd
|
|
+ .writef(USB_CMD_RS, true);
|
|
|
|
{
|
|
debug!("Waiting for start request to complete.");
|
|
let timeout = Timeout::from_secs(1);
|
|
- while self.op.get_mut().unwrap().usb_sts.readf(USB_STS_HCH) {
|
|
+ while self
|
|
+ .op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .usb_sts
|
|
+ .readf(USB_STS_HCH)
|
|
+ {
|
|
timeout.run().map_err(|()| {
|
|
log::error!("timeout on USB_STS_HCH");
|
|
Error::new(EIO)
|
|
@@ -587,11 +632,14 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
// Ring command doorbell
|
|
debug!("Ringing command doorbell.");
|
|
- self.dbs.lock().unwrap()[0].write(0);
|
|
+ self.dbs.lock().unwrap_or_else(|e| e.into_inner())[0].write(0);
|
|
|
|
debug!("XHCI initialized.");
|
|
|
|
- self.op.get_mut().unwrap().set_cie(self.cap.cic());
|
|
+ self.op
|
|
+ .get_mut()
|
|
+ .unwrap_or_else(|e| e.into_inner())
|
|
+ .set_cie(self.cap.cic());
|
|
|
|
self.print_port_capabilities();
|
|
|
|
@@ -599,15 +647,20 @@ impl<const N: usize> Xhci<N> {
|
|
}
|
|
|
|
pub fn get_pls(&self, port_id: PortId) -> u8 {
|
|
- let mut ports = self.ports.lock().unwrap();
|
|
- let port = ports.get_mut(port_id.root_hub_port_index()).unwrap();
|
|
- port.state()
|
|
+ let mut ports = self.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
+ match ports.get_mut(port_id.root_hub_port_index()) {
|
|
+ Some(port) => port.state(),
|
|
+ None => {
|
|
+ warn!("get_pls: invalid root hub port index {}", port_id.root_hub_port_index());
|
|
+ 0
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
pub fn poll(&self) {
|
|
debug!("Polling Initial Devices!");
|
|
|
|
- let len = self.ports.lock().unwrap().len();
|
|
+ let len = self.ports.lock().unwrap_or_else(|e| e.into_inner()).len();
|
|
|
|
for root_hub_port_num in 1..=(len as u8) {
|
|
let port_id = PortId {
|
|
@@ -617,7 +670,7 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
//Get the CCS and CSC flags
|
|
let (ccs, csc, flags) = {
|
|
- let mut ports = self.ports.lock().unwrap();
|
|
+ let mut ports = self.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
let port = &mut ports[port_id.root_hub_port_index()];
|
|
let flags = port.flags();
|
|
let ccs = flags.contains(PortFlags::CCS);
|
|
@@ -633,10 +686,11 @@ impl<const N: usize> Xhci<N> {
|
|
//Do nothing
|
|
}
|
|
_ => {
|
|
- //Either something is connected, or nothing is connected and a port status change was asserted.
|
|
- self.device_enumerator_sender
|
|
+ if let Err(err) = self.device_enumerator_sender
|
|
.send(DeviceEnumerationRequest { port_id })
|
|
- .expect("Failed to generate the port enumeration request!");
|
|
+ {
|
|
+ log::error!("port {}: failed to send enumeration request: {}", port_id, err);
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
@@ -645,7 +699,7 @@ impl<const N: usize> Xhci<N> {
|
|
pub fn print_port_capabilities(&self) {
|
|
let len;
|
|
{
|
|
- let mut ports = self.ports.lock().unwrap();
|
|
+ let mut ports = self.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
len = ports.len();
|
|
}
|
|
|
|
@@ -658,7 +712,7 @@ impl<const N: usize> Xhci<N> {
|
|
let state = self.get_pls(port_id);
|
|
let mut flags;
|
|
{
|
|
- let mut ports = self.ports.lock().unwrap();
|
|
+ let mut ports = self.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
flags = ports[port_id.root_hub_port_index()].flags();
|
|
}
|
|
@@ -684,9 +738,11 @@ impl<const N: usize> Xhci<N> {
|
|
pub fn reset_port(&self, port_id: PortId) -> Result<()> {
|
|
debug!("XHCI Port {} reset", port_id);
|
|
|
|
- //TODO handle the second unwrap
|
|
- let mut ports = self.ports.lock().unwrap();
|
|
- let port = ports.get_mut(port_id.root_hub_port_index()).unwrap();
|
|
+ let mut ports = self.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
+ let port = ports.get_mut(port_id.root_hub_port_index()).ok_or_else(|| {
|
|
+ warn!("reset_port: invalid root hub port index {}", port_id.root_hub_port_index());
|
|
+ Error::new(EIO)
|
|
+ })?;
|
|
let instant = std::time::Instant::now();
|
|
|
|
debug!("Port {} Link State: {}", port_id, port.state());
|
|
@@ -731,7 +787,7 @@ impl<const N: usize> Xhci<N> {
|
|
{
|
|
// If ERDP EHB bit is set, clear it before sending command
|
|
//TODO: find out why this bit is set earlier!
|
|
- let mut run = self.run.lock().unwrap();
|
|
+ let mut run = self.run.lock().unwrap_or_else(|e| e.into_inner());
|
|
let mut int = &mut run.ints[index];
|
|
|
|
if int.erdp_low.readf(1 << 3) {
|
|
@@ -743,7 +799,7 @@ impl<const N: usize> Xhci<N> {
|
|
}
|
|
|
|
pub fn interrupt_is_pending(&self, index: usize) -> bool {
|
|
- let mut run = self.run.lock().unwrap();
|
|
+ let mut run = self.run.lock().unwrap_or_else(|e| e.into_inner());
|
|
let mut int = &mut run.ints[index];
|
|
int.erdp_low.readf(1 << 3)
|
|
}
|
|
@@ -753,7 +809,7 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
let (event_trb, command_trb) = self
|
|
.execute_command(|cmd, cycle| cmd.enable_slot(slot_ty, cycle))
|
|
- .await;
|
|
+ .await?;
|
|
|
|
trace!("Slot is enabled!");
|
|
self::scheme::handle_event_trb("ENABLE_SLOT", &event_trb, &command_trb)?;
|
|
@@ -765,7 +821,7 @@ impl<const N: usize> Xhci<N> {
|
|
trace!("Disable slot {}", slot);
|
|
let (event_trb, command_trb) = self
|
|
.execute_command(|cmd, cycle| cmd.disable_slot(slot, cycle))
|
|
- .await;
|
|
+ .await?;
|
|
|
|
self::scheme::handle_event_trb("DISABLE_SLOT", &event_trb, &command_trb)?;
|
|
//self.event_handler_finished();
|
|
@@ -793,19 +849,58 @@ impl<const N: usize> Xhci<N> {
|
|
}
|
|
|
|
pub async fn attach_device(&self, port_id: PortId) -> syscall::Result<()> {
|
|
+ self.attach_device_with_speed(port_id, None).await
|
|
+ }
|
|
+
|
|
+ pub async fn attach_device_with_speed(
|
|
+ &self,
|
|
+ port_id: PortId,
|
|
+ speed_override: Option<u8>,
|
|
+ ) -> syscall::Result<()> {
|
|
if self.port_states.contains_key(&port_id) {
|
|
debug!("Already contains port {}", port_id);
|
|
return Err(syscall::Error::new(EAGAIN));
|
|
}
|
|
|
|
- let (data, state, speed, flags) = {
|
|
- let port = &self.ports.lock().unwrap()[port_id.root_hub_port_index()];
|
|
+ let (data, state, portsc_speed, flags) = {
|
|
+ let port = &self.ports.lock().unwrap_or_else(|e| e.into_inner())[port_id.root_hub_port_index()];
|
|
(port.read(), port.state(), port.speed(), port.flags())
|
|
};
|
|
|
|
+ let speed = match speed_override {
|
|
+ Some(byte) => {
|
|
+ let category_res = UsbSpeed::try_from(byte).ok();
|
|
+ match category_res {
|
|
+ Some(category) => match self.lookup_speed_category(port_id, category) {
|
|
+ Some(proto_speed) => {
|
|
+ let psiv = proto_speed.psiv();
|
|
+ log::info!(
|
|
+ "port {} speed override {:?} mapped to PSIV {}",
|
|
+ port_id,
|
|
+ category,
|
|
+ psiv
|
|
+ );
|
|
+ psiv
|
|
+ }
|
|
+ None => {
|
|
+ log::warn!(
|
|
+ "port {} no protocol speed found for {:?}, falling back to PORTSC speed {}",
|
|
+ port_id,
|
|
+ category,
|
|
+ portsc_speed
|
|
+ );
|
|
+ portsc_speed
|
|
+ }
|
|
+ },
|
|
+ None => portsc_speed,
|
|
+ }
|
|
+ }
|
|
+ None => portsc_speed,
|
|
+ };
|
|
+
|
|
debug!(
|
|
- "XHCI Port {}: {:X}, State {}, Speed {}, Flags {:?}",
|
|
- port_id, data, state, speed, flags
|
|
+ "XHCI Port {}: {:X}, State {}, Speed {} (override {:?})",
|
|
+ port_id, data, state, speed, speed_override
|
|
);
|
|
|
|
if flags.contains(port::PortFlags::CCS) {
|
|
@@ -829,10 +924,13 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
debug!("Enabled port {}, which the xHC mapped to {}", port_id, slot);
|
|
|
|
- //TODO: get correct speed for child devices
|
|
- let protocol_speed = self
|
|
- .lookup_psiv(port_id, speed)
|
|
- .expect("Failed to retrieve speed ID");
|
|
+ let protocol_speed = match self.lookup_psiv(port_id, speed) {
|
|
+ Some(ps) => ps,
|
|
+ None => {
|
|
+ log::error!("port {}: no protocol speed for PSIV {}, cannot attach", port_id, speed);
|
|
+ return Err(syscall::Error::new(syscall::EINVAL));
|
|
+ }
|
|
+ };
|
|
|
|
let mut input = unsafe { self.alloc_dma_zeroed::<InputContext<N>>()? };
|
|
|
|
@@ -873,9 +971,12 @@ impl<const N: usize> Xhci<N> {
|
|
// Ensure correct packet size is used
|
|
let dev_desc_8_byte = self.fetch_dev_desc_8_byte(port_id, slot).await?;
|
|
{
|
|
- let mut port_state = self.port_states.get_mut(&port_id).unwrap();
|
|
+ let mut port_state = self.port_states.get_mut(&port_id).ok_or_else(|| {
|
|
+ warn!("fetch_descriptors: missing port state for {}", port_id);
|
|
+ Error::new(EIO)
|
|
+ })?;
|
|
|
|
- let mut input = port_state.input_context.lock().unwrap();
|
|
+ let mut input = port_state.input_context.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
self.update_max_packet_size(&mut *input, slot, dev_desc_8_byte)
|
|
.await?;
|
|
@@ -885,15 +986,24 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
let dev_desc = self.get_desc(port_id, slot).await?;
|
|
debug!("Got the full device descriptor!");
|
|
- self.port_states.get_mut(&port_id).unwrap().dev_desc = Some(dev_desc);
|
|
+ self.port_states.get_mut(&port_id).ok_or_else(|| {
|
|
+ warn!("fetch_descriptors: missing port state for {}", port_id);
|
|
+ Error::new(EIO)
|
|
+ })?.dev_desc = Some(dev_desc);
|
|
|
|
debug!("Got the port states again!");
|
|
{
|
|
- let mut port_state = self.port_states.get_mut(&port_id).unwrap();
|
|
+ let mut port_state = self.port_states.get_mut(&port_id).ok_or_else(|| {
|
|
+ warn!("fetch_descriptors: missing port state for {}", port_id);
|
|
+ Error::new(EIO)
|
|
+ })?;
|
|
|
|
- let mut input = port_state.input_context.lock().unwrap();
|
|
+ let mut input = port_state.input_context.lock().unwrap_or_else(|e| e.into_inner());
|
|
debug!("Got the input context!");
|
|
- let dev_desc = port_state.dev_desc.as_ref().unwrap();
|
|
+ let dev_desc = port_state.dev_desc.as_ref().ok_or_else(|| {
|
|
+ warn!("fetch_descriptors: device descriptor not set for {}", port_id);
|
|
+ Error::new(EIO)
|
|
+ })?;
|
|
|
|
self.update_default_control_pipe(&mut *input, slot, dev_desc)
|
|
.await?;
|
|
@@ -932,12 +1042,12 @@ impl<const N: usize> Xhci<N> {
|
|
);
|
|
}
|
|
None => {
|
|
- //TODO: kill harder
|
|
warn!(
|
|
- "driver process {} for port {} still running",
|
|
+ "driver process {} for port {} still running, sending SIGKILL",
|
|
child.id(),
|
|
port_id
|
|
);
|
|
+ let _ = child.kill();
|
|
}
|
|
},
|
|
Err(err) => {
|
|
@@ -1001,7 +1111,7 @@ impl<const N: usize> Xhci<N> {
|
|
.execute_command(|trb, cycle| {
|
|
trb.evaluate_context(slot_id, input_context.physical(), false, cycle)
|
|
})
|
|
- .await;
|
|
+ .await?;
|
|
|
|
self::scheme::handle_event_trb("EVALUATE_CONTEXT", &event_trb, &command_trb)?;
|
|
//self.event_handler_finished();
|
|
@@ -1035,7 +1145,7 @@ impl<const N: usize> Xhci<N> {
|
|
.execute_command(|trb, cycle| {
|
|
trb.evaluate_context(slot_id, input_context.physical(), false, cycle)
|
|
})
|
|
- .await;
|
|
+ .await?;
|
|
debug!("Completed the command to update the default control pipe");
|
|
|
|
self::scheme::handle_event_trb("EVALUATE_CONTEXT", &event_trb, &command_trb)?;
|
|
@@ -1060,13 +1170,10 @@ impl<const N: usize> Xhci<N> {
|
|
if let Some((parent_port, port_num)) = port.parent() {
|
|
match self.port_states.get(&parent_port) {
|
|
Some(parent_state) => {
|
|
- // parent info must be supplied if:
|
|
let mut needs_parent_info = false;
|
|
+ // parent info must be supplied if:
|
|
// 1. the device is low or full speed and connected through a high speed hub
|
|
- //TODO: determine device speed (speed is not accurate as it comes from the port)
|
|
// 2. the device is superspeed and connected through a higher rank hub
|
|
- //TODO: determine device speed (speed is not accurate as it comes from the port)
|
|
- // For now, this is just set to true to force things to work
|
|
needs_parent_info = true;
|
|
if needs_parent_info {
|
|
parent_hub_slot_id = parent_state.slot;
|
|
@@ -1110,8 +1217,7 @@ impl<const N: usize> Xhci<N> {
|
|
| (u32::from(number_of_ports) << 24),
|
|
);
|
|
|
|
- // TODO
|
|
- let ttt = 0u8;
|
|
+ let ttt = 0u8; // TODO: read from parent hub HubDesc think_time field
|
|
let interrupter = 0u8;
|
|
|
|
assert_eq!(ttt & 0b11, ttt);
|
|
@@ -1133,7 +1239,7 @@ impl<const N: usize> Xhci<N> {
|
|
512
|
|
};
|
|
let host_initiate_disable = false; // only applies to streams
|
|
- let max_burst_size = 0u8; // TODO
|
|
+ let max_burst_size = 0u8;
|
|
|
|
assert_eq!(max_error_count & 0b11, max_error_count);
|
|
input_context.device.endpoints[0].b.write(
|
|
@@ -1166,7 +1272,7 @@ impl<const N: usize> Xhci<N> {
|
|
.execute_command(|trb, cycle| {
|
|
trb.address_device(slot, input_context_physical, false, cycle)
|
|
})
|
|
- .await;
|
|
+ .await?;
|
|
|
|
if event_trb.completion_code() != TrbCompletionCode::Success as u8 {
|
|
error!(
|
|
@@ -1190,7 +1296,7 @@ impl<const N: usize> Xhci<N> {
|
|
/// Checks whether an IRQ has been received from *this* device, in case of an interrupt. Always
|
|
/// true when using MSI/MSI-X.
|
|
pub fn received_irq(&self) -> bool {
|
|
- let mut runtime_regs = self.run.lock().unwrap();
|
|
+ let mut runtime_regs = self.run.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
if self.uses_msi_interrupts() {
|
|
// Since using MSI and MSI-X implies having no IRQ sharing whatsoever, the IP bit
|
|
@@ -1224,7 +1330,10 @@ impl<const N: usize> Xhci<N> {
|
|
// TODO: Now that there are some good error crates, I don't think errno.h error codes are
|
|
// suitable here.
|
|
|
|
- let ps = self.port_states.get(&port).unwrap();
|
|
+ let ps = self.port_states.get(&port).ok_or_else(|| {
|
|
+ warn!("spawn_drivers: missing port state for {}", port);
|
|
+ Error::new(EIO)
|
|
+ })?;
|
|
trace!("Spawning driver on port: {}", port);
|
|
|
|
//TODO: support choosing config?
|
|
@@ -1243,7 +1352,10 @@ impl<const N: usize> Xhci<N> {
|
|
})?;
|
|
|
|
trace!("Got config and device descriptors on port {}", port);
|
|
- let drivers_usercfg: &DriversConfig = &DRIVERS_CONFIG;
|
|
+ let drivers_usercfg = DRIVERS_CONFIG.as_ref().map_err(|err| {
|
|
+ error!("failed to parse internally embedded xhcid drivers config: {}", err);
|
|
+ Error::new(EIO)
|
|
+ })?;
|
|
|
|
for ifdesc in config_desc.interface_descs.iter() {
|
|
//TODO: support alternate settings
|
|
@@ -1435,13 +1547,27 @@ impl<const N: usize> Xhci<N> {
|
|
self.supported_protocol_speeds(port)
|
|
.find(|speed| speed.psiv() == psiv)
|
|
}
|
|
+
|
|
+ fn lookup_speed_category(
|
|
+ &self,
|
|
+ port: PortId,
|
|
+ category: UsbSpeed,
|
|
+ ) -> Option<&'static ProtocolSpeed> {
|
|
+ self.supported_protocol_speeds(port).find(|speed| match category {
|
|
+ UsbSpeed::Low => speed.is_lowspeed(),
|
|
+ UsbSpeed::Full => speed.is_fullspeed(),
|
|
+ UsbSpeed::High => speed.is_highspeed(),
|
|
+ UsbSpeed::Super => speed.is_superspeed_gen1x1(),
|
|
+ UsbSpeed::SuperPlus => speed.is_superspeed_gen_x() && !speed.is_superspeed_gen1x1(),
|
|
+ })
|
|
+ }
|
|
}
|
|
pub fn start_irq_reactor<const N: usize>(hci: &Arc<Xhci<N>>, irq_file: Option<File>) {
|
|
let hci_clone = Arc::clone(&hci);
|
|
|
|
debug!("About to start IRQ reactor");
|
|
|
|
- *hci.irq_reactor.lock().unwrap() = Some(thread::spawn(move || {
|
|
+ *hci.irq_reactor.lock().unwrap_or_else(|e| e.into_inner()) = Some(thread::spawn(move || {
|
|
debug!("Started IRQ reactor thread");
|
|
IrqReactor::new(hci_clone, irq_file).run()
|
|
}));
|
|
@@ -1452,7 +1578,7 @@ pub fn start_device_enumerator<const N: usize>(hci: &Arc<Xhci<N>>) {
|
|
|
|
debug!("About to start Device Enumerator");
|
|
|
|
- *hci.device_enumerator.lock().unwrap() = Some(thread::spawn(move || {
|
|
+ *hci.device_enumerator.lock().unwrap_or_else(|e| e.into_inner()) = Some(thread::spawn(move || {
|
|
debug!("Started Device Enumerator");
|
|
DeviceEnumerator::new(hci_clone).run();
|
|
}));
|
|
@@ -1480,10 +1606,10 @@ use crate::xhci::port::PortFlags;
|
|
use lazy_static::lazy_static;
|
|
|
|
lazy_static! {
|
|
- static ref DRIVERS_CONFIG: DriversConfig = {
|
|
+ static ref DRIVERS_CONFIG: std::result::Result<DriversConfig, toml::de::Error> = {
|
|
// TODO: Load this at runtime.
|
|
const TOML: &'static [u8] = include_bytes!("../../drivers.toml");
|
|
|
|
- toml::from_slice::<DriversConfig>(TOML).expect("Failed to parse internally embedded config file")
|
|
+ toml::from_slice::<DriversConfig>(TOML)
|
|
};
|
|
}
|
|
diff --git a/drivers/usb/xhcid/src/xhci/ring.rs b/drivers/usb/xhcid/src/xhci/ring.rs
|
|
index 8e187ebe..c05ed8fb 100644
|
|
--- a/drivers/usb/xhcid/src/xhci/ring.rs
|
|
+++ b/drivers/usb/xhcid/src/xhci/ring.rs
|
|
@@ -1,6 +1,7 @@
|
|
use std::mem;
|
|
|
|
-use syscall::error::Result;
|
|
+use log::error;
|
|
+use syscall::error::{Error, Result, EIO};
|
|
|
|
use common::dma::Dma;
|
|
|
|
@@ -108,7 +109,7 @@ impl Ring {
|
|
pub(crate) fn end_virt_addr(&self) -> *const Trb {
|
|
unsafe { self.start_virt_addr().offset(self.trbs.len() as isize) }
|
|
}
|
|
- pub fn trb_phys_ptr(&self, ac64: bool, trb: &Trb) -> u64 {
|
|
+ pub fn trb_phys_ptr(&self, ac64: bool, trb: &Trb) -> Result<u64> {
|
|
let trb_virt_pointer = trb as *const Trb;
|
|
let trbs_base_virt_pointer = self.trbs.as_ptr();
|
|
|
|
@@ -116,7 +117,12 @@ impl Ring {
|
|
|| (trb_virt_pointer as usize)
|
|
> (trbs_base_virt_pointer as usize) + self.trbs.len() * mem::size_of::<Trb>()
|
|
{
|
|
- panic!("Gave a TRB outside of the ring, when retrieving its physical address in that ring. TRB: {:?} (at address {:p})", trb, trb);
|
|
+ error!(
|
|
+ "Gave a TRB outside of the ring when retrieving its physical address. TRB: {:?} (at address {:p})",
|
|
+ trb,
|
|
+ trb
|
|
+ );
|
|
+ return Err(Error::new(EIO));
|
|
}
|
|
let trb_offset_from_base = trb_virt_pointer as u64 - trbs_base_virt_pointer as u64;
|
|
|
|
@@ -127,7 +133,7 @@ impl Ring {
|
|
0xFFFF_FFFF
|
|
};
|
|
let trb_phys_ptr = trbs_base_phys_ptr + trb_offset_from_base;
|
|
- trb_phys_ptr
|
|
+ Ok(trb_phys_ptr)
|
|
}
|
|
/*
|
|
/// Endless mutable iterator that iterates through the ring items, over and over again. The
|
|
diff --git a/drivers/usb/xhcid/src/xhci/scheme.rs b/drivers/usb/xhcid/src/xhci/scheme.rs
|
|
index f2d439a4..5868d13d 100644
|
|
--- a/drivers/usb/xhcid/src/xhci/scheme.rs
|
|
+++ b/drivers/usb/xhcid/src/xhci/scheme.rs
|
|
@@ -54,31 +54,37 @@ use crate::driver_interface::*;
|
|
use regex::Regex;
|
|
|
|
lazy_static! {
|
|
- static ref REGEX_PORT_CONFIGURE: Regex = Regex::new(r"^port([\d\.]+)/configure$")
|
|
- .expect("Failed to create the regex for the port<n>/configure scheme.");
|
|
- static ref REGEX_PORT_ATTACH: Regex = Regex::new(r"^port([\d\.]+)/attach$")
|
|
- .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_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_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$")
|
|
- .expect("Failed to create the regex for the port<n>/endpoints scheme");
|
|
- static ref REGEX_PORT_SPECIFIC_ENDPOINT: Regex =
|
|
- Regex::new(r"^port([\d\.]+)/endpoints/(\d{1,3})$")
|
|
- .expect("Failed to create the regex for the port<n>/endpoints/<n> scheme");
|
|
- static ref REGEX_PORT_SUB_ENDPOINT: Regex = Regex::new(
|
|
- r"port([\d\.]+)/endpoints/(\d{1,3})/(ctl|data)$"
|
|
- )
|
|
- .expect("Failed to create the regex for the port<n>/endpoints/<n>/<sub_endpoint> scheme");
|
|
- static ref REGEX_PORT_ROOT: Regex =
|
|
- Regex::new(r"^port([\d\.]+)$").expect("Failed to create the regex for the port<n> scheme.");
|
|
- static ref REGEX_TOP_LEVEL: Regex =
|
|
- Regex::new(r"^$").expect("Failed to create the regex for the top-level scheme");
|
|
+ static ref REGEX_PORT_CONFIGURE: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/configure$");
|
|
+ static ref REGEX_PORT_ATTACH: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/attach$");
|
|
+ static ref REGEX_PORT_DETACH: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/detach$");
|
|
+ static ref REGEX_PORT_DESCRIPTORS: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/descriptors$");
|
|
+ static ref REGEX_PORT_STATE: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/state$");
|
|
+ static ref REGEX_PORT_REQUEST: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/request$");
|
|
+ static ref REGEX_PORT_ENDPOINTS: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/endpoints$");
|
|
+ static ref REGEX_PORT_SPECIFIC_ENDPOINT: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)/endpoints/(\d{1,3})$");
|
|
+ static ref REGEX_PORT_SUB_ENDPOINT: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"port([\d\.]+)/endpoints/(\d{1,3})/(ctl|data)$");
|
|
+ static ref REGEX_PORT_ROOT: std::result::Result<Regex, regex::Error> =
|
|
+ Regex::new(r"^port([\d\.]+)$");
|
|
+ static ref REGEX_TOP_LEVEL: std::result::Result<Regex, regex::Error> = Regex::new(r"^$");
|
|
+}
|
|
+
|
|
+fn compiled_regex(
|
|
+ regex: &'static std::result::Result<Regex, regex::Error>,
|
|
+ description: &'static str,
|
|
+) -> Result<&'static Regex> {
|
|
+ regex.as_ref().map_err(|err| {
|
|
+ error!("failed to compile {} regex: {}", description, err);
|
|
+ Error::new(EIO)
|
|
+ })
|
|
}
|
|
|
|
pub enum ControlFlow {
|
|
@@ -369,51 +375,64 @@ impl SchemeParameters {
|
|
//and store it if it's valid.
|
|
|
|
//Generate the regular expressions for all of our valid schemes.
|
|
+ let regex_port_configure = compiled_regex(®EX_PORT_CONFIGURE, "port<n>/configure")?;
|
|
+ let regex_port_attach = compiled_regex(®EX_PORT_ATTACH, "port<n>/attach")?;
|
|
+ let regex_port_detach = compiled_regex(®EX_PORT_DETACH, "port<n>/detach")?;
|
|
+ let regex_port_descriptors = compiled_regex(®EX_PORT_DESCRIPTORS, "port<n>/descriptors")?;
|
|
+ let regex_port_state = compiled_regex(®EX_PORT_STATE, "port<n>/state")?;
|
|
+ let regex_port_request = compiled_regex(®EX_PORT_REQUEST, "port<n>/request")?;
|
|
+ let regex_port_endpoints = compiled_regex(®EX_PORT_ENDPOINTS, "port<n>/endpoints")?;
|
|
+ let regex_port_specific_endpoint =
|
|
+ compiled_regex(®EX_PORT_SPECIFIC_ENDPOINT, "port<n>/endpoints/<n>")?;
|
|
+ let regex_port_sub_endpoint =
|
|
+ compiled_regex(®EX_PORT_SUB_ENDPOINT, "port<n>/endpoints/<n>/<sub_endpoint>")?;
|
|
+ let regex_port_root = compiled_regex(®EX_PORT_ROOT, "port<n>")?;
|
|
+ let regex_top_level = compiled_regex(®EX_TOP_LEVEL, "top-level")?;
|
|
|
|
//Check if we have a match and either return a partially initialized scheme, OR ENOENT
|
|
- if REGEX_PORT_CONFIGURE.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_CONFIGURE, scheme, 0)?;
|
|
+ if regex_port_configure.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_configure, scheme, 0)?;
|
|
|
|
Ok(Self::ConfigureEndpoints(port_num))
|
|
- } else if REGEX_PORT_ATTACH.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_ATTACH, scheme, 0)?;
|
|
+ } else if regex_port_attach.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_attach, scheme, 0)?;
|
|
|
|
Ok(Self::AttachDevice(port_num))
|
|
- } else if REGEX_PORT_DETACH.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_DETACH, scheme, 0)?;
|
|
+ } else if regex_port_detach.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_detach, scheme, 0)?;
|
|
|
|
Ok(Self::DetachDevice(port_num))
|
|
- } else if REGEX_PORT_DESCRIPTORS.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_DESCRIPTORS, scheme, 0)?;
|
|
+ } else if regex_port_descriptors.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_descriptors, scheme, 0)?;
|
|
|
|
Ok(Self::PortDesc(port_num))
|
|
- } else if REGEX_PORT_STATE.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_STATE, scheme, 0)?;
|
|
+ } else if regex_port_state.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_state, scheme, 0)?;
|
|
|
|
Ok(Self::PortState(port_num))
|
|
- } else if REGEX_PORT_REQUEST.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_REQUEST, scheme, 0)?;
|
|
+ } else if regex_port_request.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_request, scheme, 0)?;
|
|
|
|
Ok(Self::PortReq(port_num))
|
|
- } else if REGEX_PORT_ENDPOINTS.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_ENDPOINTS, scheme, 0)?;
|
|
+ } else if regex_port_endpoints.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_endpoints, scheme, 0)?;
|
|
|
|
Ok(Self::Endpoints(port_num))
|
|
- } else if REGEX_PORT_SPECIFIC_ENDPOINT.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_SPECIFIC_ENDPOINT, scheme, 0)?;
|
|
- let endpoint_num = get_u8_from_regex(®EX_PORT_SPECIFIC_ENDPOINT, scheme, 1)?;
|
|
+ } else if regex_port_specific_endpoint.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_specific_endpoint, scheme, 0)?;
|
|
+ let endpoint_num = get_u8_from_regex(regex_port_specific_endpoint, scheme, 1)?;
|
|
|
|
Ok(Self::Endpoint(port_num, endpoint_num, String::from("root")))
|
|
- } else if REGEX_PORT_SUB_ENDPOINT.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_SUB_ENDPOINT, scheme, 0)?;
|
|
- let endpoint_num = get_u8_from_regex(®EX_PORT_SUB_ENDPOINT, scheme, 1)?;
|
|
- let handle_type = get_string_from_regex(®EX_PORT_SUB_ENDPOINT, scheme, 2)?;
|
|
+ } else if regex_port_sub_endpoint.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_sub_endpoint, scheme, 0)?;
|
|
+ let endpoint_num = get_u8_from_regex(regex_port_sub_endpoint, scheme, 1)?;
|
|
+ let handle_type = get_string_from_regex(regex_port_sub_endpoint, scheme, 2)?;
|
|
|
|
Ok(Self::Endpoint(port_num, endpoint_num, handle_type))
|
|
- } else if REGEX_PORT_ROOT.is_match(scheme) {
|
|
- let port_num = get_port_id_from_regex(®EX_PORT_ROOT, scheme, 0)?;
|
|
+ } else if regex_port_root.is_match(scheme) {
|
|
+ let port_num = get_port_id_from_regex(regex_port_root, scheme, 0)?;
|
|
Ok(Self::Port(port_num))
|
|
- } else if REGEX_TOP_LEVEL.is_match(scheme) {
|
|
+ } else if regex_top_level.is_match(scheme) {
|
|
Ok(Self::TopLevel)
|
|
} else {
|
|
Err(Error::new(ENOENT))
|
|
@@ -589,7 +608,7 @@ impl<const N: usize> Xhci<N> {
|
|
///
|
|
/// # Locking
|
|
/// This function will lock `Xhci::cmd` and `Xhci::dbs`.
|
|
- pub async fn execute_command<F: FnOnce(&mut Trb, bool)>(&self, f: F) -> (Trb, Trb) {
|
|
+ pub async fn execute_command<F: FnOnce(&mut Trb, bool)>(&self, f: F) -> Result<(Trb, Trb)> {
|
|
//TODO: find out why this bit is set earlier!
|
|
if self.interrupt_is_pending(0) {
|
|
debug!("The EHB bit is already set!");
|
|
@@ -597,7 +616,7 @@ impl<const N: usize> Xhci<N> {
|
|
}
|
|
|
|
let next_event = {
|
|
- let mut command_ring = self.cmd.lock().unwrap();
|
|
+ let mut command_ring = self.cmd.lock().unwrap_or_else(|e| e.into_inner());
|
|
let (cmd_index, cycle) = (command_ring.next_index(), command_ring.cycle);
|
|
|
|
debug!("Sending command with cycle bit {}", cycle as u8);
|
|
@@ -613,12 +632,15 @@ impl<const N: usize> Xhci<N> {
|
|
&*command_ring,
|
|
command_trb,
|
|
EventDoorbell::new(self, 0, 0),
|
|
- )
|
|
+ )?
|
|
};
|
|
|
|
let trbs = next_event.await;
|
|
let event_trb = trbs.event_trb;
|
|
- let command_trb = trbs.src_trb.expect("Command completion event TRBs shall always have a valid pointer to a valid source command TRB");
|
|
+ let command_trb = trbs.src_trb.ok_or_else(|| {
|
|
+ error!("command completion event TRB missing source command TRB");
|
|
+ Error::new(EIO)
|
|
+ })?;
|
|
|
|
assert_eq!(
|
|
event_trb.trb_type(),
|
|
@@ -626,7 +648,7 @@ impl<const N: usize> Xhci<N> {
|
|
"The IRQ reactor (or the xHC) gave an invalid event TRB"
|
|
);
|
|
|
|
- (event_trb, command_trb)
|
|
+ Ok((event_trb, command_trb))
|
|
}
|
|
pub async fn execute_control_transfer<D>(
|
|
&self,
|
|
@@ -681,7 +703,7 @@ impl<const N: usize> Xhci<N> {
|
|
&ring.trbs[first_index],
|
|
&ring.trbs[last_index],
|
|
EventDoorbell::new(self, usize::from(slot), Self::def_control_endp_doorbell()),
|
|
- )
|
|
+ )?
|
|
};
|
|
|
|
let trbs = future.await;
|
|
@@ -773,7 +795,7 @@ impl<const N: usize> Xhci<N> {
|
|
doorbell_data_no_stream
|
|
},
|
|
),
|
|
- );
|
|
+ )?;
|
|
}
|
|
ControlFlow::Continue => continue,
|
|
}
|
|
@@ -856,7 +878,7 @@ impl<const N: usize> Xhci<N> {
|
|
.execute_command(|trb, cycle| {
|
|
trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle);
|
|
})
|
|
- .await;
|
|
+ .await?;
|
|
//self.event_handler_finished();
|
|
|
|
handle_event_trb("RESET_ENDPOINT", &event_trb, &command_trb)
|
|
@@ -893,7 +915,9 @@ impl<const N: usize> Xhci<N> {
|
|
endp_desc: &EndpDesc,
|
|
) -> u8 {
|
|
if speed_id.is_highspeed() && (endp_desc.is_interrupt() || endp_desc.is_isoch()) {
|
|
- assert_eq!(dev_desc.major_version(), 2);
|
|
+ if dev_desc.major_version() != 2 {
|
|
+ log::warn!("high-speed endpoint on USB {} device, expected USB 2", dev_desc.major_version());
|
|
+ }
|
|
((endp_desc.max_packet_size & 0x0C00) >> 11) as u8
|
|
} else if endp_desc.is_superspeed() {
|
|
endp_desc.max_burst()
|
|
@@ -916,10 +940,10 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
if dev_desc.major_version() == 2 && endp_desc.is_periodic() {
|
|
u32::from(max_packet_size) * (u32::from(max_burst_size) + 1)
|
|
- } else if endp_desc.has_ssp_companion() {
|
|
- endp_desc.sspc.as_ref().unwrap().bytes_per_interval
|
|
- } else if endp_desc.ssc.is_some() {
|
|
- u32::from(endp_desc.ssc.as_ref().unwrap().bytes_per_interval)
|
|
+ } else if let Some(sspc) = endp_desc.sspc.as_ref() {
|
|
+ sspc.bytes_per_interval
|
|
+ } else if let Some(ssc) = endp_desc.ssc.as_ref() {
|
|
+ u32::from(ssc.bytes_per_interval)
|
|
} else if speed_id.is_fullspeed() && endp_desc.is_interrupt() {
|
|
64
|
|
} else if speed_id.is_fullspeed() && endp_desc.is_isoch() {
|
|
@@ -957,16 +981,17 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
- port_state.cfg_idx = Some(req.config_desc);
|
|
-
|
|
- let config_desc = port_state
|
|
+ let dev_desc = port_state
|
|
.dev_desc
|
|
.as_ref()
|
|
- .unwrap()
|
|
+ .ok_or(Error::new(EBADFD))?;
|
|
+
|
|
+ let config_desc = dev_desc
|
|
.config_descs
|
|
.iter()
|
|
.find(|desc| desc.configuration_value == req.config_desc)
|
|
.ok_or(Error::new(EBADFD))?;
|
|
+ let configuration_value = config_desc.configuration_value;
|
|
@@ -987,16 +1012,18 @@ impl<const N: usize> Xhci<N> {
|
|
return Err(Error::new(EIO));
|
|
}
|
|
|
|
+ port_state.cfg_idx = Some(configuration_value);
|
|
+
|
|
(
|
|
endp_desc_count,
|
|
new_context_entries,
|
|
- config_desc.configuration_value,
|
|
+ configuration_value,
|
|
)
|
|
};
|
|
let lec = self.cap.lec();
|
|
let log_max_psa_size = self.cap.max_psa_size();
|
|
|
|
@@ -996,7 +1022,7 @@ impl<const N: usize> Xhci<N> {
|
|
let lec = self.cap.lec();
|
|
let log_max_psa_size = self.cap.max_psa_size();
|
|
|
|
- let port_speed_id = self.ports.lock().unwrap()[port.root_hub_port_index()].speed();
|
|
+ let port_speed_id = self.ports.lock().unwrap_or_else(|e| e.into_inner())[port.root_hub_port_index()].speed();
|
|
let speed_id: &ProtocolSpeed = self.lookup_psiv(port, port_speed_id).ok_or_else(|| {
|
|
warn!("no speed_id");
|
|
Error::new(EIO)
|
|
@@ -1004,7 +1030,7 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
{
|
|
let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?;
|
|
- let mut input_context = port_state.input_context.lock().unwrap();
|
|
+ let mut input_context = port_state.input_context.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
// Configure the slot context as well, which holds the last index of the endp descs.
|
|
input_context.add_context.write(1);
|
|
@@ -1035,7 +1061,7 @@ impl<const N: usize> Xhci<N> {
|
|
input_context.device.slot.a.write(current_slot_a);
|
|
input_context.device.slot.b.write(current_slot_b);
|
|
|
|
- let control = if self.op.lock().unwrap().cie() {
|
|
+ let control = if self.op.lock().unwrap_or_else(|e| e.into_inner()).cie() {
|
|
(u32::from(req.alternate_setting.unwrap_or(0)) << 16)
|
|
| (u32::from(req.interface_desc.unwrap_or(0)) << 8)
|
|
| u32::from(configuration_value)
|
|
@@ -1049,7 +1075,7 @@ impl<const N: usize> Xhci<N> {
|
|
let endp_num = endp_idx + 1;
|
|
|
|
let mut port_state = self.port_states.get_mut(&port).ok_or(Error::new(EBADFD))?;
|
|
- let dev_desc = port_state.dev_desc.as_ref().unwrap();
|
|
+ let dev_desc = port_state.dev_desc.as_ref().ok_or(Error::new(EBADFD))?;
|
|
let endp_desc = port_state.get_endp_desc(endp_idx).ok_or_else(|| {
|
|
warn!("failed to find endpoint {}", endp_idx);
|
|
Error::new(EIO)
|
|
@@ -1111,7 +1137,10 @@ impl<const N: usize> Xhci<N> {
|
|
assert_eq!(ep_ty & 0x7, ep_ty);
|
|
assert_eq!(mult & 0x3, mult);
|
|
assert_eq!(max_error_count & 0x3, max_error_count);
|
|
- assert_ne!(ep_ty, 0); // 0 means invalid.
|
|
+ if ep_ty == 0 {
|
|
+ warn!("endpoint {} has invalid xHCI type 0", endp_num);
|
|
+ return Err(Error::new(EIO));
|
|
+ }
|
|
|
|
let ring_ptr = if usb_log_max_streams.is_some() {
|
|
let mut array =
|
|
@@ -1155,7 +1184,7 @@ impl<const N: usize> Xhci<N> {
|
|
};
|
|
assert_eq!(primary_streams & 0x1F, primary_streams);
|
|
|
|
- let mut input_context = port_state.input_context.lock().unwrap();
|
|
+ let mut input_context = port_state.input_context.lock().unwrap_or_else(|e| e.into_inner());
|
|
input_context.add_context.writef(1 << endp_num_xhc, true);
|
|
|
|
let endp_i = endp_num_xhc as usize - 1;
|
|
@@ -1191,13 +1220,13 @@ impl<const N: usize> Xhci<N> {
|
|
{
|
|
let port_state = self.port_states.get(&port).ok_or(Error::new(EBADFD))?;
|
|
let slot = port_state.slot;
|
|
- let input_context_physical = port_state.input_context.lock().unwrap().physical();
|
|
+ let input_context_physical = port_state.input_context.lock().unwrap_or_else(|e| e.into_inner()).physical();
|
|
|
|
- let (event_trb, command_trb) = self
|
|
- .execute_command(|trb, cycle| {
|
|
- trb.configure_endpoint(slot, input_context_physical, cycle)
|
|
- })
|
|
- .await;
|
|
+ let (event_trb, command_trb) = self
|
|
+ .execute_command(|trb, cycle| {
|
|
+ trb.configure_endpoint(slot, input_context_physical, cycle)
|
|
+ })
|
|
+ .await?;
|
|
|
|
//self.event_handler_finished();
|
|
|
|
@@ -1234,8 +1263,16 @@ 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?;
|
|
+ if let Err(err) = self.set_interface(port, interface_num, alternate_setting).await {
|
|
+ if alternate_setting == 0 && interface_num == 0 {
|
|
+ log::debug!(
|
|
+ "port {}: SET_INTERFACE(0,0) failed (stall likely): {}, continuing",
|
|
+ port, err
|
|
+ );
|
|
+ } else {
|
|
+ return Err(err);
|
|
+ }
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -1261,7 +1298,8 @@ impl<const N: usize> Xhci<N> {
|
|
)
|
|
.await?;
|
|
|
|
- buf.copy_from_slice(&*dma_buffer.as_ref().unwrap());
|
|
+ let dma_ref = dma_buffer.as_ref().ok_or(Error::new(EIO))?;
|
|
+ buf.copy_from_slice(&*dma_ref);
|
|
Ok((completion_code, bytes_transferred))
|
|
}
|
|
async fn transfer_write(
|
|
@@ -1327,7 +1365,6 @@ impl<const N: usize> Xhci<N> {
|
|
dma_buf: Option<Dma<[u8]>>,
|
|
direction: PortReqDirection,
|
|
) -> Result<(u8, u32, Option<Dma<[u8]>>)> {
|
|
- // TODO: Check that only readable enpoints are read, etc.
|
|
let endp_num = endp_idx + 1;
|
|
|
|
let mut port_state = self
|
|
@@ -1442,7 +1479,7 @@ impl<const N: usize> Xhci<N> {
|
|
Ok((event.completion_code(), bytes_transferred, dma_buf))
|
|
}
|
|
pub async fn get_desc(&self, port_id: PortId, slot: u8) -> Result<DevDesc> {
|
|
- let ports = self.ports.lock().unwrap();
|
|
+ let ports = self.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
let port = ports
|
|
.get(port_id.root_hub_port_index())
|
|
.ok_or(Error::new(ENOENT))?;
|
|
@@ -1506,12 +1543,45 @@ impl<const N: usize> Xhci<N> {
|
|
serial_str
|
|
);
|
|
|
|
- //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) =
|
|
+ match self.fetch_bos_desc(port_id, slot).await {
|
|
+ Ok((bos_desc, bos_data)) => {
|
|
+ let bos_len = bos_desc.total_len as usize;
|
|
+ let bos_slice = if bos_len <= bos_data.len() {
|
|
+ &bos_data[..bos_len]
|
|
+ } else {
|
|
+ log::warn!(
|
|
+ "port {} slot {} BOS total_len {} exceeds buffer {}, truncating",
|
|
+ port_id, slot, bos_len, bos_data.len()
|
|
+ );
|
|
+ &bos_data[..]
|
|
+ };
|
|
+ let caps: Vec<_> = usb::bos_capability_descs(
|
|
+ bos_desc,
|
|
+ bos_slice,
|
|
+ )
|
|
+ .collect();
|
|
+ let ss = caps.iter().any(|desc| desc.is_superspeed());
|
|
+ let ssp = caps.iter().any(|desc| desc.is_superspeedplus());
|
|
+ log::info!(
|
|
+ "port {} slot {} BOS: superspeed={} superspeedplus={}",
|
|
+ port_id,
|
|
+ slot,
|
|
+ ss,
|
|
+ ssp
|
|
+ );
|
|
+ (ss, ssp)
|
|
+ }
|
|
+ Err(err) => {
|
|
+ log::debug!(
|
|
+ "port {} slot {} BOS descriptor not available: {}",
|
|
+ port_id,
|
|
+ slot,
|
|
+ err
|
|
+ );
|
|
+ (false, false)
|
|
+ }
|
|
+ };
|
|
|
|
let mut config_descs = SmallVec::new();
|
|
|
|
@@ -1564,11 +1634,11 @@ impl<const N: usize> Xhci<N> {
|
|
match iter.peek() {
|
|
Some(AnyDescriptor::SuperSpeedCompanion(n)) => {
|
|
endp.ssc = Some(SuperSpeedCmp::from(n.clone()));
|
|
- iter.next().unwrap();
|
|
+ let _ = iter.next();
|
|
}
|
|
Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => {
|
|
endp.sspc = Some(SuperSpeedPlusIsochCmp::from(n.clone()));
|
|
- iter.next().unwrap();
|
|
+ let _ = iter.next();
|
|
}
|
|
_ => break,
|
|
}
|
|
@@ -1619,6 +1689,8 @@ impl<const N: usize> Xhci<N> {
|
|
product_str,
|
|
serial_str,
|
|
config_descs,
|
|
+ supports_superspeed,
|
|
+ supports_superspeedplus,
|
|
})
|
|
}
|
|
fn port_desc_json(&self, port_id: PortId) -> Result<Vec<u8>> {
|
|
@@ -1801,14 +1873,14 @@ impl<const N: usize> Xhci<N> {
|
|
if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 {
|
|
let mut contents = Vec::new();
|
|
|
|
- let ports_guard = self.ports.lock().unwrap();
|
|
+ let ports_guard = self.ports.lock().unwrap_or_else(|e| e.into_inner());
|
|
|
|
for (index, _) in ports_guard
|
|
.iter()
|
|
.enumerate()
|
|
.filter(|(_, port)| port.flags().contains(port::PortFlags::CCS))
|
|
{
|
|
- write!(contents, "port{}\n", index).unwrap();
|
|
+ write!(contents, "port{}\n", index).map_err(|_| Error::new(EIO))?;
|
|
}
|
|
|
|
Ok(Handle::TopLevel(contents))
|
|
@@ -1856,7 +1928,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\n").map_err(|_| Error::new(EIO))?;
|
|
|
|
if self.slot_state(
|
|
self.port_states
|
|
@@ -1865,7 +1937,7 @@ impl<const N: usize> Xhci<N> {
|
|
.slot as usize,
|
|
) != SlotState::Configured as u8
|
|
{
|
|
- write!(contents, "configure\n").unwrap();
|
|
+ write!(contents, "configure\n").map_err(|_| Error::new(EIO))?;
|
|
}
|
|
|
|
Ok(Handle::Port(port_num, contents))
|
|
@@ -1916,7 +1988,7 @@ impl<const N: usize> Xhci<N> {
|
|
}*/
|
|
|
|
for ep_num in ps.endpoint_states.keys() {
|
|
- write!(contents, "{}\n", ep_num).unwrap();
|
|
+ write!(contents, "{}\n", ep_num).map_err(|_| Error::new(EIO))?;
|
|
}
|
|
|
|
Ok(Handle::Endpoints(port_num, contents))
|
|
@@ -2007,10 +2079,13 @@ impl<const N: usize> Xhci<N> {
|
|
};
|
|
Ok(Handle::Endpoint(port_num, endpoint_num, st))
|
|
}
|
|
- _ => panic!(
|
|
- "Scheme parser returned an invalid string '{}' for the endpoint handle type",
|
|
- handle_type
|
|
- ),
|
|
+ _ => {
|
|
+ log::error!(
|
|
+ "Scheme parser returned an invalid string '{}' for the endpoint handle type",
|
|
+ handle_type
|
|
+ );
|
|
+ return Err(Error::new(ENOENT));
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -2218,16 +2293,9 @@ impl<const N: usize> SchemeSync for &Xhci<N> {
|
|
let guard = self.handles.get(&fd).ok_or(Error::new(EBADF))?;
|
|
let scheme = (&*guard).to_scheme();
|
|
|
|
- write!(cursor, "{}", scheme.as_str()).expect(
|
|
- format!(
|
|
- "Failed to convert the file descriptor with value {} to the associated file path",
|
|
- fd
|
|
- )
|
|
- .as_str(),
|
|
- );
|
|
+ write!(cursor, "{}", scheme.as_str()).map_err(|_| Error::new(EIO))?;
|
|
|
|
- let src_len = usize::try_from(cursor.seek(io::SeekFrom::End(0)).unwrap()).unwrap();
|
|
- Ok(src_len)
|
|
+ Ok(cursor.position() as usize)
|
|
}
|
|
|
|
fn read(
|
|
@@ -2324,12 +2392,15 @@ impl<const N: usize> SchemeSync for &Xhci<N> {
|
|
Ok(buf.len())
|
|
}
|
|
&mut Handle::AttachDevice(port_num) => {
|
|
- //TODO: accept some arguments in buffer?
|
|
- block_on(self.attach_device(port_num))?;
|
|
+ let speed_override = if buf.len() == 1 {
|
|
+ Some(buf[0])
|
|
+ } else {
|
|
+ None
|
|
+ };
|
|
+ block_on(self.attach_device_with_speed(port_num, speed_override))?;
|
|
Ok(buf.len())
|
|
}
|
|
&mut Handle::DetachDevice(port_num) => {
|
|
- //TODO: accept some arguments in buffer?
|
|
block_on(self.detach_device(port_num))?;
|
|
Ok(buf.len())
|
|
}
|
|
@@ -2364,7 +2435,7 @@ impl<const N: usize> Xhci<N> {
|
|
let endp_desc = port_state
|
|
.dev_desc
|
|
.as_ref()
|
|
- .unwrap()
|
|
+ .ok_or(Error::new(EBADFD))?
|
|
.config_descs
|
|
.get(0)
|
|
.ok_or(Error::new(EIO))?
|
|
@@ -2409,8 +2480,12 @@ impl<const N: usize> Xhci<N> {
|
|
if self.get_endp_status(port_num, endp_num)? != EndpointStatus::Halted {
|
|
return Err(Error::new(EPROTO));
|
|
}
|
|
- // Change the endpoint state from anything, but most likely HALTED (otherwise resetting
|
|
- // would be quite meaningless), to stopped.
|
|
+
|
|
+ let endp_idx = endp_num.checked_sub(1).ok_or(Error::new(EIO))?;
|
|
+ let port_state = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?;
|
|
+ let endp_desc = port_state.get_endp_desc(endp_idx).ok_or(Error::new(EBADF))?;
|
|
+ let usb_endp_addr = endp_desc.address;
|
|
+
|
|
self.reset_endpoint(port_num, endp_num, false).await?;
|
|
self.restart_endpoint(port_num, endp_num).await?;
|
|
|
|
@@ -2418,10 +2493,10 @@ impl<const N: usize> Xhci<N> {
|
|
self.device_req_no_data(
|
|
port_num,
|
|
usb::Setup {
|
|
- kind: 0b0000_0010, // endpoint recipient
|
|
- request: 0x01, // CLEAR_FEATURE
|
|
- value: 0x00, // ENDPOINT_HALT
|
|
- index: 0, // TODO: interface num
|
|
+ kind: 0b0000_0010,
|
|
+ request: 0x01,
|
|
+ value: 0x00,
|
|
+ index: u16::from(usb_endp_addr),
|
|
length: 0,
|
|
},
|
|
)
|
|
@@ -2456,7 +2531,7 @@ impl<const N: usize> Xhci<N> {
|
|
let endp_desc = port_state
|
|
.dev_desc
|
|
.as_ref()
|
|
- .unwrap()
|
|
+ .ok_or(Error::new(EBADFD))?
|
|
.config_descs
|
|
.get(0)
|
|
.ok_or(Error::new(EIO))?
|
|
@@ -2475,7 +2550,7 @@ impl<const N: usize> Xhci<N> {
|
|
Self::def_control_endp_doorbell()
|
|
};
|
|
|
|
- self.dbs.lock().unwrap()[slot as usize].write(doorbell);
|
|
+ self.dbs.lock().unwrap_or_else(|e| e.into_inner())[slot as usize].write(doorbell);
|
|
|
|
self.set_tr_deque_ptr(port_num, endp_num, deque_ptr_and_cycle)
|
|
.await?;
|
|
@@ -2483,13 +2558,14 @@ impl<const N: usize> Xhci<N> {
|
|
Ok(())
|
|
}
|
|
pub fn endp_direction(&self, port_num: PortId, endp_num: u8) -> Result<EndpDirection> {
|
|
+ let endp_idx = endp_num.checked_sub(1).ok_or(Error::new(EIO))? as usize;
|
|
Ok(self
|
|
.port_states
|
|
.get(&port_num)
|
|
.ok_or(Error::new(EIO))?
|
|
.dev_desc
|
|
.as_ref()
|
|
- .unwrap()
|
|
+ .ok_or(Error::new(EBADFD))?
|
|
.config_descs
|
|
.first()
|
|
.ok_or(Error::new(EIO))?
|
|
.interface_descs
|
|
.first()
|
|
.ok_or(Error::new(EIO))?
|
|
.endpoints
|
|
- .get(endp_num as usize)
|
|
+ .get(endp_idx)
|
|
.ok_or(Error::new(EIO))?
|
|
.direction())
|
|
}
|
|
@@ -2530,7 +2605,7 @@ impl<const N: usize> Xhci<N> {
|
|
slot,
|
|
)
|
|
})
|
|
- .await;
|
|
+ .await?;
|
|
//self.event_handler_finished();
|
|
|
|
handle_event_trb("SET_TR_DEQUEUE_PTR", &event_trb, &command_trb)
|
|
@@ -2724,7 +2799,7 @@ impl<const N: usize> Xhci<N> {
|
|
|
|
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)
|
|
+ Ok(cursor.position() as usize)
|
|
}
|
|
pub async fn on_read_endp_data(
|
|
&self,
|
|
@@ -2803,7 +2878,7 @@ impl<const N: usize> Xhci<N> {
|
|
pub fn event_handler_finished(&self) {
|
|
trace!("Event handler finished");
|
|
// write 1 to EHB to clear it
|
|
- self.run.lock().unwrap().ints[0]
|
|
+ self.run.lock().unwrap_or_else(|e| e.into_inner()).ints[0]
|
|
.erdp_low
|
|
.writef(1 << 3, true);
|
|
}
|