From 6d1983a3742f601ed7baf9993116ee9bf821c756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 10:59:44 +0800 Subject: [PATCH 1/8] Refactor USB host controller endpoint management and logging - Introduced a new structure to manage endpoint interfaces within the Device struct. - Enhanced endpoint setup logic to clear stale endpoints and manage interface-specific endpoints. - Implemented a method to find endpoint descriptors across all alternate settings. - Improved logging for transfer events, command completions, and port status changes with a budget mechanism to limit log output. - Updated control transfer handling to track and log control transfer states more effectively. - Modified the Device struct to maintain a map of claimed interfaces instead of a single current interface. - Adjusted endpoint retrieval methods to support multiple interfaces and their respective endpoints. - Enhanced error handling and logging for transfer events and reclaiming requests. --- usb-host/src/backend/kmod/queue.rs | 47 +- usb-host/src/backend/kmod/xhci/device.rs | 66 ++- usb-host/src/backend/kmod/xhci/endpoint.rs | 484 +++++++++++++++++++-- usb-host/src/backend/kmod/xhci/event.rs | 8 + usb-host/src/backend/kmod/xhci/host.rs | 98 ++++- usb-host/src/backend/kmod/xhci/ring.rs | 75 +++- usb-host/src/backend/kmod/xhci/transfer.rs | 30 ++ usb-host/src/backend/ty/ep/ctrl.rs | 5 +- usb-host/src/backend/ty/ep/mod.rs | 5 +- usb-host/src/device.rs | 74 +++- 10 files changed, 808 insertions(+), 84 deletions(-) diff --git a/usb-host/src/backend/kmod/queue.rs b/usb-host/src/backend/kmod/queue.rs index dcbfc764..5cfec1e2 100644 --- a/usb-host/src/backend/kmod/queue.rs +++ b/usb-host/src/backend/kmod/queue.rs @@ -4,7 +4,7 @@ use core::task::Context; use core::task::Poll; use core::{ cell::UnsafeCell, - sync::atomic::{AtomicBool, Ordering}, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; use futures::task::AtomicWaker; @@ -12,6 +12,16 @@ use alloc::collections::BTreeMap; use crate::BusAddr; +static FINISHED_QUEUE_LOG_BUDGET: AtomicUsize = AtomicUsize::new(128); + +fn take_queue_log_budget() -> bool { + FINISHED_QUEUE_LOG_BUDGET + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |left| { + left.checked_sub(1) + }) + .is_ok() +} + pub struct Finished { inner: Arc>, } @@ -89,6 +99,11 @@ impl Finished { } slot.finished.store(true, Ordering::Release); slot.waker.wake(); + } else if take_queue_log_budget() { + warn!( + "usb queue: completion address {:#x} is not registered", + addr.raw() + ); } } @@ -96,6 +111,17 @@ impl Finished { self.waiter(addr).get_finished() } + pub fn peek_finished(&self, addr: BusAddr) -> Option + where + C: Copy, + { + self.waiter(addr).peek_finished() + } + + pub fn has_finished(&self, addr: BusAddr) -> bool { + self.waiter(addr).has_finished() + } + fn waiter(&self, addr: BusAddr) -> &FinishedData { let data = unsafe { &mut *self.inner.data.get() }; let slot = data.get(&addr).unwrap(); @@ -134,7 +160,10 @@ impl Future for TWaiter { return Poll::Ready(res); } this.finished.register(cx.waker()); - Poll::Pending + match this.finished.get_finished() { + Some(res) => Poll::Ready(res), + None => Poll::Pending, + } } } @@ -149,4 +178,18 @@ impl FinishedData { } unsafe { (*self.data.get()).take() } } + + pub fn peek_finished(&self) -> Option + where + C: Copy, + { + if !self.finished.load(Ordering::Acquire) { + return None; + } + unsafe { *self.data.get() } + } + + pub fn has_finished(&self) -> bool { + self.finished.load(Ordering::Acquire) && unsafe { (*self.data.get()).is_some() } + } } diff --git a/usb-host/src/backend/kmod/xhci/device.rs b/usb-host/src/backend/kmod/xhci/device.rs index 760a364b..18731b20 100644 --- a/usb-host/src/backend/kmod/xhci/device.rs +++ b/usb-host/src/backend/kmod/xhci/device.rs @@ -50,6 +50,7 @@ pub struct Device { config_desc: Vec, port_speed: Speed, eps: BTreeMap, + ep_interfaces: BTreeMap, cmd: CommandRing, } @@ -81,6 +82,7 @@ impl Device { config_desc: vec![], port_speed: Speed::Full, eps: BTreeMap::new(), + ep_interfaces: BTreeMap::new(), cmd: host.cmd.clone(), }) } @@ -369,6 +371,8 @@ impl Device { .await?; self.current_config_value = Some(configuration_value); + self.eps.clear(); + self.ep_interfaces.clear(); self.ctx.with_input(|input| { let c = input.control_mut(); @@ -399,19 +403,37 @@ impl Device { &[], ) .await?; - self.setup_all_endpoints(interface, alternate).await?; + self.setup_interface_endpoints(interface, alternate).await?; debug!("Interface {interface} set successfully"); Ok(()) } - async fn setup_all_endpoints(&mut self, interface: u8, alternate: u8) -> Result { - let mut max_dci = 1; + async fn setup_interface_endpoints(&mut self, interface: u8, alternate: u8) -> Result { self.ctx.perper_change(); - self.eps.clear(); + let stale_endpoints = self + .ep_interfaces + .iter() + .filter_map(|(address, ep_interface)| (*ep_interface == interface).then_some(*address)) + .collect::>(); + let drop_dcis = stale_endpoints + .iter() + .filter_map(|address| { + self.find_ep_desc_any_alt(*address) + .ok() + .map(|desc| desc.dci()) + }) + .collect::>(); + let mut old_endpoints = Vec::with_capacity(stale_endpoints.len()); + for address in &stale_endpoints { + if let Some(endpoint) = self.eps.remove(address) { + old_endpoints.push(endpoint); + } + self.ep_interfaces.remove(address); + } self.ctx.with_input(|input| { let control_context = input.control_mut(); - for i in 2..32 { - control_context.set_drop_context_flag(i); + for dci in drop_dcis { + control_context.set_drop_context_flag(dci as _); } }); @@ -420,9 +442,6 @@ impl Device { .to_vec() { let dci = desc.dci(); - if dci > max_dci { - max_dci = dci; - } let mut ep_raw = self.new_ep(dci.into())?; let periodic_burst_size = match self.port_speed { Speed::High @@ -439,6 +458,7 @@ impl Device { let ring_addr = ep_raw.bus_addr(); self.eps .insert(desc.address, Endpoint::new((&desc).into(), ep_raw)); + self.ep_interfaces.insert(desc.address, interface); let xhci_interval = self.calculate_xhci_interval(desc.interval, desc.transfer_type, desc.interval); @@ -490,11 +510,21 @@ impl Device { }); } + let max_dci = self + .eps + .keys() + .filter_map(|address| { + self.find_ep_desc_any_alt(*address) + .ok() + .map(|desc| desc.dci()) + }) + .max() + .unwrap_or(1); self.ctx.with_input(|input| { input .device_mut() .slot_mut() - .set_context_entries(max_dci + 1); + .set_context_entries(max_dci + 1) }); mb(); @@ -506,6 +536,7 @@ impl Device { .set_input_context_pointer(self.ctx.input_bus_addr()), )) .await?; + drop(old_endpoints); Ok(()) } @@ -529,6 +560,21 @@ impl Device { Err(USBError::NotFound) } + fn find_ep_desc_any_alt(&self, address: u8) -> Result<&EndpointDescriptor> { + for config in &self.config_desc { + for iface in &config.interfaces { + for alt in &iface.alt_settings { + for desc in &alt.endpoints { + if desc.address == address { + return Ok(desc); + } + } + } + } + } + Err(USBError::NotFound) + } + /// 根据 XHCI 规范计算端点的 interval 值 /// 参考 xHCI 规范第 6.2.3.6 节 fn calculate_xhci_interval( diff --git a/usb-host/src/backend/kmod/xhci/endpoint.rs b/usb-host/src/backend/kmod/xhci/endpoint.rs index 43a97c16..71e4ea1b 100644 --- a/usb-host/src/backend/kmod/xhci/endpoint.rs +++ b/usb-host/src/backend/kmod/xhci/endpoint.rs @@ -1,4 +1,5 @@ use alloc::{collections::BTreeMap, sync::Arc, vec, vec::Vec}; +use core::sync::atomic::{AtomicUsize, Ordering}; use dma_api::DmaDirection; use mbarrier::mb; @@ -7,12 +8,12 @@ use usb_if::{ descriptor::{self, EndpointDescriptor}, endpoint::{RequestId, TransferCompletion, TransferRequest}, err::TransferError, - transfer::{BmRequestType, Direction}, + transfer::{BmRequestType, Direction, Recipient, Request, RequestType}, }; use xhci::{ registers::doorbell, ring::trb::{ - event::TransferEvent, + event::{CompletionCode, TransferEvent}, transfer::{self, Isoch, Normal}, }, }; @@ -31,12 +32,109 @@ use crate::{ osal::Kernel, }; +static XHCI_ENDPOINT_LOG_BUDGET: AtomicUsize = AtomicUsize::new(64); + +fn take_endpoint_log_budget() -> bool { + XHCI_ENDPOINT_LOG_BUDGET + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |left| { + left.checked_sub(1) + }) + .is_ok() +} + +#[derive(Clone, Copy, Debug)] +struct ControlTdState { + setup_trb: TransferId, + data_trb: Option, + status_trb: TransferId, + requested: usize, + actual: Option, + trace: ControlTdTrace, +} + +#[derive(Clone, Copy, Debug)] +struct ControlTdTrace { + request_type: u8, + request: u8, + value: u16, + index: u16, + length: usize, + direction: Direction, + traced: bool, +} + +impl ControlTdTrace { + fn new( + request_type: RequestType, + direction: Direction, + recipient: Recipient, + request: Request, + value: u16, + index: u16, + length: usize, + ) -> Self { + let request_type_raw = u8::from(BmRequestType::new(direction, request_type, recipient)); + let request_raw = u8::from(request); + Self { + request_type: request_type_raw, + request: request_raw, + value, + index, + length, + direction, + traced: matches!(request_type, RequestType::Class) + && matches!(recipient, Recipient::Interface | Recipient::Endpoint), + } + } + + fn is_uvc_stream_control(self) -> bool { + let selector = (self.value >> 8) as u8; + matches!(self.request_type, 0xa1 | 0x21) && matches!(selector, 0x01 | 0x02) + } + + fn direction_name(self) -> &'static str { + match self.direction { + Direction::In => "in", + Direction::Out => "out", + } + } + + fn should_log(self) -> bool { + self.traced || take_endpoint_log_budget() + } +} + +fn control_td_data_state( + ring: &SendRing, + control_td: ControlTdState, +) -> &'static str { + let Some(data_trb) = control_td.data_trb else { + return "none"; + }; + if control_td.actual.is_some() { + return "consumed"; + } + if ring.has_finished(data_trb.0) { + "queued" + } else { + "pending-or-not-short" + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ControlStage { + Setup, + Data, + Status, +} + pub struct Endpoint { dci: Dci, pub ring: SendRing, bell: Arc>, transfers: BTreeMap, cancelled: BTreeMap, + control_tds: BTreeMap, iso_packet_ids: BTreeMap>, trb_counts: BTreeMap, outstanding_trbs: usize, @@ -48,9 +146,12 @@ pub struct Endpoint { unsafe impl Send for Endpoint {} unsafe impl Sync for Endpoint {} +const ENDPOINT_RING_PAGES: usize = 16; + impl Endpoint { pub fn new(dci: Dci, kernel: &Kernel, bell: Arc>) -> crate::err::Result { - let ring = SendRing::new(DmaDirection::Bidirectional, kernel)?; + let ring = + SendRing::new_with_pages(ENDPOINT_RING_PAGES, DmaDirection::Bidirectional, kernel)?; Ok(Self { dci, @@ -58,6 +159,7 @@ impl Endpoint { bell, transfers: BTreeMap::new(), cancelled: BTreeMap::new(), + control_tds: BTreeMap::new(), iso_packet_ids: BTreeMap::new(), trb_counts: BTreeMap::new(), outstanding_trbs: 0, @@ -76,6 +178,15 @@ impl Endpoint { self.ring.bus_addr() } + fn iso_ready(&self, handle: TransferId) -> bool { + self.iso_packet_ids.get(&handle).is_none_or(|packet_ids| { + packet_ids + .iter() + .copied() + .all(|packet_id| self.ring.has_finished(packet_id.0)) + }) + } + fn doorbell(&mut self) { let mut bell = doorbell::Register::default(); bell.set_doorbell_target(self.dci.into()); @@ -92,24 +203,52 @@ impl Endpoint { handle: BusAddr, ) -> Result { let handle = TransferId(handle); + let control_td = self.control_tds.remove(&handle); + let iso_packet_ids = self.iso_packet_ids.remove(&handle); if let Some(count) = self.trb_counts.remove(&handle) { self.outstanding_trbs = self.outstanding_trbs.saturating_sub(count); } let mut t = self.transfers.remove(&handle).unwrap(); + let kind_name = match &t.kind { + TransferKind::Control(_) => "control", + TransferKind::Bulk => "bulk", + TransferKind::Interrupt => "interrupt", + TransferKind::Isochronous { .. } => "iso", + }; match c.completion_code() { - Ok(code) => match code.to_result() { - Ok(_) => Ok(()), - Err(e) => Err(e), - }, - Err(_e) => Err(TransferError::Other(anyhow!("Transfer failed"))), - }?; + Ok(code) => { + if let Err(e) = code.to_result() { + if take_endpoint_log_budget() { + warn!( + "xhci: transfer error dci={} kind={} handle={:#x} code={:?} remaining={}", + self.dci.raw(), + kind_name, + handle.0.raw(), + code, + c.trb_transfer_length() + ); + } + return Err(e); + } + } + Err(e) => { + if take_endpoint_log_budget() { + warn!( + "xhci: transfer error dci={} kind={} handle={:#x} unknown_code={} remaining={}", + self.dci.raw(), + kind_name, + handle.0.raw(), + e, + c.trb_transfer_length() + ); + } + return Err(TransferError::Other(anyhow!("Transfer failed"))); + } + } let transfer_len; if let TransferKind::Isochronous { packet_lengths } = &t.kind { - let packet_ids = self - .iso_packet_ids - .remove(&handle) - .unwrap_or_else(|| vec![handle]); + let packet_ids = iso_packet_ids.unwrap_or_else(|| vec![handle]); if packet_ids.len() != packet_lengths.len() { return Err(TransferError::Other(anyhow!( "ISO completion count mismatch: ids={}, packets={}", @@ -132,7 +271,11 @@ impl Endpoint { }; match event.completion_code() { Ok(code) => code.to_result()?, - Err(_e) => return Err(TransferError::Other(anyhow!("Transfer failed"))), + Err(e) => { + return Err(TransferError::Other(anyhow!( + "unknown XHCI ISO completion code: {e:?}" + ))); + } } let requested = packet_lengths[index]; @@ -150,8 +293,18 @@ impl Endpoint { return Ok(t); } - let remaining = c.trb_transfer_length() as usize; - transfer_len = t.buffer_len().saturating_sub(remaining); + transfer_len = match control_td { + Some(control_td) => control_td + .actual + .unwrap_or_else(|| match c.completion_code() { + Ok(code) if code.to_result().is_ok() => control_td.requested, + _ => 0, + }), + None => { + let remaining = c.trb_transfer_length() as usize; + t.buffer_len().saturating_sub(remaining) + } + }; if transfer_len > 0 && matches!(t.direction, Direction::In) { // 刷新/失效缓存,确保从 DMA 缓冲读取到有效数据 @@ -177,8 +330,9 @@ impl Endpoint { let id = self.enque_iso_trb( bus_addr, packet_lengths.first().copied().unwrap_or(0), - false, - true, + false, // chain + true, // ioc + true, // start_asap interrupt_on_short_packet, ); (id, vec![id]) @@ -193,13 +347,16 @@ impl Endpoint { buff_len: usize, chain: bool, ioc: bool, + start_asap: bool, interrupt_on_short_packet: bool, ) -> TransferId { let mut trb = Isoch::new(); trb.set_data_buffer_pointer(bus_addr as _) .set_trb_transfer_length(buff_len as _) - .set_interrupter_target(0) - .set_start_isoch_asap(); + .set_interrupter_target(0); + if start_asap { + trb.set_start_isoch_asap(); + } if interrupt_on_short_packet { trb.set_interrupt_on_short_packet(); } @@ -223,10 +380,11 @@ impl Endpoint { trb.set_interrupt_on_completion(); } - // 创建Isoch TRB let trb = transfer::Allowed::Isoch(trb); self.enque_trb(trb) } + + /// Enqueue each ISO packet as its own TD. fn enque_iso_multi( &mut self, bus_addr: u64, @@ -236,23 +394,20 @@ impl Endpoint { let mut ids = Vec::with_capacity(packet_lengths.len()); let mut offset = 0u64; - for packet_length in packet_lengths.iter().copied() { - let current_size = packet_length as u64; - let current_addr = bus_addr + offset; - + for &packet_length in packet_lengths { ids.push(self.enque_iso_trb( - current_addr, - current_size as _, - false, - true, + bus_addr + offset, + packet_length, + false, // chain + true, // ioc + true, // start_asap interrupt_on_short_packet, )); - - offset += current_size; + offset += packet_length as u64; } - let id = ids.last().copied().unwrap_or(TransferId(BusAddr(0))); - (id, ids) + let handle = ids.last().copied().unwrap_or(TransferId(BusAddr(0))); + (handle, ids) } fn required_trbs(transfer: &Transfer) -> usize { @@ -352,6 +507,15 @@ impl EndpointOp for Endpoint { request_type: t.request_type, recipient: t.recipient, }; + let control_trace = ControlTdTrace::new( + t.request_type, + transfer.direction, + t.recipient, + t.request, + t.value, + t.index, + data_len, + ); let mut setup = transfer::SetupStage::default(); setup @@ -374,6 +538,9 @@ impl EndpointOp for Endpoint { .set_data_buffer_pointer(data_bus_addr) .set_trb_transfer_length(data_len as _) .set_direction(transfer.direction.to_xhci_direction()); + if matches!(transfer.direction, Direction::In) { + _data.set_interrupt_on_short_packet(); + } data = Some(_data); } @@ -386,11 +553,67 @@ impl EndpointOp for Endpoint { status.set_direction(); } - self.ring.enque_transfer(setup.into()); + let has_data_stage = data.is_some(); + let mut control_trbs: Vec = vec![setup.into()]; if let Some(data) = data { - self.ring.enque_transfer(data.into()); + control_trbs.push(data.into()); + } + control_trbs.push(status.into()); + + let control_addrs = self.ring.enque_transfer_batch(&mut control_trbs); + let mut control_addrs = control_addrs.into_iter(); + let setup_trb = TransferId( + control_addrs + .next() + .expect("control TD must contain setup TRB"), + ); + let data_trb = if has_data_stage { + Some(TransferId( + control_addrs + .next() + .expect("control data TD must contain data TRB"), + )) + } else { + None + }; + handle = TransferId( + control_addrs + .next() + .expect("control TD must contain status TRB"), + ); + self.control_tds.insert( + handle, + ControlTdState { + setup_trb, + data_trb, + status_trb: handle, + requested: data_len, + actual: None, + trace: control_trace, + }, + ); + debug_assert!( + control_addrs.next().is_none(), + "control TD yielded more TRB addresses than expected" + ); + if control_trace.should_log() { + info!( + "xhci: submit control dci={} handle={:#x} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} setup_trb={:#x} data_trb={:#x?} status_trb={:#x} class_trace={} uvc_stream={}", + self.dci.raw(), + handle.0.raw(), + control_trace.direction_name(), + control_trace.request_type, + control_trace.request, + control_trace.value, + control_trace.index, + control_trace.length, + setup_trb.0.raw(), + data_trb.map(|id| id.0.raw()), + handle.0.raw(), + control_trace.traced, + control_trace.is_uvc_stream_control() + ); } - handle.0 = self.ring.enque_transfer(status.into()); } TransferKind::Interrupt | TransferKind::Bulk => { let trb = transfer::Allowed::Normal( @@ -413,16 +636,17 @@ impl EndpointOp for Endpoint { iso_packet_ids = ids.1; } } + let request_handle = handle; if !iso_packet_ids.is_empty() { self.iso_packet_ids.insert(handle, iso_packet_ids); } - self.trb_counts.insert(handle, required_trbs); + self.trb_counts.insert(request_handle, required_trbs); self.outstanding_trbs += required_trbs; - self.transfers.insert(handle, transfer); + self.transfers.insert(request_handle, transfer); mb(); self.doorbell(); - Ok(RequestId::new(handle.0.raw())) + Ok(RequestId::new(request_handle.0.raw())) } fn reclaim_request( @@ -430,7 +654,147 @@ impl EndpointOp for Endpoint { id: RequestId, ) -> Option> { let raw_id = BusAddr(id.raw()); - let c = self.ring.get_finished(raw_id)?; + let transfer_id = TransferId(raw_id); + let c = if let Some(control_td) = self.control_tds.get(&transfer_id).copied() { + let mut event_trbs = Vec::with_capacity(3); + event_trbs.push((control_td.setup_trb, ControlStage::Setup)); + if let Some(data_trb) = control_td.data_trb { + event_trbs.push((data_trb, ControlStage::Data)); + } + event_trbs.push((control_td.status_trb, ControlStage::Status)); + + let mut status_event = None; + for (event_trb, stage) in event_trbs { + if let Some(event) = self.ring.get_finished(event_trb.0) + && let Some(transfer) = self.transfers.get(&transfer_id) + { + let remaining = event.trb_transfer_length() as usize; + let actual = transfer.buffer_len().saturating_sub(remaining); + if matches!(stage, ControlStage::Setup) { + if let Some(control_td) = self.control_tds.get_mut(&transfer_id) { + control_td.actual = Some(0); + } + } else if matches!(stage, ControlStage::Data) { + if let Some(control_td) = self.control_tds.get_mut(&transfer_id) { + control_td.actual = Some(actual); + } + } + if control_td.trace.should_log() { + info!( + "xhci: reclaim control event dci={} handle={:#x} event_trb={:#x} stage={:?} code={:?} remaining={} actual={} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} uvc_stream={}", + self.dci.raw(), + raw_id.raw(), + event_trb.0.raw(), + stage, + event.completion_code(), + remaining, + actual, + control_td.trace.direction_name(), + control_td.trace.request_type, + control_td.trace.request, + control_td.trace.value, + control_td.trace.index, + control_td.trace.length, + control_td.trace.is_uvc_stream_control() + ); + } + match event.completion_code() { + Ok(CompletionCode::Success) + if !matches!(stage, ControlStage::Status) + && take_endpoint_log_budget() => + { + warn!( + "xhci: success on control {:?} stage without IOC dci={} status_trb={:#x} event_trb={:#x}", + stage, + self.dci.raw(), + raw_id.raw(), + event_trb.0.raw() + ); + } + Ok(code) if code.to_result().is_ok() => { + if matches!(stage, ControlStage::Status) { + status_event = Some(event); + break; + } + } + _ => { + if matches!(stage, ControlStage::Status) + && self + .control_tds + .get(&transfer_id) + .is_some_and(|control_td| control_td.actual.is_none()) + { + if let Some(control_td) = self.control_tds.get_mut(&transfer_id) { + control_td.actual = Some(0); + } + } + let cancelled = self.cancelled.remove(&transfer_id).is_some(); + let res = self + .handle_transfer_completion(event, raw_id) + .map(|transfer| transfer_to_completion(id, transfer)); + if cancelled { + return Some(Err(TransferError::Cancelled)); + } + return Some(res); + } + } + } + } + if let Some(event) = status_event { + event + } else { + if control_td.trace.should_log() { + info!( + "xhci: reclaim control pending dci={} handle={:#x} setup_done={} data_state={} status_done={} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} uvc_stream={}", + self.dci.raw(), + raw_id.raw(), + self.ring.has_finished(control_td.setup_trb.0), + control_td_data_state(&self.ring, control_td), + self.ring.has_finished(control_td.status_trb.0), + control_td.trace.direction_name(), + control_td.trace.request_type, + control_td.trace.request, + control_td.trace.value, + control_td.trace.index, + control_td.trace.length, + control_td.trace.is_uvc_stream_control() + ); + } + return None; + } + } else { + if let Some(packet_ids) = self.iso_packet_ids.get(&transfer_id) { + for packet_id in packet_ids { + if let Some(event) = self.ring.peek_finished(packet_id.0) { + match event.completion_code() { + Ok(code) if code.to_result().is_ok() => {} + _ => { + let cancelled = self.cancelled.remove(&transfer_id).is_some(); + let res = self + .handle_transfer_completion(event, raw_id) + .map(|transfer| transfer_to_completion(id, transfer)); + if cancelled { + return Some(Err(TransferError::Cancelled)); + } + return Some(res); + } + } + } + } + + if !self.iso_ready(transfer_id) { + if take_endpoint_log_budget() { + info!( + "xhci: reclaim iso pending dci={} handle={:#x} waiting for packet completions", + self.dci.raw(), + raw_id.raw() + ); + } + return None; + } + } + self.ring.get_finished(raw_id)? + }; let cancelled = self.cancelled.remove(&TransferId(raw_id)).is_some(); let res = self .handle_transfer_completion(c, raw_id) @@ -442,7 +806,45 @@ impl EndpointOp for Endpoint { } fn register_waker(&self, id: RequestId, cx: &mut core::task::Context<'_>) { - self.ring.register_cx(BusAddr(id.raw()), cx); + let raw_id = BusAddr(id.raw()); + if let Some(control_td) = self.control_tds.get(&TransferId(raw_id)) { + let mut event_trbs = Vec::with_capacity(3); + event_trbs.push(control_td.setup_trb); + if let Some(data_trb) = control_td.data_trb { + event_trbs.push(data_trb); + } + event_trbs.push(control_td.status_trb); + if control_td.trace.should_log() { + info!( + "xhci: register control waker dci={} handle={:#x} events={:?} setup_done={} data_state={} status_done={} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} uvc_stream={}", + self.dci.raw(), + raw_id.raw(), + event_trbs + .iter() + .map(|event_trb| event_trb.0.raw()) + .collect::>(), + self.ring.has_finished(control_td.setup_trb.0), + control_td_data_state(&self.ring, *control_td), + self.ring.has_finished(control_td.status_trb.0), + control_td.trace.direction_name(), + control_td.trace.request_type, + control_td.trace.request, + control_td.trace.value, + control_td.trace.index, + control_td.trace.length, + control_td.trace.is_uvc_stream_control() + ); + } + for event_trb in event_trbs { + self.ring.register_cx(event_trb.0, cx); + } + } else if let Some(packet_ids) = self.iso_packet_ids.get(&TransferId(raw_id)) { + for packet_id in packet_ids { + self.ring.register_cx(packet_id.0, cx); + } + } else { + self.ring.register_cx(raw_id, cx); + } } fn cancel_request(&mut self, id: RequestId) -> Result<(), TransferError> { diff --git a/usb-host/src/backend/kmod/xhci/event.rs b/usb-host/src/backend/kmod/xhci/event.rs index af1640e5..f12aa83a 100644 --- a/usb-host/src/backend/kmod/xhci/event.rs +++ b/usb-host/src/backend/kmod/xhci/event.rs @@ -56,6 +56,14 @@ impl EventRing { Some(allowed) } + pub fn has_pending_event(&mut self) -> bool { + let (data, flag) = self.ring.current_data(); + let Ok(allowed) = Allowed::try_from(data.to_raw()) else { + return false; + }; + flag == allowed.cycle_bit() + } + pub fn erdp(&self) -> u64 { self.ring.current_trb_addr().raw() & 0xFFFF_FFFF_FFFF_FFF0 } diff --git a/usb-host/src/backend/kmod/xhci/host.rs b/usb-host/src/backend/kmod/xhci/host.rs index 3f288867..ea961ae5 100644 --- a/usb-host/src/backend/kmod/xhci/host.rs +++ b/usb-host/src/backend/kmod/xhci/host.rs @@ -1,5 +1,9 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use core::{cell::UnsafeCell, time::Duration}; +use core::{ + cell::UnsafeCell, + sync::atomic::{AtomicUsize, Ordering}, + time::Duration, +}; use ::xhci::{ ExtendedCapability, @@ -33,6 +37,16 @@ use crate::{ queue::Finished, }; +static XHCI_EVENT_LOG_BUDGET: AtomicUsize = AtomicUsize::new(96); + +fn take_event_log_budget() -> bool { + XHCI_EVENT_LOG_BUDGET + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |left| { + left.checked_sub(1) + }) + .is_ok() +} + pub struct Xhci { pub(crate) reg: Arc>, pub(crate) kernel: Kernel, @@ -555,18 +569,35 @@ impl EventHandler { fn clean_event_ring(&self) -> Event { use xhci::ring::trb::event::Allowed; let mut event = Event::Nothing; + let mut command_events = 0usize; + let mut port_events = 0usize; + let mut transfer_events = 0usize; + let mut other_events = 0usize; while let Some(allowed) = self.event_ring().next() { match allowed { Allowed::CommandCompletion(c) => { + command_events += 1; let addr = c.command_trb_pointer(); // trace!("[Command] << {allowed:?} @{addr:X}"); + if take_event_log_budget() { + info!( + "xhci: event command ptr={:#x} slot={} code={:?}", + addr, + c.slot_id(), + c.completion_code() + ); + } self.cmd_finished.set_finished(addr.into(), c); } Allowed::PortStatusChange(st) => { + port_events += 1; // debug!("Port {} status change event", st.port_id()); // let idx = (st.port_id() - 1) as usize; let port_id = st.port_id(); + if take_event_log_budget() { + info!("xhci: event port status change port={}", port_id); + } self.ports.set_port_changed(port_id); event = Event::PortChange { @@ -574,9 +605,21 @@ impl EventHandler { }; } Allowed::TransferEvent(c) => { + transfer_events += 1; let slot_id = c.slot_id(); let ep_id = c.endpoint_id(); let ptr = c.trb_pointer(); + if take_event_log_budget() { + info!( + "xhci: event transfer slot={} ep={} ptr={:#x} code={:?} len={} event_data={}", + slot_id, + ep_id, + ptr, + c.completion_code(), + c.trb_transfer_length(), + c.event_data() + ); + } // Interrupts synchronize queue state only. Do not call // into OS glue or take manager/file/device locks here; the @@ -587,10 +630,26 @@ impl EventHandler { }; } _ => { + other_events += 1; + if take_event_log_budget() { + info!("xhci: event other {:?}", allowed); + } // debug!("unhandled event {allowed:?}"); } } } + if (command_events | port_events | transfer_events | other_events) != 0 + && take_event_log_budget() + { + info!( + "xhci: event ring drained command={} port={} transfer={} other={} erdp={:#x}", + command_events, + port_events, + transfer_events, + other_events, + self.event_ring().erdp() + ); + } event } } @@ -599,14 +658,43 @@ impl EventHandlerOp for EventHandler { fn handle_event(&self) -> Event { let mut res = Event::Nothing; let sts = self.reg().operational.usbsts.read_volatile(); + let has_event_interrupt = sts.event_interrupt(); + let has_pending_event = self.event_ring().has_pending_event(); - if !sts.event_interrupt() { + if !has_event_interrupt && !has_pending_event { return res; } - self.reg().operational.usbsts.update_volatile(|r| { - r.clear_event_interrupt(); - }); + if take_event_log_budget() { + let irq = self.reg().interrupter_register_set.interrupter_mut(0); + let iman = irq.iman.read_volatile(); + let erdp = irq.erdp.read_volatile(); + if has_event_interrupt { + info!( + "xhci: handle_event USBSTS.EINT=1 IMAN.IP={} IMAN.IE={} EHB={} ERDP={:#x} sw_erdp={:#x}", + iman.interrupt_pending(), + iman.interrupt_enable(), + erdp.event_handler_busy(), + erdp.event_ring_dequeue_pointer(), + self.event_ring().erdp() + ); + } else { + info!( + "xhci: handle_event draining pending event with USBSTS.EINT=0 IMAN.IP={} IMAN.IE={} EHB={} ERDP={:#x} sw_erdp={:#x}", + iman.interrupt_pending(), + iman.interrupt_enable(), + erdp.event_handler_busy(), + erdp.event_ring_dequeue_pointer(), + self.event_ring().erdp() + ); + } + } + + if has_event_interrupt { + self.reg().operational.usbsts.update_volatile(|r| { + r.clear_event_interrupt(); + }); + } // 【关键】GIC 中断模式下,需要手动清除 IMAN.IP // 参考: Linux xhci_irq() in xhci-ring.c:3054-3059 diff --git a/usb-host/src/backend/kmod/xhci/ring.rs b/usb-host/src/backend/kmod/xhci/ring.rs index 272326a2..78faf958 100644 --- a/usb-host/src/backend/kmod/xhci/ring.rs +++ b/usb-host/src/backend/kmod/xhci/ring.rs @@ -1,4 +1,7 @@ +use alloc::vec::Vec; + use dma_api::{DArray, DmaDirection}; +use mbarrier::mb; use xhci::ring::trb::{Link, command, transfer}; use crate::{ @@ -64,7 +67,16 @@ impl Ring { } pub fn new(link: bool, direction: DmaDirection, dma: &Kernel) -> Result { - let len = (dma.page_size() * DEFAULT_RING_PAGES) / TRB_SIZE; + Self::new_with_pages(DEFAULT_RING_PAGES, link, direction, dma) + } + + pub fn new_with_pages( + pages: usize, + link: bool, + direction: DmaDirection, + dma: &Kernel, + ) -> Result { + let len = (dma.page_size() * pages.max(1)) / TRB_SIZE; Ok(Self::new_with_len(len, link, direction, dma)?) } @@ -102,6 +114,28 @@ impl Ring { addr } + fn enque_transfer_deferred_cycle( + &mut self, + mut trb: transfer::Allowed, + ) -> (BusAddr, usize, transfer::Allowed) { + let mut visible_trb = trb; + if self.cycle { + visible_trb.set_cycle_bit(); + trb.clear_cycle_bit(); + } else { + visible_trb.clear_cycle_bit(); + trb.set_cycle_bit(); + } + let index = self.i; + let addr = self.enque_trb(trb.into()); + trace!("[Transfer] >> deferred first {visible_trb:X?} @{addr:X?}"); + (addr, index, visible_trb) + } + + fn set_transfer_trb(&mut self, index: usize, trb: transfer::Allowed) { + self.trbs.set(index, trb.into()); + } + pub fn enque_trb(&mut self, trb: TrbData) -> BusAddr { self.trbs.set(self.i, trb); let addr = self.trb_bus_addr(self.i); @@ -178,6 +212,12 @@ impl SendRing { Ok(Self { ring, finished }) } + pub fn new_with_pages(pages: usize, direction: DmaDirection, dma: &Kernel) -> Result { + let ring = Ring::new_with_pages(pages, true, direction, dma)?; + let finished = Finished::new(ring.trb_bus_addr_list()); + Ok(Self { ring, finished }) + } + pub fn enque_command(&mut self, trb: command::Allowed) -> BusAddr { let addr = self.ring.enque_command(trb); self.finished.clear_finished(addr); @@ -190,6 +230,28 @@ impl SendRing { addr } + pub fn enque_transfer_batch(&mut self, trbs: &mut [transfer::Allowed]) -> Vec { + let mut addrs = Vec::with_capacity(trbs.len()); + let Some((first, rest)) = trbs.split_first_mut() else { + return addrs; + }; + + let (first_addr, first_index, visible_first) = + self.ring.enque_transfer_deferred_cycle(*first); + self.finished.clear_finished(first_addr); + addrs.push(first_addr); + + for trb in rest { + let addr = self.ring.enque_transfer(*trb); + self.finished.clear_finished(addr); + addrs.push(addr); + } + + mb(); + self.ring.set_transfer_trb(first_index, visible_first); + addrs + } + pub fn take_finished_future(&self, addr: BusAddr) -> TWaiter { self.finished.take_waiter(addr) } @@ -202,6 +264,17 @@ impl SendRing { self.finished.get_finished(addr) } + pub fn peek_finished(&self, addr: BusAddr) -> Option + where + R: Copy, + { + self.finished.peek_finished(addr) + } + + pub fn has_finished(&self, addr: BusAddr) -> bool { + self.finished.has_finished(addr) + } + pub fn register_cx(&self, addr: BusAddr, cx: &mut core::task::Context<'_>) { self.finished.register_cx(addr, cx); } diff --git a/usb-host/src/backend/kmod/xhci/transfer.rs b/usb-host/src/backend/kmod/xhci/transfer.rs index 9f1a61c9..c80644e9 100644 --- a/usb-host/src/backend/kmod/xhci/transfer.rs +++ b/usb-host/src/backend/kmod/xhci/transfer.rs @@ -1,10 +1,21 @@ use alloc::{collections::BTreeMap, sync::Arc}; +use core::sync::atomic::{AtomicUsize, Ordering}; use xhci::ring::trb::event::TransferEvent; use crate::{BusAddr, queue::Finished}; use super::{reg::XhciRegistersShared, ring::SendRing, sync::IrqLock}; +static XHCI_TRANSFER_LOG_BUDGET: AtomicUsize = AtomicUsize::new(256); + +fn take_transfer_log_budget() -> bool { + XHCI_TRANSFER_LOG_BUDGET + .fetch_update(Ordering::AcqRel, Ordering::Acquire, |left| { + left.checked_sub(1) + }) + .is_ok() +} + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TransferId(pub(crate) BusAddr); @@ -44,7 +55,26 @@ impl TransferResultHandler { pub unsafe fn set_finished(&self, slot_id: u8, ep_id: u8, ptr: BusAddr, res: TransferEvent) { let queue_id = TransQueueId { slot_id, ep_id }; if let Some(q) = unsafe { self.inner.force_use().get(&queue_id) } { + if ep_id == 1 || take_transfer_log_budget() { + info!( + "xhci: dispatch transfer event slot={} ep={} ptr={:#x} code={:?} len={}", + slot_id, + ep_id, + ptr.raw(), + res.completion_code(), + res.trb_transfer_length() + ); + } q.set_finished(ptr, res); + } else if ep_id == 1 || take_transfer_log_budget() { + warn!( + "xhci: transfer event has no endpoint queue slot={} ep={} ptr={:#x} code={:?} len={}", + slot_id, + ep_id, + ptr.raw(), + res.completion_code(), + res.trb_transfer_length() + ); } } } diff --git a/usb-host/src/backend/ty/ep/ctrl.rs b/usb-host/src/backend/ty/ep/ctrl.rs index af91ba9f..fba51065 100644 --- a/usb-host/src/backend/ty/ep/ctrl.rs +++ b/usb-host/src/backend/ty/ep/ctrl.rs @@ -49,7 +49,7 @@ impl Endpoint { desc_index: u8, language_id: u16, buff: &mut [u8], - ) -> Result<(), TransferError> { + ) -> Result { self.control_in( ControlSetup { request_type: RequestType::Standard, @@ -60,8 +60,7 @@ impl Endpoint { }, buff, ) - .await?; - Ok(()) + .await } pub async fn get_device_descriptor(&mut self) -> Result { diff --git a/usb-host/src/backend/ty/ep/mod.rs b/usb-host/src/backend/ty/ep/mod.rs index f6848653..a1769058 100644 --- a/usb-host/src/backend/ty/ep/mod.rs +++ b/usb-host/src/backend/ty/ep/mod.rs @@ -72,7 +72,10 @@ impl Endpoint { Some(res) => Poll::Ready(res), None => { self.raw.register_waker(id, cx); - Poll::Pending + match self.raw.reclaim_request(id) { + Some(res) => Poll::Ready(res), + None => Poll::Pending, + } } } } diff --git a/usb-host/src/device.rs b/usb-host/src/device.rs index 47b913fc..74eaf9ea 100644 --- a/usb-host/src/device.rs +++ b/usb-host/src/device.rs @@ -1,5 +1,4 @@ use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec}; -use anyhow::anyhow; use core::{ any::Any, fmt::{Debug, Display}, @@ -194,7 +193,7 @@ pub struct Device { pub(crate) inner: Box, lang_id: LanguageId, manufacturer: Option, - current_interface: Option<(u8, u8)>, + claimed_interfaces: BTreeMap, } impl Debug for Device { @@ -211,7 +210,7 @@ impl From for Device { fn from(inner: T) -> Self { Self { inner: Box::new(inner), - current_interface: None, + claimed_interfaces: BTreeMap::new(), lang_id: LanguageId::default(), manufacturer: None, } @@ -222,7 +221,7 @@ impl From> for Device { fn from(inner: Box) -> Self { Self { inner, - current_interface: None, + claimed_interfaces: BTreeMap::new(), lang_id: LanguageId::default(), manufacturer: None, } @@ -250,7 +249,7 @@ impl Device { pub async fn claim_interface(&mut self, interface: u8, alternate: u8) -> Result<(), USBError> { trace!("Claiming interface {interface}, alternate {alternate}"); self.inner.claim_interface(interface, alternate).await?; - self.current_interface = Some((interface, alternate)); + self.claimed_interfaces.insert(interface, alternate); Ok(()) } @@ -269,7 +268,7 @@ impl Device { pub async fn set_configuration(&mut self, configuration_value: u8) -> crate::err::Result { let result = self.inner.set_configuration(configuration_value).await; if result.is_ok() { - self.current_interface = None; + self.claimed_interfaces.clear(); } result } @@ -298,11 +297,18 @@ impl Device { pub async fn string_descriptor(&mut self, index: u8) -> Result { let mut data = alloc::vec![0u8; 256]; let lang_id = self.lang_id(); - self.ctrl_ep_mut() + let len = self + .ctrl_ep_mut() .get_descriptor(DescriptorType::STRING, index, lang_id.into(), &mut data) .await?; - let res = decode_string_descriptor(&data)?; - Ok(res) + let descriptor_len = data + .first() + .copied() + .map(usize::from) + .unwrap_or(0) + .min(len) + .min(data.len()); + decode_string_descriptor(&data[..descriptor_len]).map_err(USBError::from) } pub async fn control_in( @@ -351,8 +357,11 @@ impl Device { self.inner.endpoint(&ep_desc) } - pub fn take_endpoints(&mut self) -> Result, USBError> { - let descriptors = self.current_endpoint_descriptors()?; + pub fn take_endpoints_for_interface( + &mut self, + interface: u8, + ) -> Result, USBError> { + let descriptors = self.current_endpoint_descriptors(interface)?; let mut endpoints = BTreeMap::new(); for desc in descriptors { let address = desc.address; @@ -361,6 +370,15 @@ impl Device { Ok(endpoints) } + pub fn take_endpoints(&mut self) -> Result, USBError> { + let mut endpoints = BTreeMap::new(); + let interfaces = self.claimed_interfaces.keys().copied().collect::>(); + for interface in interfaces { + endpoints.extend(self.take_endpoints_for_interface(interface)?); + } + Ok(endpoints) + } + #[allow(unused)] pub(crate) fn as_raw(&self) -> &T { (self.inner.as_ref() as &dyn Any) @@ -379,30 +397,44 @@ impl Device { &self, address: u8, ) -> core::result::Result<&usb_if::descriptor::EndpointDescriptor, USBError> { - self.current_endpoint_descriptors_ref()? - .iter() - .find(|ep| ep.address == address) - .ok_or(USBError::NotFound) + for interface in self.claimed_interfaces.keys().copied() { + if let Ok(desc) = + self.current_endpoint_descriptors_ref(interface) + .and_then(|descriptors| { + descriptors + .iter() + .find(|ep| ep.address == address) + .ok_or(USBError::NotFound) + }) + { + return Ok(desc); + } + } + Err(USBError::NotFound) } fn current_endpoint_descriptors( &self, + interface_number: u8, ) -> core::result::Result, USBError> { - Ok(self.current_endpoint_descriptors_ref()?.to_vec()) + Ok(self + .current_endpoint_descriptors_ref(interface_number)? + .to_vec()) } fn current_endpoint_descriptors_ref( &self, + interface_number: u8, ) -> core::result::Result<&[usb_if::descriptor::EndpointDescriptor], USBError> { - let (interface_number, alternate_setting) = match self.current_interface { - Some((i, a)) => (i, a), - None => Err(anyhow!("Interface not claim"))?, - }; + let alternate_setting = self + .claimed_interfaces + .get(&interface_number) + .ok_or(USBError::NotFound)?; for config in self.configurations() { for interface in &config.interfaces { if interface.interface_number == interface_number { for alt in &interface.alt_settings { - if alt.alternate_setting == alternate_setting { + if alt.alternate_setting == *alternate_setting { return Ok(&alt.endpoints); } } From a5ca2120340686171e79c0b82c9e6d6fd179c625 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 12:13:30 +0800 Subject: [PATCH 2/8] refactor: change logging level from info to debug for USB event handling - Updated logging statements in device and endpoint management to use debug level instead of info for better performance and reduced log verbosity. - Removed unused atomic log budget checks in host and transfer modules to streamline code. --- usb-host/src/backend/kmod/xhci/device.rs | 8 +- usb-host/src/backend/kmod/xhci/endpoint.rs | 690 ++++++++------------- usb-host/src/backend/kmod/xhci/host.rs | 88 +-- usb-host/src/backend/kmod/xhci/ring.rs | 2 +- usb-host/src/backend/kmod/xhci/transfer.rs | 31 +- 5 files changed, 317 insertions(+), 502 deletions(-) diff --git a/usb-host/src/backend/kmod/xhci/device.rs b/usb-host/src/backend/kmod/xhci/device.rs index 18731b20..79a30823 100644 --- a/usb-host/src/backend/kmod/xhci/device.rs +++ b/usb-host/src/backend/kmod/xhci/device.rs @@ -590,7 +590,7 @@ impl Device { // HighSpeed, SuperSpeed, SuperSpeedPlus ISO 端点 // Interval = max(1, min(16, bInterval)) let interval = binterval.clamp(1, 16); - info!( + debug!( "ISO endpoint HS/SS: bInterval={} -> XHCI interval={}", binterval, interval ); @@ -605,7 +605,7 @@ impl Device { // 计算 floor(log2(bInterval)) let log2_binterval = 31 - (binterval as u32).leading_zeros() as u8 - 1; let interval = (log2_binterval + 3).clamp(1, 16); - info!( + debug!( "ISO endpoint FS/LS: bInterval={} -> log2={} -> XHCI interval={}", binterval, log2_binterval, interval ); @@ -620,7 +620,7 @@ impl Device { // HighSpeed, SuperSpeed, SuperSpeedPlus 中断端点 // Interval = max(1, min(16, bInterval)) let interval = binterval.clamp(1, 16); - info!( + debug!( "INT endpoint HS/SS: bInterval={} -> XHCI interval={}", binterval, interval ); @@ -635,7 +635,7 @@ impl Device { // 计算 floor(log2(bInterval)) let log2_binterval = 31 - (binterval as u32).leading_zeros() as u8 - 1; let interval = (log2_binterval + 3).clamp(1, 16); - info!( + debug!( "INT endpoint FS/LS: bInterval={} -> log2={} -> XHCI interval={}", binterval, log2_binterval, interval ); diff --git a/usb-host/src/backend/kmod/xhci/endpoint.rs b/usb-host/src/backend/kmod/xhci/endpoint.rs index 71e4ea1b..abcc4201 100644 --- a/usb-host/src/backend/kmod/xhci/endpoint.rs +++ b/usb-host/src/backend/kmod/xhci/endpoint.rs @@ -1,5 +1,4 @@ use alloc::{collections::BTreeMap, sync::Arc, vec, vec::Vec}; -use core::sync::atomic::{AtomicUsize, Ordering}; use dma_api::DmaDirection; use mbarrier::mb; @@ -8,12 +7,12 @@ use usb_if::{ descriptor::{self, EndpointDescriptor}, endpoint::{RequestId, TransferCompletion, TransferRequest}, err::TransferError, - transfer::{BmRequestType, Direction, Recipient, Request, RequestType}, + transfer::{BmRequestType, Direction}, }; use xhci::{ registers::doorbell, ring::trb::{ - event::{CompletionCode, TransferEvent}, + event::TransferEvent, transfer::{self, Isoch, Normal}, }, }; @@ -32,111 +31,64 @@ use crate::{ osal::Kernel, }; -static XHCI_ENDPOINT_LOG_BUDGET: AtomicUsize = AtomicUsize::new(64); +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct EndpointRequestId(u64); -fn take_endpoint_log_budget() -> bool { - XHCI_ENDPOINT_LOG_BUDGET - .fetch_update(Ordering::AcqRel, Ordering::Acquire, |left| { - left.checked_sub(1) - }) - .is_ok() +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ControlStage { + Setup, + Data, + Status, } #[derive(Clone, Copy, Debug)] -struct ControlTdState { +struct ControlTd { setup_trb: TransferId, data_trb: Option, status_trb: TransferId, requested: usize, actual: Option, - trace: ControlTdTrace, } -#[derive(Clone, Copy, Debug)] -struct ControlTdTrace { - request_type: u8, - request: u8, - value: u16, - index: u16, - length: usize, - direction: Direction, - traced: bool, -} - -impl ControlTdTrace { - fn new( - request_type: RequestType, - direction: Direction, - recipient: Recipient, - request: Request, - value: u16, - index: u16, - length: usize, - ) -> Self { - let request_type_raw = u8::from(BmRequestType::new(direction, request_type, recipient)); - let request_raw = u8::from(request); - Self { - request_type: request_type_raw, - request: request_raw, - value, - index, - length, - direction, - traced: matches!(request_type, RequestType::Class) - && matches!(recipient, Recipient::Interface | Recipient::Endpoint), +impl ControlTd { + fn trbs(self) -> impl Iterator { + let mut trbs = Vec::with_capacity(3); + trbs.push((self.setup_trb, ControlStage::Setup)); + if let Some(data_trb) = self.data_trb { + trbs.push((data_trb, ControlStage::Data)); } + trbs.push((self.status_trb, ControlStage::Status)); + trbs.into_iter() } - fn is_uvc_stream_control(self) -> bool { - let selector = (self.value >> 8) as u8; - matches!(self.request_type, 0xa1 | 0x21) && matches!(selector, 0x01 | 0x02) - } - - fn direction_name(self) -> &'static str { - match self.direction { - Direction::In => "in", - Direction::Out => "out", + fn register_waker(&self, ring: &SendRing, cx: &mut core::task::Context<'_>) { + for (trb, _) in (*self).trbs() { + ring.register_cx(trb.0, cx); } } - - fn should_log(self) -> bool { - self.traced || take_endpoint_log_budget() - } } -fn control_td_data_state( - ring: &SendRing, - control_td: ControlTdState, -) -> &'static str { - let Some(data_trb) = control_td.data_trb else { - return "none"; - }; - if control_td.actual.is_some() { - return "consumed"; - } - if ring.has_finished(data_trb.0) { - "queued" - } else { - "pending-or-not-short" - } +struct SubmittedTd { + transfer: Transfer, + kind: SubmittedTdKind, + trb_count: usize, + cancelled: bool, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum ControlStage { - Setup, - Data, - Status, +#[derive(Clone)] +enum SubmittedTdKind { + Normal { completion_trb: TransferId }, + Control(ControlTd), + Iso { packet_trbs: Vec }, } pub struct Endpoint { dci: Dci, pub ring: SendRing, bell: Arc>, - transfers: BTreeMap, - cancelled: BTreeMap, - control_tds: BTreeMap, - iso_packet_ids: BTreeMap>, - trb_counts: BTreeMap, + next_request_id: u64, + inflight: BTreeMap, + trb_to_request: BTreeMap, outstanding_trbs: usize, kernel: Kernel, max_packet_size: usize, @@ -157,11 +109,9 @@ impl Endpoint { dci, ring, bell, - transfers: BTreeMap::new(), - cancelled: BTreeMap::new(), - control_tds: BTreeMap::new(), - iso_packet_ids: BTreeMap::new(), - trb_counts: BTreeMap::new(), + next_request_id: 1, + inflight: BTreeMap::new(), + trb_to_request: BTreeMap::new(), outstanding_trbs: 0, kernel: kernel.clone(), max_packet_size: 0, @@ -178,15 +128,6 @@ impl Endpoint { self.ring.bus_addr() } - fn iso_ready(&self, handle: TransferId) -> bool { - self.iso_packet_ids.get(&handle).is_none_or(|packet_ids| { - packet_ids - .iter() - .copied() - .all(|packet_id| self.ring.has_finished(packet_id.0)) - }) - } - fn doorbell(&mut self) { let mut bell = doorbell::Register::default(); bell.set_doorbell_target(self.dci.into()); @@ -197,79 +138,125 @@ impl Endpoint { &self.ring } - fn handle_transfer_completion( - &mut self, - c: TransferEvent, - handle: BusAddr, - ) -> Result { - let handle = TransferId(handle); - let control_td = self.control_tds.remove(&handle); - let iso_packet_ids = self.iso_packet_ids.remove(&handle); - if let Some(count) = self.trb_counts.remove(&handle) { - self.outstanding_trbs = self.outstanding_trbs.saturating_sub(count); - } - let mut t = self.transfers.remove(&handle).unwrap(); - let kind_name = match &t.kind { + fn allocate_request_id(&mut self) -> EndpointRequestId { + let id = EndpointRequestId(self.next_request_id); + self.next_request_id = self.next_request_id.wrapping_add(1).max(1); + id + } + + fn public_request_id(id: EndpointRequestId) -> RequestId { + RequestId::new(id.0) + } + + fn private_request_id(id: RequestId) -> EndpointRequestId { + EndpointRequestId(id.raw()) + } + + fn validate_completion_code( + &self, + event: TransferEvent, + transfer: &Transfer, + ) -> Result<(), TransferError> { + let kind_name = match &transfer.kind { TransferKind::Control(_) => "control", TransferKind::Bulk => "bulk", TransferKind::Interrupt => "interrupt", TransferKind::Isochronous { .. } => "iso", }; - match c.completion_code() { + match event.completion_code() { Ok(code) => { if let Err(e) = code.to_result() { - if take_endpoint_log_budget() { - warn!( - "xhci: transfer error dci={} kind={} handle={:#x} code={:?} remaining={}", - self.dci.raw(), - kind_name, - handle.0.raw(), - code, - c.trb_transfer_length() - ); - } - return Err(e); - } - } - Err(e) => { - if take_endpoint_log_budget() { warn!( - "xhci: transfer error dci={} kind={} handle={:#x} unknown_code={} remaining={}", + "xhci: transfer error dci={} kind={} code={:?} remaining={}", self.dci.raw(), kind_name, - handle.0.raw(), - e, - c.trb_transfer_length() + code, + event.trb_transfer_length() ); + return Err(e); } + } + Err(e) => { + warn!( + "xhci: transfer error dci={} kind={} unknown_code={} remaining={}", + self.dci.raw(), + kind_name, + e, + event.trb_transfer_length() + ); return Err(TransferError::Other(anyhow!("Transfer failed"))); } } + Ok(()) + } - let transfer_len; - if let TransferKind::Isochronous { packet_lengths } = &t.kind { - let packet_ids = iso_packet_ids.unwrap_or_else(|| vec![handle]); - if packet_ids.len() != packet_lengths.len() { + fn complete_request( + &mut self, + request_id: EndpointRequestId, + event_trb: TransferId, + event: TransferEvent, + ) -> Result { + let submitted = self + .inflight + .remove(&request_id) + .expect("request disappeared"); + self.outstanding_trbs = self.outstanding_trbs.saturating_sub(submitted.trb_count); + match &submitted.kind { + SubmittedTdKind::Normal { completion_trb } => { + self.trb_to_request.remove(completion_trb); + } + SubmittedTdKind::Control(control_td) => { + for (trb, _) in (*control_td).trbs() { + self.trb_to_request.remove(&trb); + } + } + SubmittedTdKind::Iso { packet_trbs } => { + for packet_trb in packet_trbs { + self.trb_to_request.remove(packet_trb); + } + } + } + + if submitted.cancelled { + return Err(TransferError::Cancelled); + } + + self.validate_completion_code(event, &submitted.transfer)?; + self.transfer_from_completion(submitted, event_trb, event) + } + + fn transfer_from_completion( + &mut self, + submitted: SubmittedTd, + event_trb: TransferId, + event: TransferEvent, + ) -> Result { + let mut transfer = submitted.transfer; + if let SubmittedTdKind::Iso { packet_trbs } = &submitted.kind { + let TransferKind::Isochronous { packet_lengths } = &transfer.kind else { + return Err(TransferError::Other(anyhow!("non-ISO transfer has ISO TD"))); + }; + if packet_trbs.len() != packet_lengths.len() { return Err(TransferError::Other(anyhow!( "ISO completion count mismatch: ids={}, packets={}", - packet_ids.len(), + packet_trbs.len(), packet_lengths.len() ))); } - let mut actual_lengths = Vec::with_capacity(packet_ids.len()); - for (index, packet_id) in packet_ids.iter().copied().enumerate() { - let event = if packet_id == handle { - c + let mut actual_lengths = Vec::with_capacity(packet_trbs.len()); + for (index, packet_trb) in packet_trbs.iter().copied().enumerate() { + let packet_event = if packet_trb == event_trb { + event } else { - self.ring.get_finished(packet_id.0).ok_or_else(|| { + self.ring.get_finished(packet_trb.0).ok_or_else(|| { TransferError::Other(anyhow!( "missing ISO packet completion for {:?}", - packet_id + packet_trb )) })? }; - match event.completion_code() { + match packet_event.completion_code() { Ok(code) => code.to_result()?, Err(e) => { return Err(TransferError::Other(anyhow!( @@ -279,41 +266,37 @@ impl Endpoint { } let requested = packet_lengths[index]; - let remaining = event.trb_transfer_length() as usize; + let remaining = packet_event.trb_transfer_length() as usize; actual_lengths.push(requested.saturating_sub(remaining)); } - transfer_len = actual_lengths.iter().sum(); - t.iso_packet_actual_lengths = actual_lengths; - if transfer_len > 0 && matches!(t.direction, Direction::In) { - t.prepare_read_all(); + let transfer_len = actual_lengths.iter().sum(); + transfer.iso_packet_actual_lengths = actual_lengths; + if transfer_len > 0 && matches!(transfer.direction, Direction::In) { + transfer.prepare_read_all(); } - t.transfer_len = transfer_len; - trace!("ISO transfer data length: {}", t.transfer_len); - return Ok(t); + transfer.transfer_len = transfer_len; + trace!("ISO transfer data length: {}", transfer.transfer_len); + return Ok(transfer); } - transfer_len = match control_td { - Some(control_td) => control_td - .actual - .unwrap_or_else(|| match c.completion_code() { - Ok(code) if code.to_result().is_ok() => control_td.requested, - _ => 0, - }), - None => { - let remaining = c.trb_transfer_length() as usize; - t.buffer_len().saturating_sub(remaining) + let transfer_len = match submitted.kind { + SubmittedTdKind::Control(control_td) => { + control_td.actual.unwrap_or(control_td.requested) + } + SubmittedTdKind::Normal { .. } => { + let remaining = event.trb_transfer_length() as usize; + transfer.buffer_len().saturating_sub(remaining) } + SubmittedTdKind::Iso { .. } => unreachable!("ISO was handled above"), }; - if transfer_len > 0 && matches!(t.direction, Direction::In) { - // 刷新/失效缓存,确保从 DMA 缓冲读取到有效数据 - // t.dma_slice().prepare_read_all(); - t.prepare_read_all(); + if transfer_len > 0 && matches!(transfer.direction, Direction::In) { + transfer.prepare_read_all(); } - t.transfer_len = transfer_len; - trace!("Transfer data length: {}", t.transfer_len); - Ok(t) + transfer.transfer_len = transfer_len; + trace!("Transfer data length: {}", transfer.transfer_len); + Ok(transfer) } fn enque_trb(&mut self, trb: transfer::Allowed) -> TransferId { @@ -456,66 +439,32 @@ impl EndpointOp for Endpoint { let mut data_bus_addr = 0; if transfer.buffer_len() > 0 { - // let data_slice = transfer.dma_slice(); if matches!(transfer.direction, Direction::Out) { - // data_slice.confirm_write_all(); transfer.confirm_write_all(); } - // data_bus_addr = data_slice.bus_addr(); data_bus_addr = transfer.dma_addr(); - - // 检查缓冲区起始地址是否在 dma_mask 范围内 - assert!( - data_bus_addr <= self.kernel.dma_mask(), - "DMA address 0x{:x} exceeds controller DMA mask 0x{:x} ({}-bit addressing)", - data_bus_addr, - self.kernel.dma_mask(), - if self.kernel.dma_mask() == u32::MAX as u64 { - 32 - } else { - 64 - } - ); - - // 检查缓冲区结束地址是否在 dma_mask 范围内 let buffer_end = data_bus_addr + transfer.buffer_len() as u64; - assert!( - buffer_end <= self.kernel.dma_mask(), - "DMA buffer end 0x{:x} (start: 0x{:x}, len: {} bytes) exceeds controller DMA mask 0x{:x} ({}-bit addressing)", - buffer_end, - data_bus_addr, - transfer.buffer_len(), - self.kernel.dma_mask(), - if self.kernel.dma_mask() == u32::MAX as u64 { - 32 - } else { - 64 - } - ); + if data_bus_addr > self.kernel.dma_mask() || buffer_end > self.kernel.dma_mask() { + return Err(TransferError::Other(anyhow!( + "DMA buffer [{:#x}, {:#x}) exceeds controller DMA mask {:#x}", + data_bus_addr, + buffer_end, + self.kernel.dma_mask() + ))); + } } let data_len = transfer.buffer_len(); let dir = transfer.direction; + let request_id = self.allocate_request_id(); - let mut handle = TransferId(BusAddr(0)); - let mut iso_packet_ids = Vec::new(); - - match &transfer.kind { + let kind = match &transfer.kind { TransferKind::Control(t) => { let bm_request_type = BmRequestType { direction: transfer.direction, request_type: t.request_type, recipient: t.recipient, }; - let control_trace = ControlTdTrace::new( - t.request_type, - transfer.direction, - t.recipient, - t.request, - t.value, - t.index, - data_len, - ); let mut setup = transfer::SetupStage::default(); setup @@ -560,7 +509,7 @@ impl EndpointOp for Endpoint { } control_trbs.push(status.into()); - let control_addrs = self.ring.enque_transfer_batch(&mut control_trbs); + let control_addrs = self.ring.enqueue_transfer_td(&mut control_trbs); let mut control_addrs = control_addrs.into_iter(); let setup_trb = TransferId( control_addrs @@ -576,44 +525,28 @@ impl EndpointOp for Endpoint { } else { None }; - handle = TransferId( + let status_trb = TransferId( control_addrs .next() .expect("control TD must contain status TRB"), ); - self.control_tds.insert( - handle, - ControlTdState { - setup_trb, - data_trb, - status_trb: handle, - requested: data_len, - actual: None, - trace: control_trace, - }, - ); debug_assert!( control_addrs.next().is_none(), "control TD yielded more TRB addresses than expected" ); - if control_trace.should_log() { - info!( - "xhci: submit control dci={} handle={:#x} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} setup_trb={:#x} data_trb={:#x?} status_trb={:#x} class_trace={} uvc_stream={}", - self.dci.raw(), - handle.0.raw(), - control_trace.direction_name(), - control_trace.request_type, - control_trace.request, - control_trace.value, - control_trace.index, - control_trace.length, - setup_trb.0.raw(), - data_trb.map(|id| id.0.raw()), - handle.0.raw(), - control_trace.traced, - control_trace.is_uvc_stream_control() - ); + for trb in [Some(setup_trb), data_trb, Some(status_trb)] + .into_iter() + .flatten() + { + self.trb_to_request.insert(trb, request_id); } + SubmittedTdKind::Control(ControlTd { + setup_trb, + data_trb, + status_trb, + requested: data_len, + actual: None, + }) } TransferKind::Interrupt | TransferKind::Bulk => { let trb = transfer::Allowed::Normal( @@ -624,7 +557,9 @@ impl EndpointOp for Endpoint { .set_interrupt_on_short_packet() .set_interrupt_on_completion(), ); - handle.0 = self.ring.enque_transfer(trb); + let completion_trb = TransferId(self.ring.enque_transfer(trb)); + self.trb_to_request.insert(completion_trb, request_id); + SubmittedTdKind::Normal { completion_trb } } TransferKind::Isochronous { packet_lengths } => { let ids = self.enque_iso( @@ -632,227 +567,150 @@ impl EndpointOp for Endpoint { packet_lengths, matches!(transfer.direction, Direction::In), ); - handle = ids.0; - iso_packet_ids = ids.1; + let packet_trbs = ids.1; + for packet_trb in &packet_trbs { + self.trb_to_request.insert(*packet_trb, request_id); + } + SubmittedTdKind::Iso { packet_trbs } } - } - let request_handle = handle; - if !iso_packet_ids.is_empty() { - self.iso_packet_ids.insert(handle, iso_packet_ids); - } - self.trb_counts.insert(request_handle, required_trbs); + }; + self.outstanding_trbs += required_trbs; - self.transfers.insert(request_handle, transfer); + self.inflight.insert( + request_id, + SubmittedTd { + transfer, + kind, + trb_count: required_trbs, + cancelled: false, + }, + ); mb(); self.doorbell(); - Ok(RequestId::new(request_handle.0.raw())) + Ok(Self::public_request_id(request_id)) } fn reclaim_request( &mut self, id: RequestId, ) -> Option> { - let raw_id = BusAddr(id.raw()); - let transfer_id = TransferId(raw_id); - let c = if let Some(control_td) = self.control_tds.get(&transfer_id).copied() { - let mut event_trbs = Vec::with_capacity(3); - event_trbs.push((control_td.setup_trb, ControlStage::Setup)); - if let Some(data_trb) = control_td.data_trb { - event_trbs.push((data_trb, ControlStage::Data)); + let request_id = Self::private_request_id(id); + let kind = self.inflight.get(&request_id)?.kind.clone(); + match kind { + SubmittedTdKind::Normal { completion_trb } => { + let event = self.ring.get_finished(completion_trb.0)?; + Some( + self.complete_request(request_id, completion_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ) } - event_trbs.push((control_td.status_trb, ControlStage::Status)); - - let mut status_event = None; - for (event_trb, stage) in event_trbs { - if let Some(event) = self.ring.get_finished(event_trb.0) - && let Some(transfer) = self.transfers.get(&transfer_id) - { + SubmittedTdKind::Control(control_td) => { + for (event_trb, stage) in control_td.trbs() { + let Some(event) = self.ring.get_finished(event_trb.0) else { + continue; + }; let remaining = event.trb_transfer_length() as usize; - let actual = transfer.buffer_len().saturating_sub(remaining); - if matches!(stage, ControlStage::Setup) { - if let Some(control_td) = self.control_tds.get_mut(&transfer_id) { - control_td.actual = Some(0); - } - } else if matches!(stage, ControlStage::Data) { - if let Some(control_td) = self.control_tds.get_mut(&transfer_id) { - control_td.actual = Some(actual); + if let Some(submitted) = self.inflight.get_mut(&request_id) + && let SubmittedTdKind::Control(control_td) = &mut submitted.kind + { + match stage { + ControlStage::Setup => {} + ControlStage::Data => { + control_td.actual = + Some(submitted.transfer.buffer_len().saturating_sub(remaining)); + } + ControlStage::Status if control_td.actual.is_none() => { + control_td.actual = Some(control_td.requested); + } + ControlStage::Status => {} } } - if control_td.trace.should_log() { - info!( - "xhci: reclaim control event dci={} handle={:#x} event_trb={:#x} stage={:?} code={:?} remaining={} actual={} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} uvc_stream={}", - self.dci.raw(), - raw_id.raw(), - event_trb.0.raw(), - stage, - event.completion_code(), - remaining, - actual, - control_td.trace.direction_name(), - control_td.trace.request_type, - control_td.trace.request, - control_td.trace.value, - control_td.trace.index, - control_td.trace.length, - control_td.trace.is_uvc_stream_control() - ); - } match event.completion_code() { - Ok(CompletionCode::Success) - if !matches!(stage, ControlStage::Status) - && take_endpoint_log_budget() => - { - warn!( - "xhci: success on control {:?} stage without IOC dci={} status_trb={:#x} event_trb={:#x}", - stage, - self.dci.raw(), - raw_id.raw(), - event_trb.0.raw() - ); - } Ok(code) if code.to_result().is_ok() => { if matches!(stage, ControlStage::Status) { - status_event = Some(event); - break; + return Some( + self.complete_request(request_id, event_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ); } } _ => { - if matches!(stage, ControlStage::Status) - && self - .control_tds - .get(&transfer_id) - .is_some_and(|control_td| control_td.actual.is_none()) - { - if let Some(control_td) = self.control_tds.get_mut(&transfer_id) { - control_td.actual = Some(0); - } - } - let cancelled = self.cancelled.remove(&transfer_id).is_some(); - let res = self - .handle_transfer_completion(event, raw_id) - .map(|transfer| transfer_to_completion(id, transfer)); - if cancelled { - return Some(Err(TransferError::Cancelled)); - } - return Some(res); + return Some( + self.complete_request(request_id, event_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ); } } } + None } - if let Some(event) = status_event { - event - } else { - if control_td.trace.should_log() { - info!( - "xhci: reclaim control pending dci={} handle={:#x} setup_done={} data_state={} status_done={} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} uvc_stream={}", - self.dci.raw(), - raw_id.raw(), - self.ring.has_finished(control_td.setup_trb.0), - control_td_data_state(&self.ring, control_td), - self.ring.has_finished(control_td.status_trb.0), - control_td.trace.direction_name(), - control_td.trace.request_type, - control_td.trace.request, - control_td.trace.value, - control_td.trace.index, - control_td.trace.length, - control_td.trace.is_uvc_stream_control() - ); - } - return None; - } - } else { - if let Some(packet_ids) = self.iso_packet_ids.get(&transfer_id) { - for packet_id in packet_ids { - if let Some(event) = self.ring.peek_finished(packet_id.0) { + SubmittedTdKind::Iso { packet_trbs } => { + for packet_trb in &packet_trbs { + if let Some(event) = self.ring.peek_finished(packet_trb.0) { match event.completion_code() { Ok(code) if code.to_result().is_ok() => {} _ => { - let cancelled = self.cancelled.remove(&transfer_id).is_some(); - let res = self - .handle_transfer_completion(event, raw_id) - .map(|transfer| transfer_to_completion(id, transfer)); - if cancelled { - return Some(Err(TransferError::Cancelled)); - } - return Some(res); + let event = self + .ring + .get_finished(packet_trb.0) + .expect("peeked ISO event disappeared"); + return Some( + self.complete_request(request_id, *packet_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ); } } } } - if !self.iso_ready(transfer_id) { - if take_endpoint_log_budget() { - info!( - "xhci: reclaim iso pending dci={} handle={:#x} waiting for packet completions", - self.dci.raw(), - raw_id.raw() - ); - } - return None; + if packet_trbs + .iter() + .all(|packet_trb| self.ring.has_finished(packet_trb.0)) + { + let completion_trb = *packet_trbs.last().expect("ISO TD has no packet TRBs"); + let event = self + .ring + .get_finished(completion_trb.0) + .expect("ready ISO completion disappeared"); + Some( + self.complete_request(request_id, completion_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ) + } else { + None } } - self.ring.get_finished(raw_id)? - }; - let cancelled = self.cancelled.remove(&TransferId(raw_id)).is_some(); - let res = self - .handle_transfer_completion(c, raw_id) - .map(|transfer| transfer_to_completion(id, transfer)); - if cancelled { - return Some(Err(TransferError::Cancelled)); } - Some(res) } fn register_waker(&self, id: RequestId, cx: &mut core::task::Context<'_>) { - let raw_id = BusAddr(id.raw()); - if let Some(control_td) = self.control_tds.get(&TransferId(raw_id)) { - let mut event_trbs = Vec::with_capacity(3); - event_trbs.push(control_td.setup_trb); - if let Some(data_trb) = control_td.data_trb { - event_trbs.push(data_trb); - } - event_trbs.push(control_td.status_trb); - if control_td.trace.should_log() { - info!( - "xhci: register control waker dci={} handle={:#x} events={:?} setup_done={} data_state={} status_done={} dir={} req_type={:#04x} req={:#04x} value={:#06x} index={:#06x} len={} uvc_stream={}", - self.dci.raw(), - raw_id.raw(), - event_trbs - .iter() - .map(|event_trb| event_trb.0.raw()) - .collect::>(), - self.ring.has_finished(control_td.setup_trb.0), - control_td_data_state(&self.ring, *control_td), - self.ring.has_finished(control_td.status_trb.0), - control_td.trace.direction_name(), - control_td.trace.request_type, - control_td.trace.request, - control_td.trace.value, - control_td.trace.index, - control_td.trace.length, - control_td.trace.is_uvc_stream_control() - ); + let request_id = Self::private_request_id(id); + let Some(submitted) = self.inflight.get(&request_id) else { + return; + }; + match &submitted.kind { + SubmittedTdKind::Normal { completion_trb } => { + self.ring.register_cx(completion_trb.0, cx); } - for event_trb in event_trbs { - self.ring.register_cx(event_trb.0, cx); + SubmittedTdKind::Control(control_td) => { + control_td.register_waker(&self.ring, cx); } - } else if let Some(packet_ids) = self.iso_packet_ids.get(&TransferId(raw_id)) { - for packet_id in packet_ids { - self.ring.register_cx(packet_id.0, cx); + SubmittedTdKind::Iso { packet_trbs } => { + for packet_trb in packet_trbs { + self.ring.register_cx(packet_trb.0, cx); + } } - } else { - self.ring.register_cx(raw_id, cx); } } fn cancel_request(&mut self, id: RequestId) -> Result<(), TransferError> { - let transfer_id = TransferId(BusAddr(id.raw())); - if !self.transfers.contains_key(&transfer_id) { - return Err(TransferError::InvalidEndpoint); - } - self.cancelled.insert(transfer_id, ()); + let request_id = Self::private_request_id(id); + let submitted = self + .inflight + .get_mut(&request_id) + .ok_or(TransferError::InvalidEndpoint)?; + submitted.cancelled = true; Ok(()) } } diff --git a/usb-host/src/backend/kmod/xhci/host.rs b/usb-host/src/backend/kmod/xhci/host.rs index ea961ae5..94882cf7 100644 --- a/usb-host/src/backend/kmod/xhci/host.rs +++ b/usb-host/src/backend/kmod/xhci/host.rs @@ -1,9 +1,5 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use core::{ - cell::UnsafeCell, - sync::atomic::{AtomicUsize, Ordering}, - time::Duration, -}; +use core::{cell::UnsafeCell, time::Duration}; use ::xhci::{ ExtendedCapability, @@ -37,16 +33,6 @@ use crate::{ queue::Finished, }; -static XHCI_EVENT_LOG_BUDGET: AtomicUsize = AtomicUsize::new(96); - -fn take_event_log_budget() -> bool { - XHCI_EVENT_LOG_BUDGET - .fetch_update(Ordering::AcqRel, Ordering::Acquire, |left| { - left.checked_sub(1) - }) - .is_ok() -} - pub struct Xhci { pub(crate) reg: Arc>, pub(crate) kernel: Kernel, @@ -579,25 +565,18 @@ impl EventHandler { Allowed::CommandCompletion(c) => { command_events += 1; let addr = c.command_trb_pointer(); - // trace!("[Command] << {allowed:?} @{addr:X}"); - if take_event_log_budget() { - info!( - "xhci: event command ptr={:#x} slot={} code={:?}", - addr, - c.slot_id(), - c.completion_code() - ); - } + trace!( + "xhci: event command ptr={:#x} slot={} code={:?}", + addr, + c.slot_id(), + c.completion_code() + ); self.cmd_finished.set_finished(addr.into(), c); } Allowed::PortStatusChange(st) => { port_events += 1; - // debug!("Port {} status change event", st.port_id()); - // let idx = (st.port_id() - 1) as usize; let port_id = st.port_id(); - if take_event_log_budget() { - info!("xhci: event port status change port={}", port_id); - } + trace!("xhci: event port status change port={}", port_id); self.ports.set_port_changed(port_id); event = Event::PortChange { @@ -609,17 +588,15 @@ impl EventHandler { let slot_id = c.slot_id(); let ep_id = c.endpoint_id(); let ptr = c.trb_pointer(); - if take_event_log_budget() { - info!( - "xhci: event transfer slot={} ep={} ptr={:#x} code={:?} len={} event_data={}", - slot_id, - ep_id, - ptr, - c.completion_code(), - c.trb_transfer_length(), - c.event_data() - ); - } + trace!( + "xhci: event transfer slot={} ep={} ptr={:#x} code={:?} len={} event_data={}", + slot_id, + ep_id, + ptr, + c.completion_code(), + c.trb_transfer_length(), + c.event_data() + ); // Interrupts synchronize queue state only. Do not call // into OS glue or take manager/file/device locks here; the @@ -631,25 +608,18 @@ impl EventHandler { } _ => { other_events += 1; - if take_event_log_budget() { - info!("xhci: event other {:?}", allowed); - } - // debug!("unhandled event {allowed:?}"); + trace!("xhci: event other {:?}", allowed); } } } - if (command_events | port_events | transfer_events | other_events) != 0 - && take_event_log_budget() - { - info!( - "xhci: event ring drained command={} port={} transfer={} other={} erdp={:#x}", - command_events, - port_events, - transfer_events, - other_events, - self.event_ring().erdp() - ); - } + trace!( + "xhci: event ring drained command={} port={} transfer={} other={} erdp={:#x}", + command_events, + port_events, + transfer_events, + other_events, + self.event_ring().erdp() + ); event } } @@ -665,12 +635,12 @@ impl EventHandlerOp for EventHandler { return res; } - if take_event_log_budget() { + { let irq = self.reg().interrupter_register_set.interrupter_mut(0); let iman = irq.iman.read_volatile(); let erdp = irq.erdp.read_volatile(); if has_event_interrupt { - info!( + trace!( "xhci: handle_event USBSTS.EINT=1 IMAN.IP={} IMAN.IE={} EHB={} ERDP={:#x} sw_erdp={:#x}", iman.interrupt_pending(), iman.interrupt_enable(), @@ -679,7 +649,7 @@ impl EventHandlerOp for EventHandler { self.event_ring().erdp() ); } else { - info!( + trace!( "xhci: handle_event draining pending event with USBSTS.EINT=0 IMAN.IP={} IMAN.IE={} EHB={} ERDP={:#x} sw_erdp={:#x}", iman.interrupt_pending(), iman.interrupt_enable(), diff --git a/usb-host/src/backend/kmod/xhci/ring.rs b/usb-host/src/backend/kmod/xhci/ring.rs index 78faf958..bf3cb7cb 100644 --- a/usb-host/src/backend/kmod/xhci/ring.rs +++ b/usb-host/src/backend/kmod/xhci/ring.rs @@ -230,7 +230,7 @@ impl SendRing { addr } - pub fn enque_transfer_batch(&mut self, trbs: &mut [transfer::Allowed]) -> Vec { + pub fn enqueue_transfer_td(&mut self, trbs: &mut [transfer::Allowed]) -> Vec { let mut addrs = Vec::with_capacity(trbs.len()); let Some((first, rest)) = trbs.split_first_mut() else { return addrs; diff --git a/usb-host/src/backend/kmod/xhci/transfer.rs b/usb-host/src/backend/kmod/xhci/transfer.rs index c80644e9..7ea39698 100644 --- a/usb-host/src/backend/kmod/xhci/transfer.rs +++ b/usb-host/src/backend/kmod/xhci/transfer.rs @@ -1,21 +1,10 @@ use alloc::{collections::BTreeMap, sync::Arc}; -use core::sync::atomic::{AtomicUsize, Ordering}; use xhci::ring::trb::event::TransferEvent; use crate::{BusAddr, queue::Finished}; use super::{reg::XhciRegistersShared, ring::SendRing, sync::IrqLock}; -static XHCI_TRANSFER_LOG_BUDGET: AtomicUsize = AtomicUsize::new(256); - -fn take_transfer_log_budget() -> bool { - XHCI_TRANSFER_LOG_BUDGET - .fetch_update(Ordering::AcqRel, Ordering::Acquire, |left| { - left.checked_sub(1) - }) - .is_ok() -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TransferId(pub(crate) BusAddr); @@ -55,18 +44,16 @@ impl TransferResultHandler { pub unsafe fn set_finished(&self, slot_id: u8, ep_id: u8, ptr: BusAddr, res: TransferEvent) { let queue_id = TransQueueId { slot_id, ep_id }; if let Some(q) = unsafe { self.inner.force_use().get(&queue_id) } { - if ep_id == 1 || take_transfer_log_budget() { - info!( - "xhci: dispatch transfer event slot={} ep={} ptr={:#x} code={:?} len={}", - slot_id, - ep_id, - ptr.raw(), - res.completion_code(), - res.trb_transfer_length() - ); - } + trace!( + "xhci: dispatch transfer event slot={} ep={} ptr={:#x} code={:?} len={}", + slot_id, + ep_id, + ptr.raw(), + res.completion_code(), + res.trb_transfer_length() + ); q.set_finished(ptr, res); - } else if ep_id == 1 || take_transfer_log_budget() { + } else { warn!( "xhci: transfer event has no endpoint queue slot={} ep={} ptr={:#x} code={:?} len={}", slot_id, From 32f88b946bd1c025eb101dba81a446fc980b4dc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 14:39:05 +0800 Subject: [PATCH 3/8] refactor: streamline endpoint and event ring handling, enhance ISO transfer management --- usb-host/src/backend/kmod/queue.rs | 25 -- usb-host/src/backend/kmod/xhci/device.rs | 6 +- usb-host/src/backend/kmod/xhci/endpoint.rs | 319 +++++++++++++-------- usb-host/src/backend/kmod/xhci/event.rs | 96 +++++-- usb-host/src/backend/kmod/xhci/host.rs | 53 ++-- usb-host/src/backend/kmod/xhci/ring.rs | 39 +-- usb-host/src/backend/umod/endpoint.rs | 2 +- 7 files changed, 325 insertions(+), 215 deletions(-) diff --git a/usb-host/src/backend/kmod/queue.rs b/usb-host/src/backend/kmod/queue.rs index 5cfec1e2..6461ed50 100644 --- a/usb-host/src/backend/kmod/queue.rs +++ b/usb-host/src/backend/kmod/queue.rs @@ -111,17 +111,6 @@ impl Finished { self.waiter(addr).get_finished() } - pub fn peek_finished(&self, addr: BusAddr) -> Option - where - C: Copy, - { - self.waiter(addr).peek_finished() - } - - pub fn has_finished(&self, addr: BusAddr) -> bool { - self.waiter(addr).has_finished() - } - fn waiter(&self, addr: BusAddr) -> &FinishedData { let data = unsafe { &mut *self.inner.data.get() }; let slot = data.get(&addr).unwrap(); @@ -178,18 +167,4 @@ impl FinishedData { } unsafe { (*self.data.get()).take() } } - - pub fn peek_finished(&self) -> Option - where - C: Copy, - { - if !self.finished.load(Ordering::Acquire) { - return None; - } - unsafe { *self.data.get() } - } - - pub fn has_finished(&self) -> bool { - self.finished.load(Ordering::Acquire) && unsafe { (*self.data.get()).is_some() } - } } diff --git a/usb-host/src/backend/kmod/xhci/device.rs b/usb-host/src/backend/kmod/xhci/device.rs index 79a30823..0cc20b67 100644 --- a/usb-host/src/backend/kmod/xhci/device.rs +++ b/usb-host/src/backend/kmod/xhci/device.rs @@ -454,7 +454,11 @@ impl Device { } _ => 0, }; - ep_raw.configure_periodic(desc.max_packet_size as usize, periodic_burst_size); + ep_raw.configure_periodic( + desc.max_packet_size as usize, + periodic_burst_size, + desc.interval, + ); let ring_addr = ep_raw.bus_addr(); self.eps .insert(desc.address, Endpoint::new((&desc).into(), ep_raw)); diff --git a/usb-host/src/backend/kmod/xhci/endpoint.rs b/usb-host/src/backend/kmod/xhci/endpoint.rs index abcc4201..8e0453e9 100644 --- a/usb-host/src/backend/kmod/xhci/endpoint.rs +++ b/usb-host/src/backend/kmod/xhci/endpoint.rs @@ -79,7 +79,14 @@ struct SubmittedTd { enum SubmittedTdKind { Normal { completion_trb: TransferId }, Control(ControlTd), - Iso { packet_trbs: Vec }, + Iso { packets: Vec }, +} + +#[derive(Clone, Copy)] +struct IsoPacketTd { + trb: TransferId, + event: Option, + actual: Option, } pub struct Endpoint { @@ -93,6 +100,9 @@ pub struct Endpoint { kernel: Kernel, max_packet_size: usize, max_burst_size: usize, + interval: u8, + iso_start_asap: bool, + next_iso_frame_id: u16, } unsafe impl Send for Endpoint {} @@ -116,12 +126,22 @@ impl Endpoint { kernel: kernel.clone(), max_packet_size: 0, max_burst_size: 0, + interval: 1, + iso_start_asap: true, + next_iso_frame_id: 0, }) } - pub fn configure_periodic(&mut self, max_packet_size: usize, max_burst_size: usize) { + pub fn configure_periodic( + &mut self, + max_packet_size: usize, + max_burst_size: usize, + interval: u8, + ) { self.max_packet_size = max_packet_size; self.max_burst_size = max_burst_size; + self.interval = interval.max(1); + self.iso_start_asap = true; } pub fn bus_addr(&self) -> BusAddr { @@ -210,9 +230,9 @@ impl Endpoint { self.trb_to_request.remove(&trb); } } - SubmittedTdKind::Iso { packet_trbs } => { - for packet_trb in packet_trbs { - self.trb_to_request.remove(packet_trb); + SubmittedTdKind::Iso { packets } => { + for packet in packets { + self.trb_to_request.remove(&packet.trb); } } } @@ -221,7 +241,9 @@ impl Endpoint { return Err(TransferError::Cancelled); } - self.validate_completion_code(event, &submitted.transfer)?; + if !matches!(submitted.kind, SubmittedTdKind::Iso { .. }) { + self.validate_completion_code(event, &submitted.transfer)?; + } self.transfer_from_completion(submitted, event_trb, event) } @@ -232,42 +254,27 @@ impl Endpoint { event: TransferEvent, ) -> Result { let mut transfer = submitted.transfer; - if let SubmittedTdKind::Iso { packet_trbs } = &submitted.kind { + if let SubmittedTdKind::Iso { packets } = &submitted.kind { let TransferKind::Isochronous { packet_lengths } = &transfer.kind else { return Err(TransferError::Other(anyhow!("non-ISO transfer has ISO TD"))); }; - if packet_trbs.len() != packet_lengths.len() { + if packets.len() != packet_lengths.len() { return Err(TransferError::Other(anyhow!( "ISO completion count mismatch: ids={}, packets={}", - packet_trbs.len(), + packets.len(), packet_lengths.len() ))); } - let mut actual_lengths = Vec::with_capacity(packet_trbs.len()); - for (index, packet_trb) in packet_trbs.iter().copied().enumerate() { - let packet_event = if packet_trb == event_trb { - event - } else { - self.ring.get_finished(packet_trb.0).ok_or_else(|| { - TransferError::Other(anyhow!( - "missing ISO packet completion for {:?}", - packet_trb - )) - })? - }; - match packet_event.completion_code() { - Ok(code) => code.to_result()?, - Err(e) => { - return Err(TransferError::Other(anyhow!( - "unknown XHCI ISO completion code: {e:?}" - ))); - } - } - + let mut actual_lengths = Vec::with_capacity(packets.len()); + for (index, packet) in packets.iter().copied().enumerate() { let requested = packet_lengths[index]; - let remaining = packet_event.trb_transfer_length() as usize; - actual_lengths.push(requested.saturating_sub(remaining)); + let actual = match packet.actual { + Some(actual) => actual, + None if packet.trb == event_trb => iso_packet_actual_length(requested, event)?, + None => 0, + }; + actual_lengths.push(actual); } let transfer_len = actual_lengths.iter().sum(); @@ -308,37 +315,55 @@ impl Endpoint { bus_addr: u64, packet_lengths: &[usize], interrupt_on_short_packet: bool, - ) -> (TransferId, Vec) { - if packet_lengths.len() <= 1 { - let id = self.enque_iso_trb( - bus_addr, - packet_lengths.first().copied().unwrap_or(0), - false, // chain - true, // ioc - true, // start_asap + ) -> Vec { + let mut packets = Vec::with_capacity(packet_lengths.len().max(1)); + let mut offset = 0u64; + let packet_count = packet_lengths.len().max(1); + let interval = self.interval.max(1); + let mut frame_id = self.next_iso_frame_id; + + for index in 0..packet_count { + let packet_length = packet_lengths.get(index).copied().unwrap_or(0); + let last_packet = index + 1 == packet_count; + let trb = self.enque_iso_trb( + bus_addr + offset, + packet_length, + last_packet, + !last_packet, + frame_id, interrupt_on_short_packet, ); - (id, vec![id]) - } else { - self.enque_iso_multi(bus_addr, packet_lengths, interrupt_on_short_packet) + packets.push(IsoPacketTd { + trb, + event: None, + actual: None, + }); + offset += packet_length as u64; + frame_id = frame_id.wrapping_add(interval as u16) & 0x7ff; } + + self.next_iso_frame_id = frame_id; + packets } fn enque_iso_trb( &mut self, bus_addr: u64, buff_len: usize, - chain: bool, - ioc: bool, - start_asap: bool, + last_packet: bool, + block_event_interrupt: bool, + frame_id: u16, interrupt_on_short_packet: bool, ) -> TransferId { let mut trb = Isoch::new(); trb.set_data_buffer_pointer(bus_addr as _) .set_trb_transfer_length(buff_len as _) .set_interrupter_target(0); - if start_asap { + + if self.use_iso_sia() { trb.set_start_isoch_asap(); + } else { + trb.set_frame_id(frame_id & 0x7ff); } if interrupt_on_short_packet { trb.set_interrupt_on_short_packet(); @@ -356,41 +381,17 @@ impl Endpoint { }; trb.set_td_size_or_tbc(burst_count.min(0x1f) as u8) .set_transfer_last_burst_packet_count(last_burst_packet_count.min(0xf) as u8); - if chain { - trb.set_chain_bit(); - } - if ioc { - trb.set_interrupt_on_completion(); + trb.set_interrupt_on_completion(); + if block_event_interrupt && !last_packet { + trb.set_block_event_interrupt(); } let trb = transfer::Allowed::Isoch(trb); self.enque_trb(trb) } - /// Enqueue each ISO packet as its own TD. - fn enque_iso_multi( - &mut self, - bus_addr: u64, - packet_lengths: &[usize], - interrupt_on_short_packet: bool, - ) -> (TransferId, Vec) { - let mut ids = Vec::with_capacity(packet_lengths.len()); - let mut offset = 0u64; - - for &packet_length in packet_lengths { - ids.push(self.enque_iso_trb( - bus_addr + offset, - packet_length, - false, // chain - true, // ioc - true, // start_asap - interrupt_on_short_packet, - )); - offset += packet_length as u64; - } - - let handle = ids.last().copied().unwrap_or(TransferId(BusAddr(0))); - (handle, ids) + fn use_iso_sia(&self) -> bool { + self.iso_start_asap } fn required_trbs(transfer: &Transfer) -> usize { @@ -562,16 +563,15 @@ impl EndpointOp for Endpoint { SubmittedTdKind::Normal { completion_trb } } TransferKind::Isochronous { packet_lengths } => { - let ids = self.enque_iso( + let packets = self.enque_iso( data_bus_addr, packet_lengths, matches!(transfer.direction, Direction::In), ); - let packet_trbs = ids.1; - for packet_trb in &packet_trbs { - self.trb_to_request.insert(*packet_trb, request_id); + for packet in &packets { + self.trb_to_request.insert(packet.trb, request_id); } - SubmittedTdKind::Iso { packet_trbs } + SubmittedTdKind::Iso { packets } } }; @@ -645,41 +645,78 @@ impl EndpointOp for Endpoint { } None } - SubmittedTdKind::Iso { packet_trbs } => { - for packet_trb in &packet_trbs { - if let Some(event) = self.ring.peek_finished(packet_trb.0) { - match event.completion_code() { - Ok(code) if code.to_result().is_ok() => {} - _ => { - let event = self - .ring - .get_finished(packet_trb.0) - .expect("peeked ISO event disappeared"); - return Some( - self.complete_request(request_id, *packet_trb, event) - .map(|transfer| transfer_to_completion(id, transfer)), - ); + SubmittedTdKind::Iso { packets } => { + let packet_trbs: Vec = + packets.iter().map(|packet| packet.trb).collect(); + for (index, packet_trb) in packet_trbs.iter().copied().enumerate() { + let already_done = self + .inflight + .get(&request_id) + .and_then(|submitted| match &submitted.kind { + SubmittedTdKind::Iso { packets } => { + packets.get(index).map(|packet| packet.actual.is_some()) } - } + _ => None, + }) + .unwrap_or(true); + if already_done { + continue; } - } - if packet_trbs - .iter() - .all(|packet_trb| self.ring.has_finished(packet_trb.0)) - { - let completion_trb = *packet_trbs.last().expect("ISO TD has no packet TRBs"); - let event = self - .ring - .get_finished(completion_trb.0) - .expect("ready ISO completion disappeared"); - Some( - self.complete_request(request_id, completion_trb, event) - .map(|transfer| transfer_to_completion(id, transfer)), - ) - } else { - None + let Some(event) = self.ring.get_finished(packet_trb.0) else { + continue; + }; + let requested = match self.inflight.get(&request_id) { + Some(submitted) => match &submitted.transfer.kind { + TransferKind::Isochronous { packet_lengths } => { + packet_lengths.get(index).copied().unwrap_or(0) + } + _ => 0, + }, + None => return None, + }; + let actual = match iso_packet_actual_length(requested, event) { + Ok(actual) => actual, + Err(err) => { + let cleanup_result = + self.complete_request(request_id, packet_trb, event); + let result = match cleanup_result { + Ok(_) => Err(err), + Err(cleanup_err) => Err(cleanup_err), + }; + return Some( + result.map(|transfer| transfer_to_completion(id, transfer)), + ); + } + }; + let fatal = iso_packet_is_fatal(event); + let all_completed = { + let Some(submitted) = self.inflight.get_mut(&request_id) else { + return None; + }; + let SubmittedTdKind::Iso { packets } = &mut submitted.kind else { + return None; + }; + for packet in packets.iter_mut().take(index) { + if packet.actual.is_none() { + packet.actual = Some(0); + } + } + if let Some(packet) = packets.get_mut(index) { + packet.event = Some(event); + packet.actual = Some(actual); + } + packets.iter().all(|packet| packet.actual.is_some()) + }; + + if fatal || all_completed { + return Some( + self.complete_request(request_id, packet_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ); + } } + None } } } @@ -696,9 +733,9 @@ impl EndpointOp for Endpoint { SubmittedTdKind::Control(control_td) => { control_td.register_waker(&self.ring, cx); } - SubmittedTdKind::Iso { packet_trbs } => { - for packet_trb in packet_trbs { - self.ring.register_cx(packet_trb.0, cx); + SubmittedTdKind::Iso { packets } => { + for packet in packets { + self.ring.register_cx(packet.trb.0, cx); } } } @@ -715,6 +752,58 @@ impl EndpointOp for Endpoint { } } +fn iso_packet_actual_length( + requested: usize, + event: TransferEvent, +) -> Result { + let remaining = event.trb_transfer_length() as usize; + match event.completion_code() { + Ok(code) => match code { + xhci::ring::trb::event::CompletionCode::Success => { + if remaining == 0 { + Ok(requested) + } else { + Ok(requested.saturating_sub(remaining)) + } + } + xhci::ring::trb::event::CompletionCode::ShortPacket + | xhci::ring::trb::event::CompletionCode::BabbleDetectedError + | xhci::ring::trb::event::CompletionCode::IsochBufferOverrun + | xhci::ring::trb::event::CompletionCode::MissedServiceError + | xhci::ring::trb::event::CompletionCode::UsbTransactionError + | xhci::ring::trb::event::CompletionCode::Stopped => { + Ok(requested.saturating_sub(remaining)) + } + xhci::ring::trb::event::CompletionCode::StoppedShortPacket => Ok(remaining), + xhci::ring::trb::event::CompletionCode::StoppedLengthInvalid => Ok(0), + code => { + code.to_result()?; + Ok(requested.saturating_sub(remaining)) + } + }, + Err(e) => Err(TransferError::Other(anyhow!( + "unknown XHCI ISO completion code: {e:?}" + ))), + } +} + +fn iso_packet_is_fatal(event: TransferEvent) -> bool { + match event.completion_code() { + Ok( + xhci::ring::trb::event::CompletionCode::Success + | xhci::ring::trb::event::CompletionCode::ShortPacket + | xhci::ring::trb::event::CompletionCode::BabbleDetectedError + | xhci::ring::trb::event::CompletionCode::IsochBufferOverrun + | xhci::ring::trb::event::CompletionCode::MissedServiceError + | xhci::ring::trb::event::CompletionCode::UsbTransactionError + | xhci::ring::trb::event::CompletionCode::Stopped + | xhci::ring::trb::event::CompletionCode::StoppedShortPacket + | xhci::ring::trb::event::CompletionCode::StoppedLengthInvalid, + ) => false, + Ok(_) | Err(_) => true, + } +} + pub(crate) trait EndpointDescriptorExt { fn endpoint_type(&self) -> xhci::context::EndpointType; } diff --git a/usb-host/src/backend/kmod/xhci/event.rs b/usb-host/src/backend/kmod/xhci/event.rs index f12aa83a..3c46dcde 100644 --- a/usb-host/src/backend/kmod/xhci/event.rs +++ b/usb-host/src/backend/kmod/xhci/event.rs @@ -1,8 +1,10 @@ +use alloc::vec::Vec; + use dma_api::{DArray, DmaDirection}; use mbarrier::mb; use xhci::ring::trb::event::Allowed; -use super::ring::Ring; +use super::ring::{Ring, TRBS_PER_SEGMENT}; use crate::{err::*, osal::Kernel}; #[repr(C)] @@ -13,60 +15,83 @@ pub struct EventRingSte { } pub struct EventRing { - ring: Ring, + segments: Vec, + segment_index: usize, + trb_index: usize, + cycle: bool, pub ste: DArray, } unsafe impl Send for EventRing {} unsafe impl Sync for EventRing {} -impl EventRing { - pub fn new(dma: &Kernel) -> Result { - let ring = Ring::new(true, DmaDirection::Bidirectional, dma)?; +const EVENT_RING_SEGMENTS: usize = 16; - // let mut ste = DVec::zeros(dma_mask as _, 1, 64, dma_api::Direction::Bidirectional) - // .map_err(|_| USBError::NoMemory)?; +impl EventRing { + pub fn new(max_segments: usize, dma: &Kernel) -> Result { + let segment_count = EVENT_RING_SEGMENTS.min(max_segments.max(1)); + let mut segments = Vec::with_capacity(segment_count); + for _ in 0..segment_count { + segments.push(Ring::new_segment(false, DmaDirection::Bidirectional, dma)?); + } let mut ste = dma - .array_zero_with_align(1, 64, DmaDirection::Bidirectional) + .array_zero_with_align(segment_count, 64, DmaDirection::Bidirectional) .map_err(|_| USBError::NoMemory)?; - let ste0 = EventRingSte { - addr: ring.trbs.dma_addr().as_u64(), - size: ring.len() as _, - _reserved: [0; 6], - }; - - ste.set(0, ste0); + for (index, segment) in segments.iter().enumerate() { + ste.set( + index, + EventRingSte { + addr: segment.trbs.dma_addr().as_u64(), + size: TRBS_PER_SEGMENT as _, + _reserved: [0; 6], + }, + ); + } - Ok(Self { ring, ste }) + Ok(Self { + segments, + segment_index: 0, + trb_index: 0, + cycle: true, + ste, + }) } - /// 完成一次循环返回 true pub fn next(&mut self) -> Option { - let (data, flag) = self.ring.current_data(); + let data = self.current_data(); let allowed = Allowed::try_from(data.to_raw()).ok()?; - if flag != allowed.cycle_bit() { + if self.cycle != allowed.cycle_bit() { return None; } mb(); - self.ring.inc_deque(); + self.inc_deque(); Some(allowed) } pub fn has_pending_event(&mut self) -> bool { - let (data, flag) = self.ring.current_data(); + let data = self.current_data(); let Ok(allowed) = Allowed::try_from(data.to_raw()) else { return false; }; - flag == allowed.cycle_bit() + self.cycle == allowed.cycle_bit() } pub fn erdp(&self) -> u64 { - self.ring.current_trb_addr().raw() & 0xFFFF_FFFF_FFFF_FFF0 + self.current_segment().trb_bus_addr(self.trb_index).raw() & 0xFFFF_FFFF_FFFF_FFF0 } + + pub fn erst_dequeue_pointer(&self) -> u64 { + self.erdp() | (self.segment_index as u64 & 0x7) + } + + pub fn segment_index(&self) -> u8 { + (self.segment_index & 0x7) as u8 + } + pub fn erstba(&self) -> u64 { self.ste.dma_addr().as_u64() } @@ -78,10 +103,33 @@ impl EventRing { pub fn info(&self) -> EventRingInfo { EventRingInfo { erstz: self.len() as _, - erdp: self.erdp(), + erdp: self.erst_dequeue_pointer(), erstba: self.erstba(), } } + + fn current_segment(&self) -> &Ring { + &self.segments[self.segment_index] + } + + fn current_data(&self) -> super::ring::TrbData { + self.current_segment() + .trbs + .read(self.trb_index) + .expect("event ring TRB index out of bounds") + } + + fn inc_deque(&mut self) { + self.trb_index += 1; + if self.trb_index >= TRBS_PER_SEGMENT { + self.trb_index = 0; + self.segment_index += 1; + if self.segment_index >= self.segments.len() { + self.segment_index = 0; + self.cycle = !self.cycle; + } + } + } } pub struct EventRingInfo { diff --git a/usb-host/src/backend/kmod/xhci/host.rs b/usb-host/src/backend/kmod/xhci/host.rs index 94882cf7..ffd9f84e 100644 --- a/usb-host/src/backend/kmod/xhci/host.rs +++ b/usb-host/src/backend/kmod/xhci/host.rs @@ -109,7 +109,12 @@ impl Xhci { let cmd = CommandRing::new(DmaDirection::Bidirectional, &kernel, reg_shared.clone())?; let cmd_finished = cmd.finished_handle(); - let event_ring = EventRing::new(&kernel)?; + let max_event_ring_segments = reg + .capability + .hcsparams2 + .read_volatile() + .event_ring_segment_table_max() as usize; + let event_ring = EventRing::new(max_event_ring_segments, &kernel)?; let event_ring_info = event_ring.info(); let root_hub = XhciRootHub::new(reg.clone())?; @@ -385,8 +390,8 @@ impl Xhci { debug!("ERDP: {erdp:x}"); ir0.erdp.update_volatile(|r| { - r.set_event_ring_dequeue_pointer(erdp); - r.set_dequeue_erst_segment_index(0); + r.set_event_ring_dequeue_pointer(erdp & !0xf); + r.set_dequeue_erst_segment_index((erdp & 0x7) as u8); r.clear_event_handler_busy(); }); @@ -552,6 +557,24 @@ impl EventHandler { unsafe { &mut *self.reg.get() } } + fn update_erdp(&self, clear_ehb: bool) { + let erdp = self.event_ring().erdp(); + let segment_index = self.event_ring().segment_index(); + self.reg() + .interrupter_register_set + .interrupter_mut(0) + .erdp + .update_volatile(|r| { + r.set_event_ring_dequeue_pointer(erdp); + r.set_dequeue_erst_segment_index(segment_index); + if clear_ehb { + r.clear_event_handler_busy(); + } else { + r.set_0_event_handler_busy(); + } + }); + } + fn clean_event_ring(&self) -> Event { use xhci::ring::trb::event::Allowed; let mut event = Event::Nothing; @@ -559,6 +582,7 @@ impl EventHandler { let mut port_events = 0usize; let mut transfer_events = 0usize; let mut other_events = 0usize; + let mut event_loop = 0usize; while let Some(allowed) = self.event_ring().next() { match allowed { @@ -611,6 +635,11 @@ impl EventHandler { trace!("xhci: event other {:?}", allowed); } } + event_loop += 1; + if event_loop > super::ring::TRBS_PER_SEGMENT / 2 { + self.update_erdp(false); + event_loop = 0; + } } trace!( "xhci: event ring drained command={} port={} transfer={} other={} erdp={:#x}", @@ -618,7 +647,7 @@ impl EventHandler { port_events, transfer_events, other_events, - self.event_ring().erdp() + self.event_ring().erst_dequeue_pointer() ); event } @@ -646,7 +675,7 @@ impl EventHandlerOp for EventHandler { iman.interrupt_enable(), erdp.event_handler_busy(), erdp.event_ring_dequeue_pointer(), - self.event_ring().erdp() + self.event_ring().erst_dequeue_pointer() ); } else { trace!( @@ -655,7 +684,7 @@ impl EventHandlerOp for EventHandler { iman.interrupt_enable(), erdp.event_handler_busy(), erdp.event_ring_dequeue_pointer(), - self.event_ring().erdp() + self.event_ring().erst_dequeue_pointer() ); } } @@ -673,16 +702,8 @@ impl EventHandlerOp for EventHandler { r.clear_interrupt_pending(); }); - let erdp = { - res = self.clean_event_ring(); - self.event_ring().erdp() - }; - { - irq.erdp.update_volatile(|r| { - r.set_event_ring_dequeue_pointer(erdp); - r.clear_event_handler_busy(); - }); - } + res = self.clean_event_ring(); + self.update_erdp(true); res } diff --git a/usb-host/src/backend/kmod/xhci/ring.rs b/usb-host/src/backend/kmod/xhci/ring.rs index bf3cb7cb..4c449665 100644 --- a/usb-host/src/backend/kmod/xhci/ring.rs +++ b/usb-host/src/backend/kmod/xhci/ring.rs @@ -12,7 +12,8 @@ use crate::{ }; const TRB_LEN: usize = 4; -const TRB_SIZE: usize = size_of::(); +pub(crate) const TRB_SIZE: usize = size_of::(); +pub(crate) const TRBS_PER_SEGMENT: usize = 256; const DEFAULT_RING_PAGES: usize = 2; #[derive(Clone)] @@ -80,12 +81,12 @@ impl Ring { Ok(Self::new_with_len(len, link, direction, dma)?) } - pub fn len(&self) -> usize { - self.trbs.len() + pub fn new_segment(link: bool, direction: DmaDirection, dma: &Kernel) -> Result { + Ok(Self::new_with_len(TRBS_PER_SEGMENT, link, direction, dma)?) } - fn get_trb(&self) -> Option { - self.trbs.read(self.i) + pub fn len(&self) -> usize { + self.trbs.len() } pub fn bus_addr(&self) -> BusAddr { @@ -143,10 +144,6 @@ impl Ring { addr } - pub fn current_data(&mut self) -> (TrbData, bool) { - (self.get_trb().unwrap(), self.cycle) - } - fn next_index(&mut self) -> usize { self.i += 1; let len = self.len(); @@ -177,24 +174,11 @@ impl Ring { self.i } - pub fn inc_deque(&mut self) { - self.i += 1; - let len = self.len(); - if self.i >= len { - self.i = 0; - self.cycle = !self.cycle; - } - } - pub fn trb_bus_addr(&self, i: usize) -> BusAddr { let base = self.bus_addr().raw(); (base + (i * size_of::()) as u64).into() } - pub fn current_trb_addr(&self) -> BusAddr { - self.trb_bus_addr(self.i) - } - pub fn trb_bus_addr_list(&self) -> impl Iterator + '_ { (0..self.len()).map(move |i| self.trb_bus_addr(i)) } @@ -264,17 +248,6 @@ impl SendRing { self.finished.get_finished(addr) } - pub fn peek_finished(&self, addr: BusAddr) -> Option - where - R: Copy, - { - self.finished.peek_finished(addr) - } - - pub fn has_finished(&self, addr: BusAddr) -> bool { - self.finished.has_finished(addr) - } - pub fn register_cx(&self, addr: BusAddr, cx: &mut core::task::Context<'_>) { self.finished.register_cx(addr, cx); } diff --git a/usb-host/src/backend/umod/endpoint.rs b/usb-host/src/backend/umod/endpoint.rs index 5086c6d7..85149e5b 100644 --- a/usb-host/src/backend/umod/endpoint.rs +++ b/usb-host/src/backend/umod/endpoint.rs @@ -240,7 +240,7 @@ impl EndpointOp for EndpointImpl { .get(&id.raw()) .ok_or(TransferError::InvalidEndpoint)?; let res = unsafe { libusb_cancel_transfer(trans.transfer) }; - if res == libusb1_sys::constants::LIBUSB_SUCCESS as i32 { + if res == libusb1_sys::constants::LIBUSB_SUCCESS { Ok(()) } else { Err(TransferError::Other(anyhow!( From 5fe600f8eb48718e69fc979eb99ac22516f959cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 15:03:43 +0800 Subject: [PATCH 4/8] fix(event): handle transfer activity event when no other events are present --- usb-host/src/backend/kmod/xhci/host.rs | 5 +++++ usb-host/src/backend/ty/mod.rs | 1 + 2 files changed, 6 insertions(+) diff --git a/usb-host/src/backend/kmod/xhci/host.rs b/usb-host/src/backend/kmod/xhci/host.rs index ffd9f84e..243859e0 100644 --- a/usb-host/src/backend/kmod/xhci/host.rs +++ b/usb-host/src/backend/kmod/xhci/host.rs @@ -649,6 +649,11 @@ impl EventHandler { other_events, self.event_ring().erst_dequeue_pointer() ); + if matches!(event, Event::Nothing) && transfer_events > 0 { + event = Event::TransferActivity { + count: transfer_events, + }; + } event } } diff --git a/usb-host/src/backend/ty/mod.rs b/usb-host/src/backend/ty/mod.rs index f3030238..ed78af04 100644 --- a/usb-host/src/backend/ty/mod.rs +++ b/usb-host/src/backend/ty/mod.rs @@ -14,6 +14,7 @@ pub mod transfer; pub enum Event { Nothing, PortChange { port: u8 }, + TransferActivity { count: usize }, Stopped, } From a47e61c421e1501895904e982e1157fa77f8cf41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 15:11:47 +0800 Subject: [PATCH 5/8] fix(endpoint): improve endpoint descriptor retrieval and streamline control request handling --- usb-host/src/backend/kmod/xhci/device.rs | 30 ++- usb-host/src/backend/kmod/xhci/endpoint.rs | 279 ++++++++++++--------- 2 files changed, 174 insertions(+), 135 deletions(-) diff --git a/usb-host/src/backend/kmod/xhci/device.rs b/usb-host/src/backend/kmod/xhci/device.rs index 0cc20b67..a7996fcf 100644 --- a/usb-host/src/backend/kmod/xhci/device.rs +++ b/usb-host/src/backend/kmod/xhci/device.rs @@ -418,7 +418,7 @@ impl Device { let drop_dcis = stale_endpoints .iter() .filter_map(|address| { - self.find_ep_desc_any_alt(*address) + self.find_ep_desc_in_current_config(*address) .ok() .map(|desc| desc.dci()) }) @@ -518,7 +518,7 @@ impl Device { .eps .keys() .filter_map(|address| { - self.find_ep_desc_any_alt(*address) + self.find_ep_desc_in_current_config(*address) .ok() .map(|desc| desc.dci()) }) @@ -540,6 +540,7 @@ impl Device { .set_input_context_pointer(self.ctx.input_bus_addr()), )) .await?; + // Keep old endpoint rings alive until hardware accepts the new input context. drop(old_endpoints); Ok(()) @@ -564,14 +565,23 @@ impl Device { Err(USBError::NotFound) } - fn find_ep_desc_any_alt(&self, address: u8) -> Result<&EndpointDescriptor> { - for config in &self.config_desc { - for iface in &config.interfaces { - for alt in &iface.alt_settings { - for desc in &alt.endpoints { - if desc.address == address { - return Ok(desc); - } + fn current_config_desc(&self) -> Result<&ConfigurationDescriptor> { + let Some(current_config_value) = self.current_config_value else { + return Err(USBError::ConfigurationNotSet); + }; + self.config_desc + .iter() + .find(|config| config.configuration_value == current_config_value) + .ok_or(USBError::NotFound) + } + + fn find_ep_desc_in_current_config(&self, address: u8) -> Result<&EndpointDescriptor> { + let config = self.current_config_desc()?; + for iface in &config.interfaces { + for alt in &iface.alt_settings { + for desc in &alt.endpoints { + if desc.address == address { + return Ok(desc); } } } diff --git a/usb-host/src/backend/kmod/xhci/endpoint.rs b/usb-host/src/backend/kmod/xhci/endpoint.rs index 8e0453e9..9fda0145 100644 --- a/usb-host/src/backend/kmod/xhci/endpoint.rs +++ b/usb-host/src/backend/kmod/xhci/endpoint.rs @@ -52,13 +52,13 @@ struct ControlTd { impl ControlTd { fn trbs(self) -> impl Iterator { - let mut trbs = Vec::with_capacity(3); - trbs.push((self.setup_trb, ControlStage::Setup)); - if let Some(data_trb) = self.data_trb { - trbs.push((data_trb, ControlStage::Data)); - } - trbs.push((self.status_trb, ControlStage::Status)); - trbs.into_iter() + [ + Some((self.setup_trb, ControlStage::Setup)), + self.data_trb.map(|trb| (trb, ControlStage::Data)), + Some((self.status_trb, ControlStage::Status)), + ] + .into_iter() + .flatten() } fn register_waker(&self, ring: &SendRing, cx: &mut core::task::Context<'_>) { @@ -159,9 +159,13 @@ impl Endpoint { } fn allocate_request_id(&mut self) -> EndpointRequestId { - let id = EndpointRequestId(self.next_request_id); - self.next_request_id = self.next_request_id.wrapping_add(1).max(1); - id + loop { + let id = EndpointRequestId(self.next_request_id); + self.next_request_id = self.next_request_id.wrapping_add(1).max(1); + if !self.inflight.contains_key(&id) { + return id; + } + } } fn public_request_id(id: EndpointRequestId) -> RequestId { @@ -216,10 +220,15 @@ impl Endpoint { event_trb: TransferId, event: TransferEvent, ) -> Result { - let submitted = self - .inflight - .remove(&request_id) - .expect("request disappeared"); + let submitted = self.inflight.remove(&request_id).ok_or_else(|| { + warn!( + "xhci: completion for missing request dci={} request_id={} event_trb={:#x}", + self.dci.raw(), + request_id.0, + event_trb.0.raw() + ); + TransferError::InvalidEndpoint + })?; self.outstanding_trbs = self.outstanding_trbs.saturating_sub(submitted.trb_count); match &submitted.kind { SubmittedTdKind::Normal { completion_trb } => { @@ -306,6 +315,135 @@ impl Endpoint { Ok(transfer) } + fn reclaim_control_request( + &mut self, + id: RequestId, + request_id: EndpointRequestId, + control_td: ControlTd, + ) -> Option> { + for (event_trb, stage) in control_td.trbs() { + let Some(event) = self.ring.get_finished(event_trb.0) else { + continue; + }; + let remaining = event.trb_transfer_length() as usize; + if let Some(submitted) = self.inflight.get_mut(&request_id) + && let SubmittedTdKind::Control(control_td) = &mut submitted.kind + { + match stage { + ControlStage::Setup => {} + ControlStage::Data => { + control_td.actual = + Some(submitted.transfer.buffer_len().saturating_sub(remaining)); + } + ControlStage::Status if control_td.actual.is_none() => { + control_td.actual = Some(control_td.requested); + } + ControlStage::Status => {} + } + } + match event.completion_code() { + Ok(code) if code.to_result().is_ok() => { + if matches!(stage, ControlStage::Status) { + return Some( + self.complete_request(request_id, event_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ); + } + } + _ => { + return Some( + self.complete_request(request_id, event_trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ); + } + } + } + None + } + + fn reclaim_iso_request( + &mut self, + id: RequestId, + request_id: EndpointRequestId, + packets: &[IsoPacketTd], + ) -> Option> { + for (index, packet) in packets.iter().copied().enumerate() { + if self.iso_packet_done(request_id, index) { + continue; + } + + let Some(event) = self.ring.get_finished(packet.trb.0) else { + continue; + }; + let requested = self.iso_requested_length(request_id, index)?; + let actual = match iso_packet_actual_length(requested, event) { + Ok(actual) => actual, + Err(err) => { + let cleanup_result = self.complete_request(request_id, packet.trb, event); + let result = match cleanup_result { + Ok(_) => Err(err), + Err(cleanup_err) => Err(cleanup_err), + }; + return Some(result.map(|transfer| transfer_to_completion(id, transfer))); + } + }; + + let fatal = iso_packet_is_fatal(event); + let all_completed = self.record_iso_packet(request_id, index, event, actual)?; + if fatal || all_completed { + return Some( + self.complete_request(request_id, packet.trb, event) + .map(|transfer| transfer_to_completion(id, transfer)), + ); + } + } + None + } + + fn iso_packet_done(&self, request_id: EndpointRequestId, index: usize) -> bool { + self.inflight + .get(&request_id) + .and_then(|submitted| match &submitted.kind { + SubmittedTdKind::Iso { packets } => { + packets.get(index).map(|packet| packet.actual.is_some()) + } + _ => None, + }) + .unwrap_or(true) + } + + fn iso_requested_length(&self, request_id: EndpointRequestId, index: usize) -> Option { + self.inflight + .get(&request_id) + .and_then(|submitted| match &submitted.transfer.kind { + TransferKind::Isochronous { packet_lengths } => packet_lengths.get(index).copied(), + _ => None, + }) + } + + fn record_iso_packet( + &mut self, + request_id: EndpointRequestId, + index: usize, + event: TransferEvent, + actual: usize, + ) -> Option { + let submitted = self.inflight.get_mut(&request_id)?; + let SubmittedTdKind::Iso { packets } = &mut submitted.kind else { + return None; + }; + for packet in packets.iter_mut().take(index) { + if packet.actual.is_none() { + packet.actual = Some(0); + } + } + if let Some(packet) = packets.get_mut(index) { + packet.event = Some(event); + packet.actual = Some(actual); + } + Some(packets.iter().all(|packet| packet.actual.is_some())) + } + fn enque_trb(&mut self, trb: transfer::Allowed) -> TransferId { TransferId(self.ring.enque_transfer(trb)) } @@ -606,118 +744,9 @@ impl EndpointOp for Endpoint { ) } SubmittedTdKind::Control(control_td) => { - for (event_trb, stage) in control_td.trbs() { - let Some(event) = self.ring.get_finished(event_trb.0) else { - continue; - }; - let remaining = event.trb_transfer_length() as usize; - if let Some(submitted) = self.inflight.get_mut(&request_id) - && let SubmittedTdKind::Control(control_td) = &mut submitted.kind - { - match stage { - ControlStage::Setup => {} - ControlStage::Data => { - control_td.actual = - Some(submitted.transfer.buffer_len().saturating_sub(remaining)); - } - ControlStage::Status if control_td.actual.is_none() => { - control_td.actual = Some(control_td.requested); - } - ControlStage::Status => {} - } - } - match event.completion_code() { - Ok(code) if code.to_result().is_ok() => { - if matches!(stage, ControlStage::Status) { - return Some( - self.complete_request(request_id, event_trb, event) - .map(|transfer| transfer_to_completion(id, transfer)), - ); - } - } - _ => { - return Some( - self.complete_request(request_id, event_trb, event) - .map(|transfer| transfer_to_completion(id, transfer)), - ); - } - } - } - None - } - SubmittedTdKind::Iso { packets } => { - let packet_trbs: Vec = - packets.iter().map(|packet| packet.trb).collect(); - for (index, packet_trb) in packet_trbs.iter().copied().enumerate() { - let already_done = self - .inflight - .get(&request_id) - .and_then(|submitted| match &submitted.kind { - SubmittedTdKind::Iso { packets } => { - packets.get(index).map(|packet| packet.actual.is_some()) - } - _ => None, - }) - .unwrap_or(true); - if already_done { - continue; - } - - let Some(event) = self.ring.get_finished(packet_trb.0) else { - continue; - }; - let requested = match self.inflight.get(&request_id) { - Some(submitted) => match &submitted.transfer.kind { - TransferKind::Isochronous { packet_lengths } => { - packet_lengths.get(index).copied().unwrap_or(0) - } - _ => 0, - }, - None => return None, - }; - let actual = match iso_packet_actual_length(requested, event) { - Ok(actual) => actual, - Err(err) => { - let cleanup_result = - self.complete_request(request_id, packet_trb, event); - let result = match cleanup_result { - Ok(_) => Err(err), - Err(cleanup_err) => Err(cleanup_err), - }; - return Some( - result.map(|transfer| transfer_to_completion(id, transfer)), - ); - } - }; - let fatal = iso_packet_is_fatal(event); - let all_completed = { - let Some(submitted) = self.inflight.get_mut(&request_id) else { - return None; - }; - let SubmittedTdKind::Iso { packets } = &mut submitted.kind else { - return None; - }; - for packet in packets.iter_mut().take(index) { - if packet.actual.is_none() { - packet.actual = Some(0); - } - } - if let Some(packet) = packets.get_mut(index) { - packet.event = Some(event); - packet.actual = Some(actual); - } - packets.iter().all(|packet| packet.actual.is_some()) - }; - - if fatal || all_completed { - return Some( - self.complete_request(request_id, packet_trb, event) - .map(|transfer| transfer_to_completion(id, transfer)), - ); - } - } - None + self.reclaim_control_request(id, request_id, control_td) } + SubmittedTdKind::Iso { packets } => self.reclaim_iso_request(id, request_id, &packets), } } From 1deecb951a953f0061c0108ffa5a8a01b0122f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 15:12:00 +0800 Subject: [PATCH 6/8] fix(parser): optimize format type handling and clean up unused imports --- test_crates/test_libusb_uvc/src/main.rs | 2 +- utils/uvc-frame-parser/src/lib.rs | 2 +- utils/uvc-frame-parser/src/main.rs | 10 ++-------- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/test_crates/test_libusb_uvc/src/main.rs b/test_crates/test_libusb_uvc/src/main.rs index cf75c21f..dddb6b79 100644 --- a/test_crates/test_libusb_uvc/src/main.rs +++ b/test_crates/test_libusb_uvc/src/main.rs @@ -153,7 +153,7 @@ async fn main() -> Result<(), Box> { } } } - () + } }); diff --git a/utils/uvc-frame-parser/src/lib.rs b/utils/uvc-frame-parser/src/lib.rs index f94eabeb..66f56866 100644 --- a/utils/uvc-frame-parser/src/lib.rs +++ b/utils/uvc-frame-parser/src/lib.rs @@ -942,7 +942,7 @@ impl Parser { format_type: &UncompressedFormat, video_format: &VideoFormat, ) -> Result<(), Box> { - let format_type = format_type.clone(); + let format_type = *format_type; let video_format = video_format.clone(); let input_dir = self.input_dir.clone(); let output_dir = self.output_dir.clone(); diff --git a/utils/uvc-frame-parser/src/main.rs b/utils/uvc-frame-parser/src/main.rs index cd7b383c..cafc1611 100644 --- a/utils/uvc-frame-parser/src/main.rs +++ b/utils/uvc-frame-parser/src/main.rs @@ -4,7 +4,6 @@ use clap::{Arg, Command}; use crab_uvc::{UncompressedFormat, VideoFormat, VideoFormatType}; -use env_logger; use log::{error, info, warn}; use regex::Regex; use std::fs::File; @@ -299,18 +298,13 @@ fn clean_mjpeg_data(mut data: Vec) -> Result, Box Option { - for i in 0..data.len().saturating_sub(1) { - if data[i] == 0xFF && data[i + 1] == 0xD9 { - return Some(i); - } - } - None + (0..data.len().saturating_sub(1)).find(|&i| data[i] == 0xFF && data[i + 1] == 0xD9) } /// 将十六进制字符串转换为字节数组 fn hex_to_bytes(hex_str: &str) -> Result, Box> { let hex_clean = hex_str.replace(" ", ""); - if hex_clean.len() % 2 != 0 { + if !hex_clean.len().is_multiple_of(2) { return Err("Invalid hex string length".into()); } From 199802e7459bb6f0537aab33311000445a968944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 15:13:54 +0800 Subject: [PATCH 7/8] fix(parser): remove unnecessary code and improve error handling in image conversion --- test_crates/test_libusb_uvc/src/main.rs | 1 - utils/uvc-frame-parser/src/lib.rs | 9 ++++++--- utils/uvc-frame-parser/src/main.rs | 1 - 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/test_crates/test_libusb_uvc/src/main.rs b/test_crates/test_libusb_uvc/src/main.rs index dddb6b79..ae1b51fd 100644 --- a/test_crates/test_libusb_uvc/src/main.rs +++ b/test_crates/test_libusb_uvc/src/main.rs @@ -153,7 +153,6 @@ async fn main() -> Result<(), Box> { } } } - } }); diff --git a/utils/uvc-frame-parser/src/lib.rs b/utils/uvc-frame-parser/src/lib.rs index 66f56866..a89ed5b8 100644 --- a/utils/uvc-frame-parser/src/lib.rs +++ b/utils/uvc-frame-parser/src/lib.rs @@ -598,7 +598,11 @@ impl Parser { file.write_all(raw_data).await?; // 尝试用 FFmpeg 转换为 PNG - if let Err(_) = self.convert_jpeg_to_png(&jpeg_path, output_path).await { + if self + .convert_jpeg_to_png(&jpeg_path, output_path) + .await + .is_err() + { // 如果转换失败,至少我们有 JPEG 文件 debug!("Kept JPEG file: {}", jpeg_path); } @@ -939,10 +943,9 @@ impl Parser { async fn convert_raw_frames_to_images( &self, frame_numbers: Vec, - format_type: &UncompressedFormat, + _format_type: &UncompressedFormat, video_format: &VideoFormat, ) -> Result<(), Box> { - let format_type = *format_type; let video_format = video_format.clone(); let input_dir = self.input_dir.clone(); let output_dir = self.output_dir.clone(); diff --git a/utils/uvc-frame-parser/src/main.rs b/utils/uvc-frame-parser/src/main.rs index cafc1611..10552d74 100644 --- a/utils/uvc-frame-parser/src/main.rs +++ b/utils/uvc-frame-parser/src/main.rs @@ -139,7 +139,6 @@ async fn parse_serial_log( continue; } if trimmed.contains("FRAME_DATA_END") { - in_frame_data = false; break; } if in_frame_data { From 912f71993e4fb110d831cab197af1d5c841dce80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Fri, 8 May 2026 17:32:49 +0800 Subject: [PATCH 8/8] fix(queue): replace UnsafeCell with Mutex for safer data handling and improve finished data management fix(endpoint): refactor ISO packet handling and streamline request completion logic fix(event): reduce event ring segments from 16 to 2 for optimized resource usage --- usb-host/src/backend/kmod/queue.rs | 124 +++++++++++++++------ usb-host/src/backend/kmod/xhci/endpoint.rs | 66 +++++++---- usb-host/src/backend/kmod/xhci/event.rs | 2 +- 3 files changed, 139 insertions(+), 53 deletions(-) diff --git a/usb-host/src/backend/kmod/queue.rs b/usb-host/src/backend/kmod/queue.rs index 6461ed50..557f713f 100644 --- a/usb-host/src/backend/kmod/queue.rs +++ b/usb-host/src/backend/kmod/queue.rs @@ -1,10 +1,10 @@ use alloc::sync::Arc; -use core::pin::Pin; -use core::task::Context; -use core::task::Poll; use core::{ cell::UnsafeCell, - sync::atomic::{AtomicBool, AtomicUsize, Ordering}, + hint::spin_loop, + pin::Pin, + sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}, + task::{Context, Poll}, }; use futures::task::AtomicWaker; @@ -35,12 +35,17 @@ impl Clone for Finished { } pub struct FinishedInner { - data: UnsafeCell>>>, + data: BTreeMap>>, } +const SLOT_EMPTY: u8 = 0; +const SLOT_WRITING: u8 = 1; +const SLOT_READY: u8 = 2; +const SLOT_READING: u8 = 3; + pub struct FinishedData { taken: AtomicBool, - finished: AtomicBool, + state: AtomicU8, waker: AtomicWaker, data: UnsafeCell>, } @@ -48,7 +53,7 @@ pub struct FinishedData { impl FinishedData { fn new() -> Self { Self { - finished: AtomicBool::new(false), + state: AtomicU8::new(SLOT_EMPTY), taken: AtomicBool::new(false), waker: AtomicWaker::new(), data: UnsafeCell::new(None), @@ -56,19 +61,16 @@ impl FinishedData { } } -unsafe impl Send for FinishedInner {} -unsafe impl Sync for FinishedInner {} -unsafe impl Send for FinishedData {} -unsafe impl Sync for FinishedData {} +unsafe impl Send for FinishedData {} +unsafe impl Sync for FinishedData {} + +unsafe impl Send for FinishedInner {} +unsafe impl Sync for FinishedInner {} impl FinishedInner { fn clear_finished(&self, addr: BusAddr) { - if let Some(data) = unsafe { &mut *self.data.get() }.get(&addr) { - data.finished.store(false, Ordering::Release); - data.taken.store(false, Ordering::Release); - unsafe { - (*data.data.get()).take(); - } + if let Some(data) = self.data.get(&addr) { + data.clear(); } } } @@ -81,9 +83,7 @@ impl Finished { data.insert(addr, Arc::new(FinishedData::new())); } Self { - inner: Arc::new(FinishedInner { - data: UnsafeCell::new(data), - }), + inner: Arc::new(FinishedInner { data }), } } @@ -92,13 +92,8 @@ impl Finished { } pub fn set_finished(&self, addr: BusAddr, value: C) { - let data = unsafe { &mut *self.inner.data.get() }; - if let Some(slot) = data.get_mut(&addr) { - unsafe { - *slot.data.get() = Some(value); - } - slot.finished.store(true, Ordering::Release); - slot.waker.wake(); + if let Some(slot) = self.inner.data.get(&addr) { + slot.set_finished(value); } else if take_queue_log_budget() { warn!( "usb queue: completion address {:#x} is not registered", @@ -112,8 +107,7 @@ impl Finished { } fn waiter(&self, addr: BusAddr) -> &FinishedData { - let data = unsafe { &mut *self.inner.data.get() }; - let slot = data.get(&addr).unwrap(); + let slot = self.inner.data.get(&addr).unwrap(); if slot.taken.load(Ordering::Acquire) { panic!("waiter called after take_waiter"); } @@ -126,7 +120,7 @@ impl Finished { } pub fn take_waiter(&self, addr: BusAddr) -> TWaiter { - let data = unsafe { &mut *self.inner.data.get() }.get(&addr).unwrap(); + let data = self.inner.data.get(&addr).unwrap(); if data.taken.swap(true, Ordering::AcqRel) { panic!("take_waiter called multiple times for the same addr"); } @@ -161,10 +155,76 @@ impl FinishedData { self.waker.register(waker); } + fn clear(&self) { + loop { + match self.state.load(Ordering::Acquire) { + SLOT_EMPTY => return, + SLOT_READY => { + if self + .state + .compare_exchange( + SLOT_READY, + SLOT_READING, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_ok() + { + unsafe { + (*self.data.get()).take(); + } + self.state.store(SLOT_EMPTY, Ordering::Release); + return; + } + } + SLOT_WRITING | SLOT_READING => spin_loop(), + _ => { + self.state.store(SLOT_EMPTY, Ordering::Release); + return; + } + } + } + } + + pub fn set_finished(&self, value: C) { + if self + .state + .compare_exchange( + SLOT_EMPTY, + SLOT_WRITING, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_err() + { + if take_queue_log_budget() { + warn!("usb queue: dropping duplicate completion for busy slot"); + } + return; + } + + unsafe { + *self.data.get() = Some(value); + } + self.state.store(SLOT_READY, Ordering::Release); + self.waker.wake(); + } + pub fn get_finished(&self) -> Option { - if !self.finished.load(Ordering::Acquire) { + if self + .state + .compare_exchange( + SLOT_READY, + SLOT_READING, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_err() + { return None; } - unsafe { (*self.data.get()).take() } + let value = unsafe { (*self.data.get()).take() }; + self.state.store(SLOT_EMPTY, Ordering::Release); + value } } diff --git a/usb-host/src/backend/kmod/xhci/endpoint.rs b/usb-host/src/backend/kmod/xhci/endpoint.rs index 9fda0145..5c11453e 100644 --- a/usb-host/src/backend/kmod/xhci/endpoint.rs +++ b/usb-host/src/backend/kmod/xhci/endpoint.rs @@ -85,6 +85,7 @@ enum SubmittedTdKind { #[derive(Clone, Copy)] struct IsoPacketTd { trb: TransferId, + final_packet: bool, event: Option, actual: Option, } @@ -365,21 +366,21 @@ impl Endpoint { &mut self, id: RequestId, request_id: EndpointRequestId, - packets: &[IsoPacketTd], ) -> Option> { - for (index, packet) in packets.iter().copied().enumerate() { + let packet_count = self.iso_packet_count(request_id)?; + for index in 0..packet_count { if self.iso_packet_done(request_id, index) { continue; } + let (packet_trb, requested) = self.iso_packet_info(request_id, index)?; - let Some(event) = self.ring.get_finished(packet.trb.0) else { + let Some(event) = self.ring.get_finished(packet_trb.0) else { continue; }; - let requested = self.iso_requested_length(request_id, index)?; let actual = match iso_packet_actual_length(requested, event) { Ok(actual) => actual, Err(err) => { - let cleanup_result = self.complete_request(request_id, packet.trb, event); + let cleanup_result = self.complete_request(request_id, packet_trb, event); let result = match cleanup_result { Ok(_) => Err(err), Err(cleanup_err) => Err(cleanup_err), @@ -389,10 +390,11 @@ impl Endpoint { }; let fatal = iso_packet_is_fatal(event); - let all_completed = self.record_iso_packet(request_id, index, event, actual)?; - if fatal || all_completed { + let should_complete = + self.record_iso_packet(request_id, index, event, actual, fatal)?; + if should_complete { return Some( - self.complete_request(request_id, packet.trb, event) + self.complete_request(request_id, packet_trb, event) .map(|transfer| transfer_to_completion(id, transfer)), ); } @@ -400,6 +402,15 @@ impl Endpoint { None } + fn iso_packet_count(&self, request_id: EndpointRequestId) -> Option { + self.inflight + .get(&request_id) + .and_then(|submitted| match &submitted.kind { + SubmittedTdKind::Iso { packets } => Some(packets.len()), + _ => None, + }) + } + fn iso_packet_done(&self, request_id: EndpointRequestId, index: usize) -> bool { self.inflight .get(&request_id) @@ -412,13 +423,23 @@ impl Endpoint { .unwrap_or(true) } - fn iso_requested_length(&self, request_id: EndpointRequestId, index: usize) -> Option { - self.inflight - .get(&request_id) - .and_then(|submitted| match &submitted.transfer.kind { - TransferKind::Isochronous { packet_lengths } => packet_lengths.get(index).copied(), - _ => None, - }) + fn iso_packet_info( + &self, + request_id: EndpointRequestId, + index: usize, + ) -> Option<(TransferId, usize)> { + let submitted = self.inflight.get(&request_id)?; + let SubmittedTdKind::Iso { packets } = &submitted.kind else { + return None; + }; + let packet = packets.get(index)?; + let requested = match &submitted.transfer.kind { + TransferKind::Isochronous { packet_lengths } => { + packet_lengths.get(index).copied().unwrap_or(0) + } + _ => return None, + }; + Some((packet.trb, requested)) } fn record_iso_packet( @@ -427,21 +448,25 @@ impl Endpoint { index: usize, event: TransferEvent, actual: usize, + fatal: bool, ) -> Option { let submitted = self.inflight.get_mut(&request_id)?; let SubmittedTdKind::Iso { packets } = &mut submitted.kind else { return None; }; - for packet in packets.iter_mut().take(index) { - if packet.actual.is_none() { - packet.actual = Some(0); + let final_packet = packets.get(index).is_some_and(|packet| packet.final_packet); + if final_packet || fatal { + for packet in packets.iter_mut().take(index) { + if packet.actual.is_none() { + packet.actual = Some(0); + } } } if let Some(packet) = packets.get_mut(index) { packet.event = Some(event); packet.actual = Some(actual); } - Some(packets.iter().all(|packet| packet.actual.is_some())) + Some(final_packet || fatal || packets.iter().all(|packet| packet.actual.is_some())) } fn enque_trb(&mut self, trb: transfer::Allowed) -> TransferId { @@ -473,6 +498,7 @@ impl Endpoint { ); packets.push(IsoPacketTd { trb, + final_packet: last_packet, event: None, actual: None, }); @@ -746,7 +772,7 @@ impl EndpointOp for Endpoint { SubmittedTdKind::Control(control_td) => { self.reclaim_control_request(id, request_id, control_td) } - SubmittedTdKind::Iso { packets } => self.reclaim_iso_request(id, request_id, &packets), + SubmittedTdKind::Iso { .. } => self.reclaim_iso_request(id, request_id), } } diff --git a/usb-host/src/backend/kmod/xhci/event.rs b/usb-host/src/backend/kmod/xhci/event.rs index 3c46dcde..27eeae29 100644 --- a/usb-host/src/backend/kmod/xhci/event.rs +++ b/usb-host/src/backend/kmod/xhci/event.rs @@ -25,7 +25,7 @@ pub struct EventRing { unsafe impl Send for EventRing {} unsafe impl Sync for EventRing {} -const EVENT_RING_SEGMENTS: usize = 16; +const EVENT_RING_SEGMENTS: usize = 2; impl EventRing { pub fn new(max_segments: usize, dma: &Kernel) -> Result {