diff --git a/src/device/pcap/mod.rs b/src/device/pcap/mod.rs index 0cf393d4..91b74674 100644 --- a/src/device/pcap/mod.rs +++ b/src/device/pcap/mod.rs @@ -17,12 +17,7 @@ pub fn control_submission(base: EndpointPcapMeta, req: &UsbRequest) { } else { UsbDirection::DeviceToHost }; - packet::log_control_submission( - base, - control_direction, - req, - req.data.as_deref().unwrap_or(&[]), - ); + packet::log_control_submission(base, control_direction, req, &req.data); } pub fn control_completion_in(base: EndpointPcapMeta, urb_id: u64, data: &[u8]) { @@ -52,7 +47,7 @@ pub fn control_in_error( UsbEventType::Error, meta::control_error_status(error), Some(packet::build_setup_bytes(req)), - req.data.as_deref().unwrap_or(&[]), + &req.data, ); } @@ -68,7 +63,7 @@ pub fn control_out_error( UsbEventType::Error, meta::control_error_status(error), Some(packet::build_setup_bytes(req)), - req.data.as_deref().unwrap_or(&[]), + &req.data, ); } diff --git a/src/device/xhci/endpoint_handle.rs b/src/device/xhci/endpoint_handle.rs index 9c50ad32..dfe50443 100644 --- a/src/device/xhci/endpoint_handle.rs +++ b/src/device/xhci/endpoint_handle.rs @@ -1,6 +1,8 @@ -use std::{fmt::Debug, future::Future, mem, ops::ControlFlow, pin::Pin}; +use core::panic; +use std::{cmp::Ordering, fmt::Debug, future::Future, pin::Pin}; -use tracing::debug; +use anyhow::Ok; +use tracing::{debug, error, info, trace}; use crate::device::{ bus::BusDeviceRef, @@ -12,11 +14,16 @@ use crate::device::{ ControlRequestProcessingResult, InTrbProcessingResult, OutTrbProcessingResult, RealControlEndpointHandle, RealInEndpointHandle, RealOutEndpointHandle, }, - trb::{CompletionCode, EventTrb, RawTrb, TransferTrb, TransferTrbVariant}, + trb::{ + CompletionCode, DataStageTrb, EventDataTrb, EventTrb, NormalTrb, RawTrb, SetupStageTrb, + StatusStageTrb, TransferTrbVariant, TrbDmaInfo, + }, usbrequest::UsbRequest, }, }; +pub const MAX_VALUE_U24: u32 = 0xff_ffff; + pub trait EndpointHandle: BaseEndpointHandle { type TrbCompletionFuture<'a>: Future> + Send + 'a; @@ -24,7 +31,7 @@ pub trait EndpointHandle: BaseEndpointHandle { fn next_completion(&mut self) -> Self::TrbCompletionFuture<'_>; } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TrbProcessingResult { Ok, Stall, @@ -59,16 +66,143 @@ impl BaseEndpointHandle for DummyEndpointHandle { } } +/// Values we will need to handle an incoming Event Data Trb. +/// +/// When an Event Data is encountered two additional things are needed: +/// - the EDTLA +/// - the completion code of the previously handled TRB +#[derive(Debug, PartialEq, Eq)] +struct EventDataTrbMetadata { + /// a 24 Bit sized counter to track already transmitted bytes of the current TD + edtla: u32, + previous_completion_code: CompletionCode, +} +impl EventDataTrbMetadata { + const fn default() -> Self { + Self { + edtla: 0, + previous_completion_code: CompletionCode::Success, + } + } + + const fn zero(&mut self) { + self.edtla = 0; + } + /// input is 17 Bit + const fn add(&mut self, addend: u32) { + self.edtla = MAX_VALUE_U24 & (self.edtla.wrapping_add(addend)); + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct ControlTransferState { + /// upcoming or current stage/TD of a control transfer to be handled + pub state: ControlTransferStage, + /// holding the UsbRequest and all associated data + pub data: ControlTransferData, + event_meta: EventDataTrbMetadata, +} +impl ControlTransferState { + // previous_completion_code should never be used as is, thus the error as a default value + const fn new(data: ControlTransferData) -> Self { + Self { + state: ControlTransferStage::ExpectSetupStageTrb, + data, + event_meta: EventDataTrbMetadata::default(), + } + } +} + +fn interrupt_on_completion( + address: u64, + completion_code: CompletionCode, + event_data: bool, + endpoint_id: u8, + slot_id: u8, + event_sender: &EventSender, +) -> anyhow::Result<()> { + trace!("interrupt_on_completion triggered for address {}", address); + let event = EventTrb::new_transfer_event_trb( + address, + 0, + completion_code, + event_data, + endpoint_id, + slot_id, + ); + + event_sender.send(event)?; + Ok(()) +} + +/// Track how far we are with parsing the Control Transfer (chain of TRB). +/// +/// ```mermaid +/// graph TD; +/// +/// expect_setup_stage_trb((expect_setup_stage_trb)) +/// maybe_data((maybe_data)) +/// more_data((more_data)) +/// expect_status_stage_trb((expect_status_stage_trb)) +/// expect_event_data_as_final_trb((expect_event_data_as_final_trb)) +/// +/// expect_setup_stage_trb--(received setup_stage_trb)-->maybe_data +/// expect_setup_stage_trb--(any other trb)-->expect_setup_stage_trb +/// +/// maybe_data--(received setup_stage_trb)-->maybe_data +/// maybe_data--(status_stage, with chain)-->expect_event_data_as_final_trb +/// maybe_data--(status_stage, no chain or any other trb)-->expect_setup_stage_trb +/// maybe_data--(data_stage, no chain)-->expect_status_stage_trb +/// maybe_data--(data_stage, with chain)-->more_data +/// +/// more_data--(received setup_stage_trb)-->maybe_data +/// more_data--(any other trb)-->expect_setup_stage_trb +/// more_data--(normal or event_data, with chain)-->more_data +/// more_data--(normal or event_data, no chain)-->expect_status_stage_trb +/// +/// expect_status_stage_trb--(received setup_stage)-->maybe_data +/// expect_status_stage_trb--(status_stage, with chain)-->expect_event_data_as_final_trb +/// expect_status_stage_trb--(status_stage, no chain or any other trb)-->expect_setup_stage_trb +/// +/// expect_event_data_as_final_trb--(received setup_stage)-->maybe_data +/// expect_event_data_as_final_trb--(event_data, no chain or any other trb)-->expect_setup_stage_trb +/// ``` +#[derive(Debug, PartialEq, Eq)] +pub enum ControlTransferStage { + /// Nothing happened yet. Awaiting a Setup Stage Trb and dropping any other + /// Trb (they will not reach the hardware device). + ExpectSetupStageTrb, + /// Either collect data if a Data Stage Trb is received or skip the Data + /// Stage TD altogether if a Status Stage Trb is received. + MaybeDataStageTrb, + MoreData, + /// Finished processing the Data Stage if there was one. + ExpectStatusStageTrb, + /// Status Stage TRB had a chain bit and there will be exactly one + /// Event Data Trb to finish the Control Transfer. + ExpectFinalEventDataTrb, +} + +/// The state machine provides the information partially as ControlSubmissionState::AwaitingControlIn(TransferTrb). +/// Track state between us and the guest for building the current control request. +#[derive(Debug, PartialEq, Eq)] +pub enum ControlTransferData { + In(UsbRequest), + Out(UsbRequest), +} + #[derive(Debug)] pub struct ControlEndpointHandle { slot_id: u8, endpoint_id: u8, pcap_meta: EndpointPcapMeta, real_ep: RCEH, - trb_parser: ControlRequestParser, dma_bus: BusDeviceRef, event_sender: EventSender, + /// referring to usbvfiod to hardware communication submission_state: ControlSubmissionState, + /// referring to usbvfiod to guest communication + transfer_state: ControlTransferState, } impl ControlEndpointHandle { @@ -85,24 +219,372 @@ impl ControlEndpointHandle { endpoint_id, pcap_meta, real_ep, - trb_parser: ControlRequestParser::new(dma_bus.clone()), dma_bus, event_sender, submission_state: ControlSubmissionState::NoTrbSubmitted, + transfer_state: ControlTransferState::new(ControlTransferData::In( + UsbRequest::default(), + )), + } + } + + fn handle_setup_stage_trb(&mut self, address: u64, trb: SetupStageTrb) -> anyhow::Result<()> { + let usb_request = UsbRequest { + address, + request_type: trb.request_type, + request: trb.request, + value: trb.value, + index: trb.index, + length: trb.length, + data_pointer: None, + data: vec![], + }; + + if trb.request_type & 0x80 != 0 { + trace!("SetupStage TRB with ControlIn"); + + self.transfer_state = + ControlTransferState::new(ControlTransferData::In(usb_request.clone())); + + self.real_ep.submit_control_request(usb_request.clone())?; + pcap::control_submission(self.pcap_meta, &usb_request); + + self.submission_state = ControlSubmissionState::AwaitingControlIn( + address, + TransferTrbVariant::SetupStage(trb), + ); + } else { + trace!("SetupStage TRB with ControlOut"); + + self.transfer_state = ControlTransferState::new(ControlTransferData::Out(usb_request)); + + // actual hardware request happens in status stage after consuming the data stage td + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + self.transfer_state.state = ControlTransferStage::MaybeDataStageTrb; + self.submission_state = ControlSubmissionState::ParserConsumedTrb( + address, + TransferTrbVariant::SetupStage(trb), + ); + } + + Ok(()) + } + + fn handle_setup_stage_hardware_response( + &mut self, + address: u64, + trb: SetupStageTrb, + hardware_data: &mut Vec, + ) -> anyhow::Result<()> { + match &mut self.transfer_state.data { + // collect hardware data + ControlTransferData::In(request) => { + trace!("control in data {:?}", hardware_data); + + pcap::control_completion_in(self.pcap_meta, request.address, hardware_data); + + request.data.append(hardware_data); + + request.data.resize(trb.length as usize, 0); + + self.transfer_state.event_meta.previous_completion_code = CompletionCode::Success; + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + self.transfer_state.state = ControlTransferStage::MaybeDataStageTrb; + } + ControlTransferData::Out(_) => { + unreachable!("internal error: ControlOut SetupTrb have insufficient information to do the Hardware request; a submission state to arrive here should never be used"); + } + } + Ok(()) + } + + fn data_slices(&mut self, address: u64, trb: &T) { + let data_pointer = trb.data_pointer(); + let transfer_length = trb.transfer_length(); + let immediate_data = trb.has_immediate_data(); + + match &mut self.transfer_state.data { + ControlTransferData::In(usb_request) => { + trace!("DMA for ControlIn"); + + // From xhci specification chapter 4.11.2.2: + // + // System software is responsible for ensuring that the total data length defined by a + // Data Stage TD (i.e. the sum of the Length fields of the Data Stage TRB and all Normal + // TRBs) is equal to wLength. Note that communicating with some non-compliant + // devices may require violating this rule. + if usb_request.data.len() < transfer_length as usize { + self.transfer_state.state = ControlTransferStage::ExpectStatusStageTrb; + self.transfer_state.event_meta.zero(); + self.submission_state = ControlSubmissionState::ParserError(address); // TODO maybe protocol error? + return; + } + + // All transfers are done but to have the expected value in the + // created Events we keep count of pretend transfers. + self.transfer_state.event_meta.add(transfer_length); + + let byte_slice: Vec = usb_request + .data + .drain(0..transfer_length as usize) + .collect(); + + trace!( + "DataStage TRB len: {} slice: {:?}", + byte_slice.len(), + byte_slice + ); + self.dma_bus.write_bulk(data_pointer, &byte_slice); + } + ControlTransferData::Out(usb_request) => { + trace!("DMA for ControlOut"); + + if immediate_data { + // Only event data should follow when immediate data is used here + // but we do not check for that and allow multiple immediate data + // TRB in the data stage TD. + + // SAFETY: is always set in the preceding setup stage + usb_request.data.append( + &mut data_pointer.to_le_bytes()[..transfer_length as usize].to_vec(), + ); + } else { + let mut tmp = vec![0u8; transfer_length as usize]; + self.dma_bus.read_bulk(data_pointer, &mut tmp); + + // SAFETY: is always set in the preceding setup stage + usb_request.data.append(&mut tmp); + } + + // No hardware transfer happened yet but we have to track from + // the guest "successful transmitted" byte count for maybe + // created Events to show the expected edtla. + self.transfer_state.event_meta.add(transfer_length); + } + } + } + + fn handle_data_stage_trb(&mut self, address: u64, trb: DataStageTrb) -> anyhow::Result<()> { + trace!("DataStage TRB"); + + self.data_slices(address, &trb); + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + self.transfer_state.event_meta.previous_completion_code = CompletionCode::Success; + + if trb.chain { + self.transfer_state.state = ControlTransferStage::MoreData; + } else { + self.transfer_state.state = ControlTransferStage::ExpectStatusStageTrb; + } + + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(address, TransferTrbVariant::DataStage(trb)); + Ok(()) + } + + fn handle_normal_trb(&mut self, address: u64, trb: NormalTrb) -> anyhow::Result<()> { + trace!("Normal TRB"); + + self.data_slices(address, &trb); + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + if !trb.chain { + self.transfer_state.state = ControlTransferStage::ExpectStatusStageTrb; + } + + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(address, TransferTrbVariant::Normal(trb)); + Ok(()) + } + + fn handle_status_stage_trb(&mut self, address: u64, trb: StatusStageTrb) -> anyhow::Result<()> { + match &mut self.transfer_state.data { + ControlTransferData::In(_) => { + trace!("StatusStage TRB with ControlIn"); + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + if trb.chain { + self.transfer_state.state = ControlTransferStage::ExpectFinalEventDataTrb; + } else { + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.transfer_state.event_meta.zero(); + } + + self.submission_state = ControlSubmissionState::ParserConsumedTrb( + address, + TransferTrbVariant::StatusStage(trb), + ); + } + ControlTransferData::Out(usb_request_out) => { + trace!("StatusStage TRB with ControlOut"); + + self.real_ep + .submit_control_request(usb_request_out.clone())?; + pcap::control_submission(self.pcap_meta, usb_request_out); + + self.submission_state = ControlSubmissionState::AwaitingControlOut( + address, + TransferTrbVariant::StatusStage(trb), + ); + } + } + Ok(()) + } + + fn handle_status_stage_trb_hardware_response( + &mut self, + address: u64, + trb: StatusStageTrb, + ) -> anyhow::Result<()> { + match &mut self.transfer_state.data { + ControlTransferData::In(_) => { + unreachable!("internal error: ControlIn requests do the Hardware request in the SetupStage; a submission state to arrive here should never be used"); + } + ControlTransferData::Out(usb_request) => { + trace!("StatusStage TRB with ControlOut"); + + pcap::control_completion_out( + self.pcap_meta, + usb_request.address, + u32::from(usb_request.length), + ); + + if trb.chain { + self.transfer_state.state = ControlTransferStage::ExpectFinalEventDataTrb; + } else { + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.transfer_state.event_meta.zero(); + } + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + Ok(()) + } + } + } + + fn handle_event_data_trb(&mut self, address: u64, trb: EventDataTrb) -> anyhow::Result<()> { + trace!("EventData TRB"); + + let event = EventTrb::new_transfer_event_trb( + trb.event_data, + self.transfer_state.event_meta.edtla, + self.transfer_state.event_meta.previous_completion_code, + true, + self.endpoint_id, + self.slot_id, + ); + + self.event_sender.send(event)?; + self.transfer_state.event_meta.zero(); + + // According to the spec Event Data shall always have the IOC bit set. + // We handle the IOC bit with the same generic function as we do on TransferTrb's. + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + if !trb.chain { + match self.transfer_state.state { + ControlTransferStage::MoreData => { + self.transfer_state.state = ControlTransferStage::ExpectStatusStageTrb; + self.transfer_state.event_meta.zero(); + } + ControlTransferStage::ExpectFinalEventDataTrb => { + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.transfer_state.event_meta.zero(); + } + _ => { + error!("driver did not provide a spec ocmpliant control transfer trb chain"); + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.transfer_state.event_meta.zero(); + } + } } + + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(address, TransferTrbVariant::EventData(trb)); + Ok(()) } } -#[derive(Debug, Default)] +/// Track communication between us and the host hardware. +#[derive(Debug, Default, Clone)] enum ControlSubmissionState { #[default] NoTrbSubmitted, - ParserConsumedTrb, - // store address of trb that failed to parse. - // needs to be specified inside the transfer event indicating the error. + ParserConsumedTrb(u64, TransferTrbVariant), ParserError(u64), - AwaitingControlIn(UsbRequest), - AwaitingControlOut(UsbRequest), + AwaitingControlIn(u64, TransferTrbVariant), + AwaitingControlOut(u64, TransferTrbVariant), } impl EndpointHandle for ControlEndpointHandle { @@ -110,28 +592,114 @@ impl EndpointHandle for ControlEndpointHandle> + Send + 'a>>; fn submit_trb(&mut self, trb: RawTrb) -> anyhow::Result<()> { - let trb_address = trb.address; - if let ControlFlow::Break(res) = self.trb_parser.trb(trb) { - match res { - Ok(request) => { - let request_copy = request.clone_without_data(); - let is_out_request = request.request_type & 0x80 == 0; + let variant = TransferTrbVariant::parse(trb.buffer); - pcap::control_submission(self.pcap_meta, &request); + if let TransferTrbVariant::Unrecognized(_, _) = &variant { + // logging this is happening in next_completion() + self.submission_state = ControlSubmissionState::ParserError(trb.address); + } - self.real_ep.submit_control_request(request)?; + match &self.transfer_state.state { + ControlTransferStage::ExpectSetupStageTrb => match variant { + TransferTrbVariant::SetupStage(setup_stage) => { + self.handle_setup_stage_trb(trb.address, setup_stage)?; + } + other_trb => { + info!( + "invalid control transfer sequence; expected Setup Stage Trb, got: {:?}", + other_trb + ); + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(trb.address, other_trb); + } + }, + ControlTransferStage::MaybeDataStageTrb => match variant { + TransferTrbVariant::SetupStage(setup_stage) => { + info!( + "received Setup Stage TRB abort ongoing control transfer in favour of this new one" + ); + self.handle_setup_stage_trb(trb.address, setup_stage)?; + } + TransferTrbVariant::DataStage(data_stage) => { + self.handle_data_stage_trb(trb.address, data_stage)?; + } + TransferTrbVariant::StatusStage(status_stage) => { + self.handle_status_stage_trb(trb.address, status_stage)?; + } + other_trb => { + info!( + "invalid control transfer sequence; expected Setup, Data or Status Stage Trb, got: {:?}", + other_trb + ); + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(trb.address, other_trb); + } + }, + ControlTransferStage::MoreData => match variant { + TransferTrbVariant::SetupStage(setup_stage) => { + info!( + "received Setup Stage TRB abort ongoing control transfer in favour of this new one" + ); + self.handle_setup_stage_trb(trb.address, setup_stage)?; + } + TransferTrbVariant::Normal(normal) => { + self.handle_normal_trb(trb.address, normal)?; + } + TransferTrbVariant::EventData(event_data) => { + self.handle_event_data_trb(trb.address, event_data)?; + } + other_trb => { + info!( + "invalid control transfer sequence; expected Setup Stage, Normal or Event Data Trb, got: {:?}", + other_trb + ); + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(trb.address, other_trb); + } + }, - self.submission_state = match is_out_request { - true => ControlSubmissionState::AwaitingControlOut(request_copy), - false => ControlSubmissionState::AwaitingControlIn(request_copy), - }; + ControlTransferStage::ExpectStatusStageTrb => match variant { + TransferTrbVariant::SetupStage(setup_stage) => { + info!( + "received Setup Stage TRB abort ongoing control transfer in favour of this new one" + ); + self.handle_setup_stage_trb(trb.address, setup_stage)?; } - Err(_) => { - self.submission_state = ControlSubmissionState::ParserError(trb_address); + TransferTrbVariant::StatusStage(status_stage) => { + self.handle_status_stage_trb(trb.address, status_stage)?; } - } - } else { - self.submission_state = ControlSubmissionState::ParserConsumedTrb; + other_trb => { + info!( + "invalid control transfer sequence; expected Setup or Status Stage Trb, got: {:?}", + other_trb + ); + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(trb.address, other_trb); + } + }, + ControlTransferStage::ExpectFinalEventDataTrb => match variant { + TransferTrbVariant::SetupStage(setup_stage) => { + info!( + "received Setup Stage TRB abort ongoing control transfer in favour of this new one" + ); + self.handle_setup_stage_trb(trb.address, setup_stage)?; + } + TransferTrbVariant::EventData(event_data) => { + self.handle_event_data_trb(trb.address, event_data)?; + } + other_trb => { + info!( + "invalid control transfer sequence; expected Setup Stage or Event Data Trb, got: {:?}", + other_trb + ); + self.transfer_state.state = ControlTransferStage::ExpectSetupStageTrb; + self.submission_state = + ControlSubmissionState::ParserConsumedTrb(trb.address, other_trb); + } + }, } Ok(()) @@ -139,12 +707,19 @@ impl EndpointHandle for ControlEndpointHandle Self::TrbCompletionFuture<'_> { Box::pin(async { - let result = match self.submission_state { - ControlSubmissionState::ParserConsumedTrb => TrbProcessingResult::Ok, - ControlSubmissionState::ParserError(trb_address) => { - pcap::trb_error(self.pcap_meta, trb_address); + let result = match self.submission_state.clone() { + ControlSubmissionState::ParserConsumedTrb(address, variant) => { + trace!("consumed trb from address: {} as: {:?}", address, variant); + TrbProcessingResult::Ok + } + ControlSubmissionState::ParserError(address) => { + info!( + "Failed to parse Transfer Trb on Control Endpoint. slot {}", + self.slot_id + ); + pcap::trb_error(self.pcap_meta, address); let event = EventTrb::new_transfer_event_trb( - trb_address, + address, 0, CompletionCode::TrbError, false, @@ -154,67 +729,57 @@ impl EndpointHandle for ControlEndpointHandle { + ControlSubmissionState::AwaitingControlIn(address, variant) => { let processing_result = self.real_ep.next_completion().await?; match processing_result { - ControlRequestProcessingResult::SuccessfulControlIn(data) => { - debug!("got data from control in: {data:?}"); - pcap::control_completion_in(self.pcap_meta, usb_request.address, &data); - if let Some(data_pointer) = usb_request.data_pointer { - debug!("writing data to {data_pointer}"); - self.dma_bus.write_bulk(data_pointer, &data); - } - - let event = EventTrb::new_transfer_event_trb( - usb_request.address, - 0, - CompletionCode::Success, - false, - self.endpoint_id, - self.slot_id, - ); - self.event_sender.send(event)?; - + ControlRequestProcessingResult::SuccessfulControlIn(mut data) => { + let trb = match variant { + TransferTrbVariant::SetupStage(setup_stage) => setup_stage, + _ => unreachable!("internal error: never set this ControlSubmissionState besides with a SetupStage."), + }; + self.handle_setup_stage_hardware_response(address, trb, &mut data)?; TrbProcessingResult::Ok } - ControlRequestProcessingResult::SuccessfulControlOut => unreachable!(), + ControlRequestProcessingResult::SuccessfulControlOut => unreachable!( + "internal error: never set AwaitingControlIn and received a SuccessfulControlOut." + ), processing_error => { + let usb_request = match &self.transfer_state.data { + ControlTransferData::In(usb_request) => usb_request, + _ => unreachable!("internal error: never set AwaitingControlIn without a UsbRequest containing a ControlIn"), + }; pcap::control_in_error(self.pcap_meta, usb_request, &processing_error); - self.handle_processing_error(processing_error, usb_request.address)? + self.handle_processing_error(processing_error, address)? } } } - ControlSubmissionState::AwaitingControlOut(ref usb_request) => { + ControlSubmissionState::AwaitingControlOut(address, variant) => { let processing_result = self.real_ep.next_completion().await?; match processing_result { ControlRequestProcessingResult::SuccessfulControlIn(_) => { - unreachable!() + unreachable!("internal error: never set AwaitingControlOut and receive a SuccessfulControlIn.") } ControlRequestProcessingResult::SuccessfulControlOut => { - pcap::control_completion_out( - self.pcap_meta, - usb_request.address, - u32::from(usb_request.length), - ); - let event = EventTrb::new_transfer_event_trb( - usb_request.address, - 0, - CompletionCode::Success, - false, - self.endpoint_id, - self.slot_id, - ); - self.event_sender.send(event)?; - + let trb = match variant { + TransferTrbVariant::StatusStage(status_stage) => status_stage, + _ => unreachable!("internal error: never set this ControlSubmissionState besides with a StatusStage."), + }; + self.handle_status_stage_trb_hardware_response(address, trb)?; TrbProcessingResult::Ok } processing_error => { + let usb_request = match &self.transfer_state.data { + ControlTransferData::Out(usb_request) => usb_request, + _ => unreachable!("internal error: never set AwaitingControlOut without a UsbRequest containing a ControlOut"), + }; pcap::control_out_error(self.pcap_meta, usb_request, &processing_error); - self.handle_processing_error(processing_error, usb_request.address)? + self.handle_processing_error(processing_error, address)? } } } - ControlSubmissionState::NoTrbSubmitted => unreachable!(), + ControlSubmissionState::NoTrbSubmitted => { + unreachable!("internal error: Always set a different ControlSubmissionState in submit_trb().") + } }; self.submission_state = ControlSubmissionState::NoTrbSubmitted; @@ -281,97 +846,65 @@ impl ControlEndpointHandle { TrbProcessingResult::TransactionError } ControlRequestProcessingResult::SuccessfulControlIn(_) => { - panic!("SuccessfulControlIn should be handled elsewhere") + unreachable!( + "internal error: Don't try processing an error with a successful ControlRequestProcessingResult." + ) } ControlRequestProcessingResult::SuccessfulControlOut => { - panic!("SuccessfulControlOut should be handled elsewhere") + unreachable!( + "internal error: Don't try processing an error with a successful ControlRequestProcessingResult." + ) } }; Ok(mapped) } } -#[derive(Debug)] -struct ControlRequestParser { - state: ControlRequestParserState, - dma_bus: BusDeviceRef, - request_builder: UsbRequest, -} - -impl ControlRequestParser { - fn new(dma_bus: BusDeviceRef) -> Self { - Self { - state: ControlRequestParserState::Initial, - dma_bus, - request_builder: Default::default(), - } +fn handle_event_data_trb_normal_ep( + address: &u64, + event_data_trb: &EventDataTrb, + event_meta: &mut EventDataTrbMetadata, + endpoint_id: u8, + slot_id: u8, + event_sender: &EventSender, +) -> anyhow::Result<()> { + trace!("EventData TRB on Normal Ep"); + + let event = EventTrb::new_transfer_event_trb( + event_data_trb.event_data, + event_meta.edtla, + event_meta.previous_completion_code, + true, + endpoint_id, + slot_id, + ); + + event_sender.send(event)?; + event_meta.zero(); + + // It was not clear from the specification alone if the IOC bit is + // actually intended for the above event or as this separate one. + if event_data_trb.interrupt_on_completion { + interrupt_on_completion( + *address, + CompletionCode::Success, + false, + endpoint_id, + slot_id, + event_sender, + )?; } -} -#[derive(Debug)] -enum ControlRequestParserState { - Initial, - SetupStageConsumed, - DataStageConsumed, + Ok(()) } -impl ControlRequestParser { - fn trb(&mut self, trb: RawTrb) -> ControlFlow> { - let transfer_trb = TransferTrbVariant::parse(trb.buffer); - - loop { - match &self.state { - ControlRequestParserState::Initial => match transfer_trb { - TransferTrbVariant::SetupStage(setup_trb_data) => { - let request = UsbRequest { - address: 0, - request_type: setup_trb_data.request_type, - request: setup_trb_data.request, - value: setup_trb_data.value, - index: setup_trb_data.index, - length: setup_trb_data.length, - data_pointer: None, - data: None, - }; - self.request_builder = request; - self.state = ControlRequestParserState::SetupStageConsumed; - return ControlFlow::Continue(()); - } - _ => return ControlFlow::Break(Err(())), - }, - ControlRequestParserState::SetupStageConsumed => match transfer_trb { - TransferTrbVariant::DataStage(data_trb_data) => { - if data_trb_data.immediate_data { - todo!("immediate data on data stage trb") - } - let mut data = vec![0; self.request_builder.length as usize]; - self.dma_bus - .read_bulk(data_trb_data.data_pointer, &mut data); - - self.request_builder.data = Some(data); - self.request_builder.data_pointer = Some(data_trb_data.data_pointer); - self.state = ControlRequestParserState::DataStageConsumed; - return ControlFlow::Continue(()); - } - TransferTrbVariant::StatusStage(_) => { - self.state = ControlRequestParserState::DataStageConsumed; - continue; - } - _ => return ControlFlow::Break(Err(())), - }, - ControlRequestParserState::DataStageConsumed => match transfer_trb { - TransferTrbVariant::StatusStage(_) => { - self.request_builder.address = trb.address; - let request = mem::take(&mut self.request_builder); - self.request_builder = UsbRequest::default(); - self.state = ControlRequestParserState::Initial; - return ControlFlow::Break(Ok(request)); - } - _ => return ControlFlow::Break(Err(())), - }, - } - } - } +#[derive(Debug, Default, Clone)] +enum NormalSubmissionState { + #[default] + NoTrbSubmitted, + UnsupportedTrbType(RawTrb), + AwaitingRealTransfer(u64, NormalTrb), + ConsumedEventDataTrb, } #[derive(Debug)] @@ -383,6 +916,7 @@ pub struct OutEndpointHandle { dma_bus: BusDeviceRef, event_sender: EventSender, submission_state: NormalSubmissionState, + event_meta: EventDataTrbMetadata, } impl OutEndpointHandle { @@ -402,16 +936,76 @@ impl OutEndpointHandle { dma_bus, event_sender, submission_state: NormalSubmissionState::NoTrbSubmitted, + event_meta: EventDataTrbMetadata::default(), } } -} -#[derive(Debug, Default)] -enum NormalSubmissionState { - #[default] - NoTrbSubmitted, - UnsupportedTrbType(RawTrb), - AwaitingRealTransfer(TransferTrb), + fn handle_normal_trb_pre_hardware( + &mut self, + address: u64, + trb: NormalTrb, + ) -> anyhow::Result<()> { + trace!("handle_normal_trb_pre_hardware Out"); + + if trb.immediate_data { + todo!("immediate data in a Normal Trb on a Normal Out Endpoint") + } + + if !trb.chain { + self.event_meta = EventDataTrbMetadata::default(); + } + + let mut data = vec![0; trb.transfer_length as usize]; + self.dma_bus.read_bulk(trb.data_pointer, &mut data); + + self.real_ep.submit(data.clone())?; + pcap::out_submission(self.pcap_meta, address, &data, trb.transfer_length); + + self.submission_state = NormalSubmissionState::AwaitingRealTransfer(address, trb); + self.event_meta.previous_completion_code = CompletionCode::Success; + + Ok(()) + } + + fn handle_normal_trb_post_hardware( + &mut self, + address: u64, + trb: NormalTrb, + ) -> anyhow::Result<()> { + trace!("handle_normal_trb_post_hardware Out"); + + self.event_meta.add(trb.transfer_length); + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + pcap::out_completion(self.pcap_meta, address, trb.transfer_length); + + self.event_meta.previous_completion_code = CompletionCode::Success; + Ok(()) + } + + fn handle_event_data_trb(&mut self, address: u64, trb: EventDataTrb) -> anyhow::Result<()> { + handle_event_data_trb_normal_ep( + &address, + &trb, + &mut self.event_meta, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + + self.submission_state = NormalSubmissionState::ConsumedEventDataTrb; + Ok(()) + } } impl EndpointHandle for OutEndpointHandle { @@ -424,25 +1018,12 @@ impl EndpointHandle for OutEndpointHandle { "submit_trb called twice without calling next_completion" ); - let transfer_trb = TransferTrbVariant::parse(trb.buffer); - match &transfer_trb { - TransferTrbVariant::Normal(normal_data) => { - if normal_data.immediate_data { - todo!("immediate data on normal trb") - } - let mut data = vec![0; normal_data.transfer_length as usize]; - self.dma_bus.read_bulk(normal_data.data_pointer, &mut data); - pcap::out_submission( - self.pcap_meta, - trb.address, - &data, - normal_data.transfer_length, - ); - self.real_ep.submit(data)?; - self.submission_state = NormalSubmissionState::AwaitingRealTransfer(TransferTrb { - address: trb.address, - variant: transfer_trb, - }); + match TransferTrbVariant::parse(trb.buffer) { + TransferTrbVariant::Normal(normal) => { + self.handle_normal_trb_pre_hardware(trb.address, normal)?; + } + TransferTrbVariant::EventData(event_data) => { + self.handle_event_data_trb(trb.address, event_data)?; } _ => self.submission_state = NormalSubmissionState::UnsupportedTrbType(trb), } @@ -457,7 +1038,15 @@ impl EndpointHandle for OutEndpointHandle { ); Box::pin(async { - let result = match self.submission_state { + let result = match &self.submission_state { + NormalSubmissionState::ConsumedEventDataTrb => { + trace!( + "Slot {} Endpoint {} Consumed Event Data Trb", + self.slot_id, + self.endpoint_id + ); + TrbProcessingResult::Ok + } NormalSubmissionState::UnsupportedTrbType(ref trb) => { let transfer_event = EventTrb::new_transfer_event_trb( trb.address, @@ -471,78 +1060,87 @@ impl EndpointHandle for OutEndpointHandle { TrbProcessingResult::TrbError } - NormalSubmissionState::AwaitingRealTransfer(ref transfer_trb) => { - let (completion_code, processing_result) = - match self.real_ep.next_completion().await? { - OutTrbProcessingResult::Disconnect => { - pcap::out_error( - self.pcap_meta, - transfer_trb.address, - &OutTrbProcessingResult::Disconnect, - &[], - ); - ( - Some(CompletionCode::UsbTransactionError), - TrbProcessingResult::Disconnect, - ) - } - OutTrbProcessingResult::Stall => { - pcap::out_error( - self.pcap_meta, - transfer_trb.address, - &OutTrbProcessingResult::Stall, - &[], - ); - (Some(CompletionCode::StallError), TrbProcessingResult::Stall) - } - OutTrbProcessingResult::TransactionError => { - pcap::out_error( - self.pcap_meta, - transfer_trb.address, - &OutTrbProcessingResult::TransactionError, - &[], - ); - ( - Some(CompletionCode::UsbTransactionError), - TrbProcessingResult::TransactionError, - ) - } - OutTrbProcessingResult::Success => { - let completion_code = - if let TransferTrbVariant::Normal(ref normal_data) = - transfer_trb.variant - { - pcap::out_completion( - self.pcap_meta, - transfer_trb.address, - normal_data.transfer_length, - ); - match normal_data.interrupt_on_completion { - true => Some(CompletionCode::Success), - false => None, - } - } else { - unreachable!(); - }; - (completion_code, TrbProcessingResult::Ok) - } - }; - - if let Some(completion_code) = completion_code { - let transfer_event = EventTrb::new_transfer_event_trb( - transfer_trb.address, - 0, - completion_code, - false, - self.endpoint_id, - self.slot_id, - ); - self.event_sender.send(transfer_event)?; - } + NormalSubmissionState::AwaitingRealTransfer(address, normal) => { + match &self.real_ep.next_completion().await? { + OutTrbProcessingResult::Disconnect => { + info!( + "Device has been disconnected. slot {} ep {}", + self.slot_id, self.endpoint_id + ); + pcap::out_error( + self.pcap_meta, + *address, + &OutTrbProcessingResult::Disconnect, + &[], + ); - processing_result + let event = EventTrb::new_transfer_event_trb( + *address, + 0, + CompletionCode::UsbTransactionError, + false, + self.endpoint_id, + self.slot_id, + ); + self.event_sender.send(event)?; + + TrbProcessingResult::Disconnect + } + OutTrbProcessingResult::Stall => { + debug!( + "Device Stall while waiting for hardware response. slot {} ep {}", + self.slot_id, self.endpoint_id + ); + pcap::out_error( + self.pcap_meta, + *address, + &OutTrbProcessingResult::Stall, + &[], + ); + + let event = EventTrb::new_transfer_event_trb( + *address, + 0, + CompletionCode::StallError, + false, + self.endpoint_id, + self.slot_id, + ); + self.event_sender.send(event)?; + + TrbProcessingResult::Stall + } + OutTrbProcessingResult::TransactionError => { + info!("Transaction Error while waiting for hardware response. slot {} ep {}", + self.slot_id, self.endpoint_id); + pcap::out_error( + self.pcap_meta, + *address, + &OutTrbProcessingResult::TransactionError, + &[], + ); + + let event = EventTrb::new_transfer_event_trb( + *address, + 0, + CompletionCode::UsbTransactionError, + false, + self.endpoint_id, + self.slot_id, + ); + self.event_sender.send(event)?; + + TrbProcessingResult::TransactionError + } + OutTrbProcessingResult::Success => { + self.handle_normal_trb_post_hardware(*address, normal.clone())?; + TrbProcessingResult::Ok + } + } + } + NormalSubmissionState::NoTrbSubmitted => { + unreachable!("internal error: Always set a different ControlSubmissionState in submit_trb().") } - NormalSubmissionState::NoTrbSubmitted => unreachable!(), }; self.submission_state = NormalSubmissionState::NoTrbSubmitted; @@ -572,6 +1170,8 @@ pub struct InEndpointHandle { dma_bus: BusDeviceRef, event_sender: EventSender, submission_state: NormalSubmissionState, + /// Values we will need to handle an incoming Event Data Trb. + event_meta: EventDataTrbMetadata, } impl InEndpointHandle { @@ -591,7 +1191,103 @@ impl InEndpointHandle { dma_bus, event_sender, submission_state: NormalSubmissionState::NoTrbSubmitted, + event_meta: EventDataTrbMetadata::default(), + } + } + + fn handle_normal_trb_pre_hardware( + &mut self, + address: u64, + trb: NormalTrb, + ) -> anyhow::Result<()> { + trace!("handle_normal_trb_pre_hardware In"); + + if trb.immediate_data { + todo!("immediate data in a Normal Trb on a Normal In Endpoint") + } + + if !trb.chain { + self.event_meta = EventDataTrbMetadata::default(); } + + self.real_ep.submit(trb.transfer_length as usize)?; + pcap::in_submission(self.pcap_meta, address, trb.transfer_length); + + self.submission_state = NormalSubmissionState::AwaitingRealTransfer(address, trb); + self.event_meta.previous_completion_code = CompletionCode::Success; + + Ok(()) + } + + fn handle_normal_trb_post_hardware( + &mut self, + address: u64, + trb: NormalTrb, + hardware_data: Vec, + ) -> anyhow::Result<()> { + trace!("handle_normal_trb_post_hardware In"); + + let completion_code: CompletionCode; + + // SACETY: in case the hardware_data.len() is bigger than u32 we take the u32 value regardless + let dma_length: u32 = match hardware_data.len().cmp(&(trb.transfer_length as usize)) { + Ordering::Less => { + debug!("received less than requested"); + completion_code = CompletionCode::ShortPacket; + // SAFETY: comparison with a u32, less will always fit in a u32 + hardware_data.len().try_into().unwrap() + } + Ordering::Equal => { + debug!("received exactly as requested"); + completion_code = CompletionCode::Success; + // SAFETY: comparison with a u32, equal will always fit in a u32 + hardware_data.len().try_into().unwrap() + } + Ordering::Greater => { + error!( + "received more than requested; likely losing this data: {:?}", + &hardware_data[trb.transfer_length as usize..] + ); + completion_code = CompletionCode::Success; + // device responded with more than requested + // idk where the overhead goes but we track the requested amount + trb.transfer_length + } + }; + + self.event_meta.add(dma_length); + self.dma_bus + .write_bulk(trb.data_pointer, &hardware_data[..dma_length as usize]); + + if trb.interrupt_on_completion { + interrupt_on_completion( + address, + CompletionCode::Success, + false, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + } + + pcap::in_completion(self.pcap_meta, address, &hardware_data); + self.event_meta.previous_completion_code = completion_code; + + Ok(()) + } + + fn handle_event_data_trb(&mut self, address: u64, trb: EventDataTrb) -> anyhow::Result<()> { + handle_event_data_trb_normal_ep( + &address, + &trb, + &mut self.event_meta, + self.endpoint_id, + self.slot_id, + &self.event_sender, + )?; + + self.submission_state = NormalSubmissionState::ConsumedEventDataTrb; + Ok(()) } } @@ -605,15 +1301,12 @@ impl EndpointHandle for InEndpointHandle { "submit_trb called twice without calling next_completion" ); - let transfer_trb = TransferTrbVariant::parse(trb.buffer); - match &transfer_trb { - TransferTrbVariant::Normal(normal_data) => { - pcap::in_submission(self.pcap_meta, trb.address, normal_data.transfer_length); - self.real_ep.submit(normal_data.transfer_length as usize)?; - self.submission_state = NormalSubmissionState::AwaitingRealTransfer(TransferTrb { - address: trb.address, - variant: transfer_trb, - }); + match TransferTrbVariant::parse(trb.buffer) { + TransferTrbVariant::Normal(normal) => { + self.handle_normal_trb_pre_hardware(trb.address, normal)?; + } + TransferTrbVariant::EventData(event_data) => { + self.handle_event_data_trb(trb.address, event_data)?; } _ => self.submission_state = NormalSubmissionState::UnsupportedTrbType(trb), } @@ -628,7 +1321,16 @@ impl EndpointHandle for InEndpointHandle { ); Box::pin(async { - let result = match self.submission_state { + let result = match self.submission_state.clone() { + NormalSubmissionState::ConsumedEventDataTrb => { + trace!( + "Slot {} Endpoint {} Consumed Event Data Trb", + self.slot_id, + self.endpoint_id + ); + TrbProcessingResult::Ok + } + NormalSubmissionState::UnsupportedTrbType(ref trb) => { let transfer_event = EventTrb::new_transfer_event_trb( trb.address, @@ -642,90 +1344,80 @@ impl EndpointHandle for InEndpointHandle { TrbProcessingResult::TrbError } - NormalSubmissionState::AwaitingRealTransfer(ref transfer_trb) => { - let (completion_code, processing_result) = match self - .real_ep - .next_completion() - .await? - { + NormalSubmissionState::AwaitingRealTransfer(address, normal) => { + match self.real_ep.next_completion().await? { InTrbProcessingResult::Disconnect => { + info!( + "Device has been disconnected. slot {} ep {}", + self.slot_id, self.endpoint_id + ); pcap::in_error( self.pcap_meta, - transfer_trb.address, + address, &InTrbProcessingResult::Disconnect, ); - ( - Some(CompletionCode::UsbTransactionError), - TrbProcessingResult::Disconnect, - ) + + let event = EventTrb::new_transfer_event_trb( + address, + 0, + CompletionCode::UsbTransactionError, + false, + self.endpoint_id, + self.slot_id, + ); + self.event_sender.send(event)?; + + TrbProcessingResult::Disconnect } InTrbProcessingResult::Stall => { - pcap::in_error( - self.pcap_meta, - transfer_trb.address, - &InTrbProcessingResult::Stall, + debug!( + "Device Stall while waiting for hardware response. slot {} ep {}", + self.slot_id, self.endpoint_id + ); + pcap::in_error(self.pcap_meta, address, &InTrbProcessingResult::Stall); + + let event = EventTrb::new_transfer_event_trb( + address, + 0, + CompletionCode::StallError, + false, + self.endpoint_id, + self.slot_id, ); - (Some(CompletionCode::StallError), TrbProcessingResult::Stall) + self.event_sender.send(event)?; + + TrbProcessingResult::Stall } InTrbProcessingResult::TransactionError => { + info!("Transaction Error while waiting for hardware response. slot {} ep {}", + self.slot_id, self.endpoint_id); pcap::in_error( self.pcap_meta, - transfer_trb.address, + address, &InTrbProcessingResult::TransactionError, ); - ( - Some(CompletionCode::UsbTransactionError), - TrbProcessingResult::TransactionError, - ) + + let event = EventTrb::new_transfer_event_trb( + address, + 0, + CompletionCode::UsbTransactionError, + false, + self.endpoint_id, + self.slot_id, + ); + self.event_sender.send(event)?; + + TrbProcessingResult::TransactionError } InTrbProcessingResult::Success(data) => { - pcap::in_completion(self.pcap_meta, transfer_trb.address, &data); - let completion_code = if let TransferTrbVariant::Normal( - ref normal_data, - ) = transfer_trb.variant - { - // needs more checks. - // - if we got less data, we need to do short-packet handling - let requested_len = normal_data.transfer_length as usize; - let received_len = data.len(); - let dma_length = if received_len > requested_len { - debug!("device delivered more data than requested. Requested {requested_len}, received {received_len}. Sending {:?}, dropping {:?}", &data[..requested_len], &data[requested_len..]); - requested_len - } else { - received_len - }; - self.dma_bus - .write_bulk(normal_data.data_pointer, &data[..dma_length]); - - // event sending only when IOC is set - match normal_data.interrupt_on_completion { - true => Some(CompletionCode::Success), - false => None, - } - } else { - unreachable!(); - }; - - (completion_code, TrbProcessingResult::Ok) + self.handle_normal_trb_post_hardware(address, normal, data)?; + TrbProcessingResult::Ok } - }; - - if let Some(completion_code) = completion_code { - let transfer_event = EventTrb::new_transfer_event_trb( - transfer_trb.address, - 0, - completion_code, - false, - self.endpoint_id, - self.slot_id, - ); - - self.event_sender.send(transfer_event)?; } - - processing_result } - NormalSubmissionState::NoTrbSubmitted => unreachable!(), + NormalSubmissionState::NoTrbSubmitted => { + unreachable!("internal error: Always set a different ControlSubmissionState in submit_trb().") + } }; self.submission_state = NormalSubmissionState::NoTrbSubmitted; @@ -745,3 +1437,1200 @@ impl BaseEndpointHandle for InEndpointHandle { Box::pin(async { self.real_ep.clear_halt().await }) } } + +#[cfg(test)] +mod tests { + use super::*; + + use crate::device::xhci::interrupter::tests::testutils::MockInterrupter; + use crate::device::{bus::testutils::TestBusDevice, xhci::trb::testutils::RawTrbBuilder}; + use crate::dynamic_bus::DynamicBus; + + use std::sync::Arc; + + const SLOT_ID: u8 = 1; + const ENDPOINT_ID: u8 = 1; + + const FIRST_ADDRESS: u64 = 0x10; + const SECOND_ADDRESS: u64 = 0x20; + const THIRD_ADDRESS: u64 = 0x30; + const FOURTH_ADDRESS: u64 = 0x40; + const FIFTH_ADDRESS: u64 = 0x50; + const SIXTH_ADDRESS: u64 = 0x60; + + const DMA_POINTER_1: u64 = 0x200; + const DMA_POINTER_2: u64 = 0x400; + const DMA_POINTER_3: u64 = 0x600; + const DMA_POINTER_4: u64 = 0x800; + + const SETUP_WLENGTH: u16 = 512; + const TRANSFER_LENGTH: u32 = SETUP_WLENGTH as u32; + const EVENT_DATA_FIELD: u64 = 0xda7a; + + const TRB_TYPE_NORMAL: u8 = 0x1; + const TRB_TYPE_SETUP_STAGE: u8 = 0x2; + const TRB_TYPE_DATA_STAGE: u8 = 0x3; + const TRB_TYPE_STATUS_STAGE: u8 = 0x4; + const TRB_TYPE_EVENT_DATA: u8 = 0x7; + + const SETUP_BM_REQUEST_TYPE_IN: u8 = 0x80; + const SETUP_BM_REQUEST_TYPE_OUT: u8 = 0; + + const SETUP_TRANSFER_TYPE_OUT_DATA: u8 = 0x2; + const SETUP_TRANSFER_TYPE_IN_DATA: u8 = 0x3; + + // will return the requested length of bytes with a value of 42 + #[derive(Debug)] + pub struct MockRealControlEndpointReadStatic { + data_length: u16, + direction: bool, + } + impl MockRealControlEndpointReadStatic { + fn new() -> Self { + Self { + data_length: 0, + direction: false, + } + } + } + + impl RealControlEndpointHandle for MockRealControlEndpointReadStatic { + type TrbCompletionFuture<'a> = Pin< + Box> + Send + 'a>, + >; + + fn submit_control_request(&mut self, request: UsbRequest) -> anyhow::Result<()> { + // fake request is instantly submitted but we need to remember the direction for next_complete + const IN: u8 = 0b10000000; + self.direction = (request.request_type & IN) == IN; + self.data_length = request.length; + + Ok(()) + } + + fn next_completion(&mut self) -> Self::TrbCompletionFuture<'_> { + Box::pin(async { + let result = match self.direction { + true => { + let data = vec![42; self.data_length as usize]; + ControlRequestProcessingResult::SuccessfulControlIn(data) + } + false => ControlRequestProcessingResult::SuccessfulControlOut, + }; + Ok(result) + }) + } + } + + impl BaseEndpointHandle for MockRealControlEndpointReadStatic { + type CompletionFuture<'a> = Pin> + Send + 'a>>; + + fn cancel(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + + fn clear_halt(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + } + + // Initialize test environment using the DummyRealControlEndpointReadStatic + // + // Use the ControlEndpointHandle to submit some TransferTrb. + // Use the UnboundedReceiver to directly check events meant for a EventRing. + fn init_control_endpoint_handle_test( + real_ep: T, + ) -> (MockInterrupter, ControlEndpointHandle) { + let pcap_usb_bus_number = 1; + let pcap_meta = EndpointPcapMeta::control(pcap_usb_bus_number, SLOT_ID, ENDPOINT_ID); + + let dma_bus = Arc::new(DynamicBus::new()); + let dma_backing = vec![99; 2048]; + dma_bus + .add(0x0, Arc::new(TestBusDevice::new(&dma_backing[..]))) + .expect(""); + + let (event_sender, interrupter) = MockInterrupter::new(); + + let control_endpoint = ControlEndpointHandle::new( + SLOT_ID, + ENDPOINT_ID, + pcap_meta, + real_ep, + dma_bus, + event_sender, + ); + (interrupter, control_endpoint) + } + + /// Wrapper to simplify creating a successful expected EventTrb for comparison. + fn expected_event(trb_pointer: u64, trb_transfer_length: u32, event_data: bool) -> EventTrb { + EventTrb::new_transfer_event_trb( + trb_pointer, + trb_transfer_length, + CompletionCode::Success, + event_data, + ENDPOINT_ID, + SLOT_ID, + ) + } + + #[tokio::test] + async fn submit_shortest_possible_control_in_request() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + let status_stage = RawTrbBuilder::new(SECOND_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + + let input_trb = vec![setup_stage, status_stage]; + + for trb in input_trb.clone() { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + } + + #[tokio::test] + async fn submit_shortest_possible_control_in_request_with_data_stage() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_setup_length(SETUP_WLENGTH) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .with_byte(14, SETUP_TRANSFER_TYPE_IN_DATA) + .build(); + let data_stage = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_ioc() + .with_type(TRB_TYPE_DATA_STAGE) + .with_dir() + .build(); + let status_stage = RawTrbBuilder::new(THIRD_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + + let input_trb = vec![setup_stage, data_stage, status_stage]; + + for trb in input_trb.clone() { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(THIRD_ADDRESS, 0, false)) + ); + } + + #[tokio::test] + async fn submit_second_illegal_data_stage_trb() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_setup_length(SETUP_WLENGTH) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .with_byte(14, SETUP_TRANSFER_TYPE_IN_DATA) + .build(); + let data_stage_1 = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_DATA_STAGE) + .with_dir() + .build(); + let data_stage_2 = RawTrbBuilder::new(THIRD_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_ioc() + .with_type(TRB_TYPE_DATA_STAGE) + .with_dir() + .build(); + let status_stage = RawTrbBuilder::new(FOURTH_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + + let input_trb = vec![setup_stage, data_stage_1, data_stage_2, status_stage]; + + for trb in input_trb.clone() { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + + assert!(interrupter.is_empty()); + assert!(interrupter.is_empty()); + } + + #[tokio::test] + async fn submit_control_in_request_with_event_data_at_the_end_of_the_data_stage() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_setup_length(SETUP_WLENGTH) + .with_idt() + .with_ioc() + .with_type(0x2) + .with_byte(14, SETUP_TRANSFER_TYPE_IN_DATA) + .build(); + let data_stage = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_ch() + .with_type(0x3) + .with_dir() + .build(); + let event_data = RawTrbBuilder::new(THIRD_ADDRESS) + .with_data_field(EVENT_DATA_FIELD) + .with_ioc() + .with_type(0x7) + .with_dir() + .build(); + let status_stage = RawTrbBuilder::new(FOURTH_ADDRESS) + .with_ioc() + .with_type(0x4) + .with_dir() + .build(); + + let input_trb = vec![setup_stage, data_stage, event_data, status_stage]; + + for trb in input_trb { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(EVENT_DATA_FIELD, 512, true)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(THIRD_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FOURTH_ADDRESS, 0, false)) + ); + } + + #[tokio::test] + async fn submit_control_in_request_with_event_data_between_two_trb_of_the_data_td() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_setup_length(SETUP_WLENGTH * 2) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .with_byte(14, SETUP_TRANSFER_TYPE_IN_DATA) + .build(); + let data_stage = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_ch() + .with_type(TRB_TYPE_DATA_STAGE) + .with_dir() + .build(); + let event_data = RawTrbBuilder::new(THIRD_ADDRESS) + .with_data_field(EVENT_DATA_FIELD) + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_EVENT_DATA) + .with_dir() + .build(); + let normal = RawTrbBuilder::new(FOURTH_ADDRESS) + .with_data_field(DMA_POINTER_2) + .with_length(TRANSFER_LENGTH) + .with_ioc() + .with_type(TRB_TYPE_NORMAL) + .build(); + let status_stage = RawTrbBuilder::new(FIFTH_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + + let input_trb = vec![setup_stage, data_stage, event_data, normal, status_stage]; + + for trb in input_trb { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint + .next_completion() + .await + .expect("this dummy hardware request should never fail"), + TrbProcessingResult::Ok + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(EVENT_DATA_FIELD, 512, true)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(THIRD_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FOURTH_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIFTH_ADDRESS, 0, false)) + ); + } + + #[tokio::test] + async fn submit_control_in_request_with_event_data_after_status_stage_trb() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + let status_stage = RawTrbBuilder::new(SECOND_ADDRESS) + .with_ch() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + let event_data = RawTrbBuilder::new(THIRD_ADDRESS) + .with_data_field(EVENT_DATA_FIELD) + .with_ioc() + .with_type(TRB_TYPE_EVENT_DATA) + .with_dir() + .build(); + + let input_trb = vec![setup_stage, status_stage, event_data]; + + for trb in input_trb.clone() { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(EVENT_DATA_FIELD, 0, true)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(THIRD_ADDRESS, 0, false)) + ); + } + + // expecting to receive 0xda7a via an out request + #[derive(Debug)] + pub struct MockRealControlEndpointExpectDataPattern {} + impl MockRealControlEndpointExpectDataPattern { + fn new() -> Self { + Self {} + } + } + + impl RealControlEndpointHandle for MockRealControlEndpointExpectDataPattern { + type TrbCompletionFuture<'a> = Pin< + Box> + Send + 'a>, + >; + + fn submit_control_request(&mut self, request: UsbRequest) -> anyhow::Result<()> { + assert_eq!(request.data, 0xda7a_u64.to_le_bytes()[..2]); + Ok(()) + } + + fn next_completion(&mut self) -> Self::TrbCompletionFuture<'_> { + Box::pin(async { + let result = ControlRequestProcessingResult::SuccessfulControlOut; + Ok(result) + }) + } + } + + impl BaseEndpointHandle for MockRealControlEndpointExpectDataPattern { + type CompletionFuture<'a> = Pin> + Send + 'a>>; + + fn cancel(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + + fn clear_halt(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + } + + #[tokio::test] + async fn submit_control_out_request_with_data_stage_using_immediate_data() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointExpectDataPattern::new()); + + const DMA_POINTER: u64 = 0xeb8bda7a; + const TRANSFER_LENGTH: u32 = 2; + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_OUT) + .with_setup_length(SETUP_WLENGTH) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .with_byte(14, SETUP_TRANSFER_TYPE_OUT_DATA) + .build(); + let data_stage = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER) + .with_length(TRANSFER_LENGTH) + .with_ioc() + .with_idt() + .with_type(TRB_TYPE_DATA_STAGE) + .build(); + let status_stage = RawTrbBuilder::new(THIRD_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .build(); + + let input_trb = vec![setup_stage, data_stage, status_stage]; + + for trb in input_trb.clone() { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(THIRD_ADDRESS, 0, false)) + ); + } + + #[tokio::test] + async fn submitting_out_of_order_or_unfinished_sequence_does_not_prevent_the_following_valid_sequence_of_trb( + ) { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let status_stage_out_of_order = RawTrbBuilder::new(FIRST_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + let setup_stage_incomplete_sequence = RawTrbBuilder::new(SECOND_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + let setup_stage = RawTrbBuilder::new(THIRD_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_idt() + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + let status_stage = RawTrbBuilder::new(FOURTH_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + + let input_trb = vec![ + status_stage_out_of_order, + setup_stage_incomplete_sequence, + setup_stage, + status_stage, + ]; + + for trb in input_trb.clone() { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + // The incomplete sequence (the second trb; a lone setup stage) is valid + // and we expect an event. + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(THIRD_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FOURTH_ADDRESS, 0, false)) + ); + } + + #[tokio::test] + async fn submit_setup_stage_with_wrong_wlength() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointReadStatic::new()); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + // system software made a mistake; should be TRANSFER_LENGTH*3 + .with_setup_length(SETUP_WLENGTH) + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + let data_stage = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_DATA_STAGE) + .with_dir() + .build(); + // with the above mistake this trb is will fail + let normal_1 = RawTrbBuilder::new(THIRD_ADDRESS) + .with_data_field(DMA_POINTER_2) + .with_length(TRANSFER_LENGTH) + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_NORMAL) + .build(); + let normal_2 = RawTrbBuilder::new(FOURTH_ADDRESS) + .with_data_field(DMA_POINTER_3) + .with_length(TRANSFER_LENGTH) + .with_ioc() + .with_type(TRB_TYPE_NORMAL) + .build(); + let status_stage = RawTrbBuilder::new(FIFTH_ADDRESS) + .with_ioc() + .with_type(TRB_TYPE_STATUS_STAGE) + .with_dir() + .build(); + + let input_trb = vec![setup_stage, data_stage, normal_1, normal_2, status_stage]; + + for trb in input_trb.clone() { + control_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(THIRD_ADDRESS, 0, false)) + ); + assert!(interrupter.is_empty()); + assert!(interrupter.is_empty()); + } + + // expecting to receive 0xda7a via an out request + #[derive(Debug)] + pub struct MockRealControlEndpointHardwareError { + error: ControlRequestProcessingResult, + } + impl MockRealControlEndpointHardwareError { + fn new(error: ControlRequestProcessingResult) -> Self { + Self { error } + } + } + + impl RealControlEndpointHandle for MockRealControlEndpointHardwareError { + type TrbCompletionFuture<'a> = Pin< + Box> + Send + 'a>, + >; + + fn submit_control_request(&mut self, _request: UsbRequest) -> anyhow::Result<()> { + Ok(()) + } + + fn next_completion(&mut self) -> Self::TrbCompletionFuture<'_> { + Box::pin(async { Ok(self.error.clone()) }) + } + } + + impl BaseEndpointHandle for MockRealControlEndpointHardwareError { + type CompletionFuture<'a> = Pin> + Send + 'a>>; + + fn cancel(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + + fn clear_halt(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + } + + #[tokio::test] + async fn control_request_returns_hardware_disconnect() { + let (mut interrupter, mut control_endpoint) = init_control_endpoint_handle_test( + MockRealControlEndpointHardwareError::new(ControlRequestProcessingResult::Disconnect), + ); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_setup_length(SETUP_WLENGTH) + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + + control_endpoint + .submit_trb(setup_stage) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Disconnect) + ); + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(EventTrb::new_transfer_event_trb( + FIRST_ADDRESS, + 0, + CompletionCode::UsbTransactionError, + false, + ENDPOINT_ID, + SLOT_ID, + )) + ); + } + + #[tokio::test] + async fn control_request_returns_hardware_stall() { + let (mut interrupter, mut control_endpoint) = init_control_endpoint_handle_test( + MockRealControlEndpointHardwareError::new(ControlRequestProcessingResult::Stall), + ); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_setup_length(SETUP_WLENGTH) + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + + control_endpoint + .submit_trb(setup_stage) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Stall) + ); + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(EventTrb::new_transfer_event_trb( + FIRST_ADDRESS, + 0, + CompletionCode::StallError, + false, + ENDPOINT_ID, + SLOT_ID, + )) + ); + } + + #[tokio::test] + async fn control_request_returns_hardware_transaction_error() { + let (mut interrupter, mut control_endpoint) = + init_control_endpoint_handle_test(MockRealControlEndpointHardwareError::new( + ControlRequestProcessingResult::TransactionError, + )); + + let setup_stage = RawTrbBuilder::new(FIRST_ADDRESS) + .with_setup_type(SETUP_BM_REQUEST_TYPE_IN) + .with_setup_length(SETUP_WLENGTH) + .with_ioc() + .with_type(TRB_TYPE_SETUP_STAGE) + .build(); + + control_endpoint + .submit_trb(setup_stage) + .expect("this dummy hardware request should never fail"); + assert_eq!( + control_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::TransactionError) + ); + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(EventTrb::new_transfer_event_trb( + FIRST_ADDRESS, + 0, + CompletionCode::UsbTransactionError, + false, + ENDPOINT_ID, + SLOT_ID, + )) + ); + } + + // dummy for bulk in real endpoint returning `vec![42; requested length]` + #[derive(Debug)] + struct DummyRealInEndpoint { + data_length: usize, + } + impl DummyRealInEndpoint { + fn new() -> Self { + Self { data_length: 0 } + } + } + impl RealInEndpointHandle for DummyRealInEndpoint { + type TrbCompletionFuture<'a> = + Pin> + Send + 'a>>; + + fn submit(&mut self, data: usize) -> anyhow::Result<()> { + self.data_length = data; + Ok(()) + } + + fn next_completion(&mut self) -> Self::TrbCompletionFuture<'_> { + Box::pin(async { + let data = vec![42; self.data_length]; + let result = InTrbProcessingResult::Success(data); + Ok(result) + }) + } + } + impl BaseEndpointHandle for DummyRealInEndpoint { + type CompletionFuture<'a> = Pin> + Send + 'a>>; + + fn cancel(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + + fn clear_halt(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + } + + #[tokio::test] + async fn submit_multi_trb_bulk_in_transfer_with_event_data() { + const SLOT_ID: u8 = 1; + const ENDPOINT_ID: u8 = 1; + + let pcap_usb_bus_number = 1; + let pcap_meta = EndpointPcapMeta::bulk(pcap_usb_bus_number, SLOT_ID, ENDPOINT_ID); + + let real_ep = DummyRealInEndpoint::new(); + + let dma_bus = Arc::new(DynamicBus::new()); + let dma_backing = vec![99; 2048]; + dma_bus + .add(0x0, Arc::new(TestBusDevice::new(&dma_backing[..]))) + .expect(""); + + let (event_sender, mut interrupter) = MockInterrupter::new(); + + let mut bulk_in_endpoint = InEndpointHandle::new( + SLOT_ID, + ENDPOINT_ID, + pcap_meta, + real_ep, + dma_bus, + event_sender, + ); + + let normal_1 = RawTrbBuilder::new(FIRST_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x8) // remaining TD Size: 2048 + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_NORMAL) + .build(); + let normal_2 = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER_2) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x6) // remaining TD Size: 1536 + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_NORMAL) + .build(); + let normal_3 = RawTrbBuilder::new(THIRD_ADDRESS) + .with_data_field(DMA_POINTER_3) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x4) // remaining TD Size: 1024 + .with_ch() + .with_type(TRB_TYPE_NORMAL) + .build(); + let event_data_1 = RawTrbBuilder::new(FOURTH_ADDRESS) + .with_data_field(EVENT_DATA_FIELD) + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_EVENT_DATA) + .with_dir() + .build(); + let normal_4 = RawTrbBuilder::new(FIFTH_ADDRESS) + .with_data_field(DMA_POINTER_4) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x2) // remaining TD Size: 512 + .with_ch() + .with_type(TRB_TYPE_NORMAL) + .build(); + let event_data_2 = RawTrbBuilder::new(SIXTH_ADDRESS) + .with_data_field(EVENT_DATA_FIELD) + .with_ioc() + .with_type(TRB_TYPE_EVENT_DATA) + .with_dir() + .build(); + + let input_trb = vec![ + normal_1, + normal_2, + normal_3, + event_data_1, + normal_4, + event_data_2, + ]; + + for trb in input_trb { + bulk_in_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + bulk_in_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(EVENT_DATA_FIELD, TRANSFER_LENGTH * 3, true)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FOURTH_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(EVENT_DATA_FIELD, TRANSFER_LENGTH, true)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SIXTH_ADDRESS, 0, false)) + ); + } + + // dummy for bulk out real endpoint returning success while discarding the data + #[derive(Debug)] + struct MockRealOutEndpoint {} + impl MockRealOutEndpoint { + fn new() -> Self { + Self {} + } + } + impl RealOutEndpointHandle for MockRealOutEndpoint { + type TrbCompletionFuture<'a> = + Pin> + Send + 'a>>; + + fn submit(&mut self, data: Vec) -> anyhow::Result<()> { + println!("consumed data of length: {}", data.len()); + Ok(()) + } + + fn next_completion(&mut self) -> Self::TrbCompletionFuture<'_> { + Box::pin(async { + let result = OutTrbProcessingResult::Success; + Ok(result) + }) + } + } + impl BaseEndpointHandle for MockRealOutEndpoint { + type CompletionFuture<'a> = Pin> + Send + 'a>>; + + fn cancel(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + + fn clear_halt(&mut self) -> Self::CompletionFuture<'_> { + // nothing we want to do + Box::pin(async { Ok(()) }) + } + } + + #[tokio::test] + async fn submit_multi_trb_bulk_out_transfer_with_event_data() { + const SLOT_ID: u8 = 1; + const ENDPOINT_ID: u8 = 1; + + let pcap_usb_bus_number = 1; + let pcap_meta = EndpointPcapMeta::bulk(pcap_usb_bus_number, SLOT_ID, ENDPOINT_ID); + + let real_ep = MockRealOutEndpoint::new(); + + let dma_bus = Arc::new(DynamicBus::new()); + let dma_backing = vec![99; 2048]; + dma_bus + .add(0x0, Arc::new(TestBusDevice::new(&dma_backing[..]))) + .expect(""); + + let (event_sender, mut interrupter) = MockInterrupter::new(); + + let mut bulk_out_endpoint = OutEndpointHandle::new( + SLOT_ID, + ENDPOINT_ID, + pcap_meta, + real_ep, + dma_bus, + event_sender, + ); + + let normal_1 = RawTrbBuilder::new(FIRST_ADDRESS) + .with_data_field(DMA_POINTER_1) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x8) // remaining TD Size: 2048 + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_NORMAL) + .build(); + let normal_2 = RawTrbBuilder::new(SECOND_ADDRESS) + .with_data_field(DMA_POINTER_2) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x6) // remaining TD Size: 1536 + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_NORMAL) + .build(); + let normal_3 = RawTrbBuilder::new(THIRD_ADDRESS) + .with_data_field(DMA_POINTER_3) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x4) // remaining TD Size: 1024 + .with_ch() + .with_type(TRB_TYPE_NORMAL) + .build(); + let event_data_1 = RawTrbBuilder::new(FOURTH_ADDRESS) + .with_data_field(EVENT_DATA_FIELD) + .with_ch() + .with_ioc() + .with_type(TRB_TYPE_EVENT_DATA) + .build(); + let normal_4 = RawTrbBuilder::new(FIFTH_ADDRESS) + .with_data_field(DMA_POINTER_4) + .with_length(TRANSFER_LENGTH) + .with_byte(11, 0x2) // remaining TD Size: 512 + .with_ch() + .with_type(TRB_TYPE_NORMAL) + .build(); + let event_data_2 = RawTrbBuilder::new(SIXTH_ADDRESS) + .with_data_field(EVENT_DATA_FIELD) + .with_ioc() + .with_type(TRB_TYPE_EVENT_DATA) + .build(); + + let input_trb = vec![ + normal_1, + normal_2, + normal_3, + event_data_1, + normal_4, + event_data_2, + ]; + + for trb in input_trb { + bulk_out_endpoint + .submit_trb(trb) + .expect("this dummy hardware request should never fail"); + assert_eq!( + bulk_out_endpoint.next_completion().await.ok(), + Some(TrbProcessingResult::Ok) + ); + } + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FIRST_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SECOND_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(EVENT_DATA_FIELD, TRANSFER_LENGTH * 3, true)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(FOURTH_ADDRESS, 0, false)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(EVENT_DATA_FIELD, TRANSFER_LENGTH, true)) + ); + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(expected_event(SIXTH_ADDRESS, 0, false)) + ); + } +} diff --git a/src/device/xhci/interrupter.rs b/src/device/xhci/interrupter.rs index c1edc121..2814864d 100644 --- a/src/device/xhci/interrupter.rs +++ b/src/device/xhci/interrupter.rs @@ -176,3 +176,55 @@ impl EventWorker { } } } + +#[cfg(test)] +pub mod tests { + use super::*; + + pub mod testutils { + use super::*; + pub struct MockInterrupter { + msg_recv: mpsc::UnboundedReceiver, + } + + impl MockInterrupter { + pub fn new() -> (EventSender, Self) { + let (sender, recv) = mpsc::unbounded_channel(); + let event_sender = EventSender { sender }; + let dummy = Self { msg_recv: recv }; + + (event_sender, dummy) + } + + pub fn is_empty(&self) -> bool { + self.msg_recv.is_empty() + } + + /// receiving anything else than a EventTrb will return None + pub async fn await_event(&mut self) -> Option { + match self.msg_recv.recv().await { + Some(InterrupterMessage::SendEvent(event_trb)) => Some(event_trb), + _ => None, + } + } + } + + mod tests { + use super::*; + + #[tokio::test] + async fn send_event_through_dummy_interrupter() { + let (event_sender, mut interrupter) = MockInterrupter::new(); + + let event = EventTrb::new_port_status_change_event_trb(1); + matches!(event_sender.send(event), Ok(())); + + assert!(!interrupter.is_empty()); + assert_eq!( + interrupter.await_event().await, + Some(EventTrb::new_port_status_change_event_trb(1)) + ); + } + } + } +} diff --git a/src/device/xhci/nusb.rs b/src/device/xhci/nusb.rs index 1d5a43a5..19cbfbec 100644 --- a/src/device/xhci/nusb.rs +++ b/src/device/xhci/nusb.rs @@ -235,7 +235,7 @@ async fn control_endpoint_worker( let processing_result = match is_out_request { true => { - let data = request.data.unwrap_or(Vec::new()); + let data = request.data; let control = ControlOut { control_type, recipient, diff --git a/src/device/xhci/real_endpoint_handle.rs b/src/device/xhci/real_endpoint_handle.rs index 9e027cb4..d30fc8e2 100644 --- a/src/device/xhci/real_endpoint_handle.rs +++ b/src/device/xhci/real_endpoint_handle.rs @@ -11,7 +11,7 @@ pub trait RealControlEndpointHandle: BaseEndpointHandle { fn next_completion(&mut self) -> Self::TrbCompletionFuture<'_>; } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ControlRequestProcessingResult { Disconnect, Stall, diff --git a/src/device/xhci/trb.rs b/src/device/xhci/trb.rs index f31eb54e..aa8e3a25 100644 --- a/src/device/xhci/trb.rs +++ b/src/device/xhci/trb.rs @@ -11,7 +11,7 @@ use super::super::pci::constants::xhci::rings::trb_types::{self, *}; /// of a Transfer Request Block. pub type RawTrbBuffer = [u8; 16]; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct RawTrb { pub address: u64, pub buffer: RawTrbBuffer, @@ -25,7 +25,7 @@ pub const fn zeroed_trb_buffer() -> RawTrbBuffer { /// Represents a TRB that the XHCI controller can place on the event ring. /// /// See XHCI specification Section 6.4.2 for detailed event TRB type descriptions. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum EventTrb { Transfer(TransferEventTrbData), CommandCompletion(CommandCompletionEventTrbData), @@ -65,7 +65,7 @@ impl EventTrb { /// /// Do not use this struct directly, use EventTrb::new_command_completion_event_trb /// instead. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct CommandCompletionEventTrbData { command_trb_pointer: u64, command_completion_parameter: u32, @@ -132,7 +132,7 @@ impl CommandCompletionEventTrbData { /// /// Do not use this struct directly, use EventTrb::new_port_status_change_event_trb /// instead. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct PortStatusChangeEventTrbData { port_id: u8, } @@ -164,9 +164,10 @@ impl PortStatusChangeEventTrbData { } /// Stores the relevant data for a Transfer Event. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub struct TransferEventTrbData { trb_pointer: u64, + /// 24 Bit trb_transfer_length: u32, completion_code: CompletionCode, event_data: bool, @@ -227,7 +228,7 @@ impl TransferEventTrbData { /// /// Refer to Table 6-90 in the XHCI specification for detailed descriptions of each code. #[allow(dead_code)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum CompletionCode { Invalid = 0, Success, @@ -795,26 +796,17 @@ impl TrbData for ResetDeviceCommandTrbData { } } -/// Represents a TRB that the driver can place on a transfer ring. -#[derive(Debug, PartialEq, Eq)] -pub struct TransferTrb { - /// Guest memory address where the driver placed the TRB. - pub address: u64, - /// Information specific to the particular transfer TRB variant. - pub variant: TransferTrbVariant, -} - /// Represents a TRB that the driver can place on a transfer ring. /// /// See XHCI specification Section 6.4.1 for detailed transfer TRB type descriptions. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum TransferTrbVariant { - Normal(NormalTrbData), - SetupStage(SetupStageTrbData), - DataStage(DataStageTrbData), - StatusStage(StatusStageTrbData), + Normal(NormalTrb), + SetupStage(SetupStageTrb), + DataStage(DataStageTrb), + StatusStage(StatusStageTrb), Isoch, - EventData(EventDataTrbData), + EventData(EventDataTrb), NoOp, #[allow(unused)] Unrecognized(RawTrbBuffer, TrbParseError), @@ -848,20 +840,27 @@ impl TransferTrbVariant { } } +pub trait TrbDmaInfo { + fn data_pointer(&self) -> u64; + fn transfer_length(&self) -> u32; + fn has_immediate_data(&self) -> bool; +} + /// Normal TRB data structure (simplified representation). /// /// This struct contains only the commonly used fields from the Normal TRB. /// See XHCI specification Section 6.4.1.1 for the complete TRB layout. -#[derive(Debug, PartialEq, Eq)] -pub struct NormalTrbData { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NormalTrb { pub data_pointer: u64, + /// 17 Bit pub transfer_length: u32, pub chain: bool, pub interrupt_on_completion: bool, pub immediate_data: bool, } -impl TrbData for NormalTrbData { +impl TrbData for NormalTrb { /// Parse data of a Normal TRB. /// /// Only `TransferTrb::try_from` should call this function. @@ -899,20 +898,33 @@ impl TrbData for NormalTrbData { } } +impl TrbDmaInfo for NormalTrb { + fn data_pointer(&self) -> u64 { + self.data_pointer + } + fn transfer_length(&self) -> u32 { + self.transfer_length + } + fn has_immediate_data(&self) -> bool { + self.immediate_data + } +} + /// Setup Stage TRB data structure. /// /// See XHCI specification Section 6.4.1.2.1 for detailed field descriptions. -#[derive(Debug, PartialEq, Eq)] -pub struct SetupStageTrbData { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SetupStageTrb { pub request_type: u8, pub request: u8, pub value: u16, pub index: u16, + /// wLength pub length: u16, pub interrupt_on_completion: bool, } -impl TrbData for SetupStageTrbData { +impl TrbData for SetupStageTrb { /// Parse data of a Setup Stage TRB. /// /// Only `TransferTrb::try_from` should call this function. @@ -950,17 +962,18 @@ impl TrbData for SetupStageTrbData { /// Data Stage TRB data structure. /// /// See XHCI specification Section 6.4.1.2.2 for detailed field descriptions. -#[derive(Debug, PartialEq, Eq)] -pub struct DataStageTrbData { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DataStageTrb { pub data_pointer: u64, - pub transfer_length: u16, + /// 17 Bit + pub transfer_length: u32, pub chain: bool, pub interrupt_on_completion: bool, pub immediate_data: bool, pub direction: bool, } -impl TrbData for DataStageTrbData { +impl TrbData for DataStageTrb { /// Parse data of a Data Stage TRB. /// /// Only `TransferTrb::try_from` should call this function. @@ -981,8 +994,8 @@ impl TrbData for DataStageTrbData { let dp_bytes: [u8; 8] = trb_bytes[0..8].try_into().unwrap(); let data_pointer = u64::from_le_bytes(dp_bytes); - let tl_bytes: [u8; 2] = trb_bytes[8..10].try_into().unwrap(); - let transfer_length = u16::from_le_bytes(tl_bytes); + let tl_bytes: [u8; 4] = [trb_bytes[8], trb_bytes[9], trb_bytes[10] & 0x01, 0]; + let transfer_length = u32::from_le_bytes(tl_bytes); let chain = trb_bytes[12] & 0x10 != 0; let interrupt_on_completion = trb_bytes[12] & 0x20 != 0; @@ -1000,17 +1013,29 @@ impl TrbData for DataStageTrbData { } } +impl TrbDmaInfo for DataStageTrb { + fn data_pointer(&self) -> u64 { + self.data_pointer + } + fn transfer_length(&self) -> u32 { + self.transfer_length + } + fn has_immediate_data(&self) -> bool { + self.immediate_data + } +} + /// Status Stage TRB data structure. /// /// See XHCI specification Section 6.4.1.2.3 for detailed field descriptions. -#[derive(Debug, PartialEq, Eq)] -pub struct StatusStageTrbData { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StatusStageTrb { pub chain: bool, pub interrupt_on_completion: bool, pub direction: bool, } -impl TrbData for StatusStageTrbData { +impl TrbData for StatusStageTrb { /// Parse data of a Status Stage TRB. /// /// Only `TransferTrb::try_from` should call this function. @@ -1042,14 +1067,14 @@ impl TrbData for StatusStageTrbData { /// Event Data TRB data structure. /// /// See XHCI specification Section 6.4.4.2 for detailed field descriptions. -#[derive(Debug, PartialEq, Eq)] -pub struct EventDataTrbData { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EventDataTrb { pub event_data: u64, pub chain: bool, pub interrupt_on_completion: bool, } -impl TrbData for EventDataTrbData { +impl TrbData for EventDataTrb { /// Parse data of a Event Data TRB. /// /// Only `TransferTrb::try_from` should call this function. @@ -1092,6 +1117,217 @@ pub enum TrbParseError { RsvdZViolation, } +#[cfg(test)] +pub mod testutils { + use super::*; + + /// This Builder simplifies creating RawTrb and prevents some easy mistakes when + /// manually writing the correct value at the corresponding index of the buffers. + pub struct RawTrbBuilder { + pub address: u64, + pub buffer: RawTrbBuffer, + } + impl RawTrbBuilder { + const CH: u8 = 0x10; + const IOC: u8 = 0x20; + const IDT: u8 = 0x40; + const DIR: u8 = 0x1; + + pub fn new(address: u64) -> Self { + Self { + address, + buffer: [0; 16], + } + } + + /// setup stage trb exclusive field: bmRequestType + pub fn with_setup_type(mut self, value: u8) -> Self { + self.buffer[0] = value; + self + } + + /// setup stage trb exclusive field: wLength + pub fn with_setup_length(mut self, value: u16) -> Self { + let value_bytes: [u8; 2] = value.to_le_bytes(); + self.buffer[6..(2 + 6)].copy_from_slice(&value_bytes); + self + } + + /// first 8 bytes of the trb + pub fn with_data_field(mut self, value: u64) -> Self { + let value_bytes: [u8; 8] = value.to_le_bytes(); + self.buffer[..8].copy_from_slice(&value_bytes); + self + } + + /// byte index 8 & 9 & 1 Bit of 10th: TRB Transfer Length + pub fn with_length(mut self, length: u32) -> Self { + let length_bytes: [u8; 4] = length.to_le_bytes(); + self.buffer[8..(2 + 8)].copy_from_slice(&length_bytes[0..2]); + self.buffer[10] = length_bytes[2] & 0b1; + self + } + + /// chain bit + pub fn with_ch(mut self) -> Self { + self.buffer[12] |= Self::CH; + self + } + + /// interrupt on completion bit + pub fn with_ioc(mut self) -> Self { + self.buffer[12] |= Self::IOC; + self + } + + /// immediate data bit + pub fn with_idt(mut self) -> Self { + self.buffer[12] |= Self::IDT; + self + } + + /// trb type field + pub fn with_type(mut self, trb_type: u8) -> Self { + self.buffer[13] |= trb_type << 2; + self + } + + /// direction bit + /// + /// for data stage and status stage trb + /// + /// if DIR { IN } else { OUT } + pub fn with_dir(mut self) -> Self { + self.buffer[14] |= Self::DIR; + self + } + + /// absolute write to an arbitrary byte index + pub fn with_byte(mut self, index: usize, value: u8) -> Self { + assert!(index < self.buffer.len()); + self.buffer[index] = value; + self + } + + pub fn build(self) -> RawTrb { + RawTrb { + address: self.address, + buffer: self.buffer, + } + } + } + + mod tests { + use super::*; + + #[test] + fn build_normal_trb_as_raw_trb() { + let normal = RawTrbBuilder::new(0x10) + .with_data_field(0x1122334455667788) + .with_length(2048) + .with_ioc() + .with_ch() + .with_type(0x1) + .build(); + let expect = RawTrb { + address: 0x10, + buffer: [ + 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0, 0x8, 0, 0, 0x30, 0x4, 0, 0, + ], + }; + assert_eq!(normal, expect); + } + + #[test] + fn build_normal_trb_as_raw_trb_with_max_length() { + let normal = RawTrbBuilder::new(0x10) + .with_data_field(0x1122334455667788) + // this is the fields maximum value not the as per specification allowed maximum value of 0x1_0000 + .with_length(0x1_ffff) + .with_ioc() + .with_ch() + .with_type(0x1) + .build(); + let expect = RawTrb { + address: 0x10, + buffer: [ + 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xff, 0xff, 0x1, 0, 0x30, 0x4, + 0, 0, + ], + }; + assert_eq!(normal, expect); + } + + #[test] + fn build_control_in_setup_stage_as_raw_trb() { + let setup_stage = RawTrbBuilder::new(0x10) + .with_setup_type(0x80) + .with_setup_length(512) + .with_length(1024) + .with_idt() + .with_ioc() + .with_type(0x2) + .with_byte(14, 0x3) // TRT: IN Data Stage + .build(); + let expect = RawTrb { + address: 0x10, + buffer: [0x80, 0, 0, 0, 0, 0, 0, 0x2, 0, 0x4, 0, 0, 0x60, 0x8, 0x3, 0], + }; + assert_eq!(setup_stage, expect); + } + + #[test] + fn build_control_in_data_stage_as_raw_trb() { + let data_stage = RawTrbBuilder::new(0x10) + .with_data_field(0x112233445566) + .with_idt() + .with_ioc() + .with_type(0x3) + .with_dir() + .build(); + let expect = RawTrb { + address: 0x10, + buffer: [ + 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0, 0, 0, 0, 0, 0, 0x60, 0xc, 0x1, 0, + ], + }; + assert_eq!(data_stage, expect); + } + + #[test] + fn build_control_in_status_stage_as_raw_trb() { + let status_stage = RawTrbBuilder::new(0x10) + .with_ioc() + .with_ch() + .with_type(0x4) + .with_dir() + .build(); + let expect = RawTrb { + address: 0x10, + buffer: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x30, 0x10, 0x1, 0], + }; + assert_eq!(status_stage, expect); + } + + #[test] + fn build_event_data_as_raw_trb() { + let event_data = RawTrbBuilder::new(0x10) + .with_data_field(0xe4e117da7a) + .with_ioc() + .with_type(0x7) + .with_dir() + .build(); + let expect = RawTrb { + address: 0x10, + buffer: [ + 0x7a, 0xda, 0x17, 0xe1, 0xe4, 0, 0, 0, 0, 0, 0, 0, 0x20, 0x1c, 0x1, 0, + ], + }; + assert_eq!(event_data, expect); + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -1208,7 +1444,7 @@ mod tests { 0x11, 0x22, 0x44, 0x33, 0x66, 0x55, 0x88, 0x77, 0x12, 0x34, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, ]; - let expected = TransferTrbVariant::Normal(NormalTrbData { + let expected = TransferTrbVariant::Normal(NormalTrb { data_pointer: 0x7788556633442211, transfer_length: 0x3412, chain: true, @@ -1224,7 +1460,7 @@ mod tests { 0x11, 0x22, 0x44, 0x33, 0x66, 0x55, 0x88, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, ]; - let expected = TransferTrbVariant::SetupStage(SetupStageTrbData { + let expected = TransferTrbVariant::SetupStage(SetupStageTrb { request_type: 0x11, request: 0x22, value: 0x3344, @@ -1241,7 +1477,7 @@ mod tests { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, ]; - let expected = TransferTrbVariant::DataStage(DataStageTrbData { + let expected = TransferTrbVariant::DataStage(DataStageTrb { data_pointer: 0x1122334455667788, transfer_length: 0x0010, chain: false, @@ -1258,7 +1494,7 @@ mod tests { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10, 0x01, 0x00, ]; - let expected = TransferTrbVariant::StatusStage(StatusStageTrbData { + let expected = TransferTrbVariant::StatusStage(StatusStageTrb { chain: true, interrupt_on_completion: true, direction: true, @@ -1272,7 +1508,7 @@ mod tests { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x10, 0x00, 0x00, 0x00, 0x30, 0x1c, 0x00, 0x00, ]; - let expected = TransferTrbVariant::EventData(EventDataTrbData { + let expected = TransferTrbVariant::EventData(EventDataTrb { event_data: 0x1122334455667788, chain: true, interrupt_on_completion: true, diff --git a/src/device/xhci/usbrequest.rs b/src/device/xhci/usbrequest.rs index 5e009c93..4753c450 100644 --- a/src/device/xhci/usbrequest.rs +++ b/src/device/xhci/usbrequest.rs @@ -2,16 +2,10 @@ /// Represents a USB control request. /// -/// For documentation of the fields other than `address`, see Section "9.3 USB -/// Device Requests" in the USB 2.0 specification. -/// -/// A request without data is packaged in two TRBs (a Setup Stage and a -/// Status Stage). `data` should then be `None`. -/// -/// A request with data is packaged in three TRBs (a Setup Stage, a Data -/// Stage and a Status Stage). `data` should then contain the pointer -/// from the Data Stage). +/// See xhci specification chapter 6.4.1.2. /// +/// For additional documentation of the fields other than `address`, see Section "9.3 USB +/// Device Requests" in the USB 2.0 specification. #[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct UsbRequest { /// The guest address of the Status Stage of this request. @@ -22,22 +16,5 @@ pub struct UsbRequest { pub index: u16, pub length: u16, pub data_pointer: Option, - pub data: Option>, -} - -impl UsbRequest { - // so that the control endpoint handler can make a copy - // (to store the request between submit_trb and next_complete) - pub const fn clone_without_data(&self) -> Self { - Self { - address: self.address, - request_type: self.request_type, - request: self.request, - value: self.value, - index: self.index, - length: self.length, - data_pointer: self.data_pointer, - data: None, - } - } + pub data: Vec, }