use thiserror::Error;
use tracing::warn;
use super::super::pci::constants::xhci::rings::trb_types::{self, *};
pub type RawTrbBuffer = [u8; 16];
#[derive(Debug)]
pub struct RawTrb {
pub address: u64,
pub buffer: RawTrbBuffer,
}
pub const fn zeroed_trb_buffer() -> RawTrbBuffer {
[0; 16]
}
#[derive(Debug)]
pub enum EventTrb {
Transfer(TransferEventTrbData),
CommandCompletion(CommandCompletionEventTrbData),
PortStatusChange(PortStatusChangeEventTrbData),
}
impl EventTrb {
pub fn to_bytes(&self, cycle_bit: bool) -> RawTrbBuffer {
let mut trb_data = match self {
Self::Transfer(data) => data.to_bytes(),
Self::CommandCompletion(data) => data.to_bytes(),
Self::PortStatusChange(data) => data.to_bytes(),
};
trb_data[12] = (trb_data[12] & !0x1) | cycle_bit as u8;
trb_data
}
}
#[derive(Debug)]
pub struct CommandCompletionEventTrbData {
command_trb_pointer: u64,
command_completion_parameter: u32,
completion_code: CompletionCode,
slot_id: u8,
}
impl EventTrb {
pub fn new_command_completion_event_trb(
command_trb_pointer: u64,
command_completion_parameter: u32,
completion_code: CompletionCode,
slot_id: u8,
) -> Self {
assert_eq!(
0,
command_trb_pointer & 0x0f,
"command_trb_pointer has to be 16-byte-aligned."
);
assert_eq!(
0,
command_completion_parameter & 0xff000000,
"command_completion_parameter has to be a 24-bit value."
);
Self::CommandCompletion(CommandCompletionEventTrbData {
command_trb_pointer,
command_completion_parameter,
completion_code,
slot_id,
})
}
}
impl CommandCompletionEventTrbData {
fn to_bytes(&self) -> RawTrbBuffer {
let mut trb = zeroed_trb_buffer();
trb[0..8].copy_from_slice(&self.command_trb_pointer.to_le_bytes());
trb[8..11].copy_from_slice(&self.command_completion_parameter.to_le_bytes()[0..3]);
trb[11] = self.completion_code as u8;
trb[13] = COMMAND_COMPLETION_EVENT << 2;
trb[15] = self.slot_id;
trb
}
}
#[derive(Debug)]
pub struct PortStatusChangeEventTrbData {
port_id: u8,
}
impl EventTrb {
pub const fn new_port_status_change_event_trb(port_id: u8) -> Self {
Self::PortStatusChange(PortStatusChangeEventTrbData { port_id })
}
}
impl PortStatusChangeEventTrbData {
const fn to_bytes(&self) -> RawTrbBuffer {
let mut bytes = zeroed_trb_buffer();
bytes[3] = self.port_id;
bytes[11] = CompletionCode::Success as u8;
bytes[13] = PORT_STATUS_CHANGE_EVENT << 2;
bytes
}
}
#[derive(Debug)]
pub struct TransferEventTrbData {
trb_pointer: u64,
trb_transfer_length: u32,
completion_code: CompletionCode,
event_data: bool,
endpoint_id: u8,
slot_id: u8,
}
impl EventTrb {
pub const fn new_transfer_event_trb(
trb_pointer: u64,
trb_transfer_length: u32,
completion_code: CompletionCode,
event_data: bool,
endpoint_id: u8,
slot_id: u8,
) -> Self {
Self::Transfer(TransferEventTrbData {
trb_pointer,
trb_transfer_length,
completion_code,
event_data,
endpoint_id,
slot_id,
})
}
}
impl TransferEventTrbData {
fn to_bytes(&self) -> RawTrbBuffer {
let mut trb = zeroed_trb_buffer();
trb[0..8].copy_from_slice(&self.trb_pointer.to_le_bytes());
trb[8..11].copy_from_slice(&self.trb_transfer_length.to_le_bytes()[0..3]);
trb[11] = self.completion_code as u8;
trb[12] = (self.event_data as u8) << 2;
trb[13] = TRANSFER_EVENT << 2;
trb[14] = self.endpoint_id;
trb[15] = self.slot_id;
trb
}
}
#[allow(dead_code)]
#[derive(Debug, Copy, Clone)]
pub enum CompletionCode {
Invalid = 0,
Success,
DataBufferError,
BabbleDetectedError,
UsbTransactionError,
TrbError,
StallError,
ResourceError,
BandwidthError,
NoSlotsAvailableError,
InvalidStreamTypeError,
SlotNotEnabledError,
EndpointNotEnabledError,
ShortPacket,
RingUnderrun,
RingOverrun,
VfEventRingFullError,
ParameterError,
BandwidthOverrunError,
ContextStateError,
NoPingResponseError,
EventRingFullError,
IncompatibleDeviceError,
MissedServiceError,
CommandRingStopped,
CommandAborted,
Stopped,
StoppedLengthInvalid,
StoppedShortedPacket,
MaxExitLatencyTooLargeError,
Reserved,
IsochBufferOverrun,
EventLostError,
UndefinedError,
InvalidStreamIdError,
SecondaryBandwidthError,
SplitTransactionError,
}
trait TrbData: Sized {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError>;
}
trait TrbVariant: Sized {
fn unrecognized(trb_bytes: RawTrbBuffer, err: TrbParseError) -> Self;
}
impl TrbVariant for CommandTrbVariant {
fn unrecognized(trb_bytes: RawTrbBuffer, err: TrbParseError) -> Self {
Self::Unrecognized(trb_bytes, err)
}
}
impl TrbVariant for TransferTrbVariant {
fn unrecognized(trb_bytes: RawTrbBuffer, err: TrbParseError) -> Self {
Self::Unrecognized(trb_bytes, err)
}
}
fn parse<O, I, F>(variant_constructor: F, trb_bytes: RawTrbBuffer) -> O
where
O: TrbVariant,
I: TrbData,
F: Fn(I) -> O,
{
I::parse(trb_bytes).map_or_else(|err| O::unrecognized(trb_bytes, err), variant_constructor)
}
#[derive(Debug, PartialEq, Eq)]
pub struct CommandTrb {
pub address: u64,
pub variant: CommandTrbVariant,
}
#[derive(Debug, PartialEq, Eq)]
pub enum CommandTrbVariant {
EnableSlot,
DisableSlot(DisableSlotCommandTrbData),
AddressDevice(AddressDeviceCommandTrbData),
ConfigureEndpoint(ConfigureEndpointCommandTrbData),
EvaluateContext(EvaluateContextCommandTrbData),
ResetEndpoint(ResetEndpointCommandTrbData),
StopEndpoint(StopEndpointCommandTrbData),
SetTrDequeuePointer(SetTrDequeuePointerCommandTrbData),
ResetDevice(ResetDeviceCommandTrbData),
ForceHeader,
NoOp,
Unrecognized(RawTrbBuffer, TrbParseError),
}
impl CommandTrbVariant {
pub fn parse(bytes: RawTrbBuffer) -> Self {
let trb_type = bytes[13] >> 2;
match trb_type {
trb_types::ENABLE_SLOT_COMMAND => Self::EnableSlot,
trb_types::DISABLE_SLOT_COMMAND => parse(Self::DisableSlot, bytes),
trb_types::ADDRESS_DEVICE_COMMAND => parse(Self::AddressDevice, bytes),
trb_types::CONFIGURE_ENDPOINT_COMMAND => parse(Self::ConfigureEndpoint, bytes),
trb_types::EVALUATE_CONTEXT_COMMAND => parse(Self::EvaluateContext, bytes),
trb_types::RESET_ENDPOINT_COMMAND => parse(Self::ResetEndpoint, bytes),
trb_types::STOP_ENDPOINT_COMMAND => parse(Self::StopEndpoint, bytes),
trb_types::SET_TR_DEQUEUE_POINTER_COMMAND => parse(Self::SetTrDequeuePointer, bytes),
trb_types::RESET_DEVICE_COMMAND => parse(Self::ResetDevice, bytes),
trb_types::FORCE_EVENT_COMMAND => Self::Unrecognized(
bytes,
TrbParseError::UnsupportedOptionalCommand(18, "Force Event Command".to_string()),
),
trb_types::NEGOTIATE_BANDWIDTH_COMMAND => Self::Unrecognized(
bytes,
TrbParseError::UnsupportedOptionalCommand(
19,
"Negotiate Bandwidth Command".to_string(),
),
),
trb_types::SET_LATENCY_TOLERANCE_VALUE_COMMAND => Self::Unrecognized(
bytes,
TrbParseError::UnsupportedOptionalCommand(
20,
"Set Latency Tolerance Value Command".to_string(),
),
),
trb_types::GET_PORT_BANDWIDTH_COMMAND => Self::Unrecognized(
bytes,
TrbParseError::UnsupportedOptionalCommand(
21,
"Get Port Bandwidth Command".to_string(),
),
),
trb_types::FORCE_HEADER_COMMAND => Self::ForceHeader,
trb_types::NO_OP_COMMAND => Self::NoOp,
trb_type => Self::Unrecognized(bytes, TrbParseError::UnknownTrbType(trb_type)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LinkTrb {
pub ring_segment_pointer: u64,
pub toggle_cycle: bool,
}
impl LinkTrb {
pub fn parse(trb_bytes: RawTrbBuffer) -> Option<Self> {
let trb_type = trb_bytes[13] >> 2;
if trb_type != trb_types::LINK {
return None;
}
let rsp_bytes: [u8; 8] = trb_bytes[0..8].try_into().unwrap();
let mut ring_segment_pointer = u64::from_le_bytes(rsp_bytes);
let toggle_cycle = trb_bytes[12] & 0x2 != 0;
if ring_segment_pointer & 0xf != 0 {
warn!("RsvdZ violation in link TRB");
ring_segment_pointer &= !0xf;
}
Some(Self {
ring_segment_pointer,
toggle_cycle,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct DisableSlotCommandTrbData {
pub slot_id: u8,
}
impl TrbData for DisableSlotCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::DISABLE_SLOT_COMMAND,
trb_type,
"DisableSlotCommandTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let slot_id = trb_bytes[15];
Ok(Self { slot_id })
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct AddressDeviceCommandTrbData {
pub input_context_pointer: u64,
pub block_set_address_request: bool,
pub slot_id: u8,
}
impl TrbData for AddressDeviceCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::ADDRESS_DEVICE_COMMAND,
trb_type,
"AddressDeviceCommandTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let icp_bytes: [u8; 8] = trb_bytes[0..8].try_into().unwrap();
let input_context_pointer = u64::from_le_bytes(icp_bytes);
if input_context_pointer & 0xf != 0 {
return Err(TrbParseError::RsvdZViolation);
}
let block_set_address_request = trb_bytes[13] & 0x2 != 0;
let slot_id = trb_bytes[15];
Ok(Self {
input_context_pointer,
block_set_address_request,
slot_id,
})
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct ConfigureEndpointCommandTrbData {
pub input_context_pointer: u64,
pub deconfigure: bool,
pub slot_id: u8,
}
impl TrbData for ConfigureEndpointCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::CONFIGURE_ENDPOINT_COMMAND,
trb_type,
"ConfigureEndpointCommandTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let icp_bytes: [u8; 8] = trb_bytes[0..8].try_into().unwrap();
let input_context_pointer = u64::from_le_bytes(icp_bytes);
if input_context_pointer & 0xf != 0 {
return Err(TrbParseError::RsvdZViolation);
}
let deconfigure = trb_bytes[13] & 0x2 != 0;
let slot_id = trb_bytes[15];
Ok(Self {
input_context_pointer,
deconfigure,
slot_id,
})
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct EvaluateContextCommandTrbData {
pub input_context_pointer: u64,
pub slot_id: u8,
}
impl TrbData for EvaluateContextCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::EVALUATE_CONTEXT_COMMAND,
trb_type,
"EvaluateContextCommandTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let icp_bytes: [u8; 8] = trb_bytes[0..8].try_into().unwrap();
let input_context_pointer = u64::from_le_bytes(icp_bytes);
if input_context_pointer & 0xf != 0 {
return Err(TrbParseError::RsvdZViolation);
}
let slot_id = trb_bytes[15];
Ok(Self {
input_context_pointer,
slot_id,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ResetEndpointCommandTrbData {
pub slot_id: u8,
pub endpoint_id: u8,
}
impl TrbData for ResetEndpointCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::RESET_ENDPOINT_COMMAND,
trb_type,
"ResetEndpointCommandTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let endpoint_id = trb_bytes[14] & 0x1f;
let slot_id = trb_bytes[15];
Ok(Self {
slot_id,
endpoint_id,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct StopEndpointCommandTrbData {
pub endpoint_id: u8,
pub slot_id: u8,
}
impl TrbData for StopEndpointCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::STOP_ENDPOINT_COMMAND,
trb_type,
"StopEndpointCommandTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let endpoint_id = trb_bytes[14] & 0x1f;
let slot_id = trb_bytes[15];
Ok(Self {
endpoint_id,
slot_id,
})
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct SetTrDequeuePointerCommandTrbData {
pub dequeue_cycle_state: bool,
pub stream_context_type: u8,
pub dequeue_pointer: u64,
pub stream_id: u16,
pub endpoint_id: u8,
pub slot_id: u8,
}
impl TrbData for SetTrDequeuePointerCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let dequeue_cycle_state = trb_bytes[0] & 1 != 0;
let stream_context_type = trb_bytes[0] & 0xe;
let mut dequeue_pointer_bytes: [u8; 8] = trb_bytes[0..8].try_into().unwrap();
dequeue_pointer_bytes[0] &= 0xf0;
let dequeue_pointer = u64::from_le_bytes(dequeue_pointer_bytes);
let stream_id_bytes: [u8; 2] = trb_bytes[11..13].try_into().unwrap();
let stream_id = u16::from_le_bytes(stream_id_bytes);
let endpoint_id = trb_bytes[14] & 0x1f;
let slot_id = trb_bytes[15];
Ok(Self {
dequeue_cycle_state,
stream_context_type,
dequeue_pointer,
stream_id,
endpoint_id,
slot_id,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ResetDeviceCommandTrbData {
pub slot_id: u8,
}
impl TrbData for ResetDeviceCommandTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::RESET_DEVICE_COMMAND,
trb_type,
"ResetDeviceCommandTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let slot_id = trb_bytes[15];
Ok(Self { slot_id })
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct TransferTrb {
pub address: u64,
pub variant: TransferTrbVariant,
}
#[derive(Debug, PartialEq, Eq)]
pub enum TransferTrbVariant {
Normal(NormalTrbData),
SetupStage(SetupStageTrbData),
DataStage(DataStageTrbData),
StatusStage(StatusStageTrbData),
Isoch,
EventData(EventDataTrbData),
NoOp,
#[allow(unused)]
Unrecognized(RawTrbBuffer, TrbParseError),
}
impl TransferTrbVariant {
pub fn parse(bytes: RawTrbBuffer) -> Self {
let trb_type = bytes[13] >> 2;
match trb_type {
trb_types::NORMAL => parse(Self::Normal, bytes),
trb_types::SETUP_STAGE => parse(Self::SetupStage, bytes),
trb_types::DATA_STAGE => parse(Self::DataStage, bytes),
trb_types::STATUS_STAGE => parse(Self::StatusStage, bytes),
trb_types::ISOCH => Self::Isoch,
trb_types::EVENT_DATA => parse(Self::EventData, bytes),
trb_types::NO_OP => Self::NoOp,
trb_type => Self::Unrecognized(bytes, TrbParseError::UnknownTrbType(trb_type)),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct NormalTrbData {
pub data_pointer: u64,
pub transfer_length: u32,
pub chain: bool,
pub interrupt_on_completion: bool,
pub immediate_data: bool,
}
impl TrbData for NormalTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::NORMAL,
trb_type,
"SetupStageTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
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; 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;
let immediate_data = trb_bytes[12] & 0x40 != 0;
Ok(Self {
data_pointer,
transfer_length,
chain,
interrupt_on_completion,
immediate_data,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct SetupStageTrbData {
pub request_type: u8,
pub request: u8,
pub value: u16,
pub index: u16,
pub length: u16,
pub interrupt_on_completion: bool,
}
impl TrbData for SetupStageTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::SETUP_STAGE,
trb_type,
"SetupStageTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let request_type = trb_bytes[0];
let request = trb_bytes[1];
let value = trb_bytes[2] as u16 + ((trb_bytes[3] as u16) << 8);
let index = trb_bytes[4] as u16 + ((trb_bytes[5] as u16) << 8);
let length = trb_bytes[6] as u16 + ((trb_bytes[7] as u16) << 8);
let interrupt_on_completion = trb_bytes[12] & 0x20 != 0;
Ok(Self {
request_type,
request,
value,
index,
length,
interrupt_on_completion,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct DataStageTrbData {
pub data_pointer: u64,
pub transfer_length: u16,
pub chain: bool,
pub interrupt_on_completion: bool,
pub immediate_data: bool,
pub direction: bool,
}
impl TrbData for DataStageTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::DATA_STAGE,
trb_type,
"DataStageTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
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 chain = trb_bytes[12] & 0x10 != 0;
let interrupt_on_completion = trb_bytes[12] & 0x20 != 0;
let immediate_data = trb_bytes[12] & 0x40 != 0;
let direction = trb_bytes[14] & 0x1 != 0;
Ok(Self {
data_pointer,
transfer_length,
chain,
interrupt_on_completion,
immediate_data,
direction,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct StatusStageTrbData {
pub chain: bool,
pub interrupt_on_completion: bool,
pub direction: bool,
}
impl TrbData for StatusStageTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::STATUS_STAGE,
trb_type,
"StatusStageTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let chain = trb_bytes[12] & 0x10 != 0;
let interrupt_on_completion = trb_bytes[12] & 0x20 != 0;
let direction = trb_bytes[14] & 0x1 != 0;
Ok(Self {
chain,
interrupt_on_completion,
direction,
})
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct EventDataTrbData {
pub event_data: u64,
pub chain: bool,
pub interrupt_on_completion: bool,
}
impl TrbData for EventDataTrbData {
fn parse(trb_bytes: RawTrbBuffer) -> Result<Self, TrbParseError> {
let trb_type = trb_bytes[13] >> 2;
assert_eq!(
trb_types::EVENT_DATA,
trb_type,
"EventDataTrbData::parse called on TRB data with incorrect TRB type ({trb_type:#x})"
);
let ed_bytes: [u8; 8] = trb_bytes[0..8].try_into().unwrap();
let event_data = u64::from_le_bytes(ed_bytes);
let chain = trb_bytes[12] & 0x10 != 0;
let interrupt_on_completion = trb_bytes[12] & 0x20 != 0;
Ok(Self {
event_data,
chain,
interrupt_on_completion,
})
}
}
#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum TrbParseError {
#[error("TRB type {0} refers to \"{1}\", which is optional and not supported.")]
UnsupportedOptionalCommand(u8, String),
#[error("TRB type {0} does not refer to any command.")]
UnknownTrbType(u8),
#[error("Detected a non-zero value in a RsvdZ field")]
RsvdZViolation,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_link_trb() {
let trb_bytes = [
0x80, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x18,
0x00, 0x00,
];
let expected = Some(LinkTrb {
ring_segment_pointer: 0x1122334455667780,
toggle_cycle: true,
});
assert_eq!(LinkTrb::parse(trb_bytes), expected);
}
#[test]
fn parse_enable_slot_command_trb() {
let trb_bytes = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24,
0x00, 0x00,
];
let expected = CommandTrbVariant::EnableSlot;
assert_eq!(CommandTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn parse_address_device_command_trb() {
let trb_bytes = [
0x80, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x2e,
0x00, 0x13,
];
let expected = CommandTrbVariant::AddressDevice(AddressDeviceCommandTrbData {
input_context_pointer: 0x1122334455667780,
block_set_address_request: true,
slot_id: 0x13,
});
assert_eq!(CommandTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn parse_configure_endpoint_command_trb() {
let trb_bytes = [
0x80, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02, 0x32,
0x00, 0x13,
];
let expected = CommandTrbVariant::ConfigureEndpoint(ConfigureEndpointCommandTrbData {
input_context_pointer: 0x1122334455667780,
deconfigure: true,
slot_id: 0x13,
});
assert_eq!(CommandTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn parse_stop_endpoint_command_trb() {
let trb_bytes = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
0x02, 0x10,
];
let expected = CommandTrbVariant::StopEndpoint(StopEndpointCommandTrbData {
endpoint_id: 0x02,
slot_id: 0x10,
});
assert_eq!(CommandTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn parse_reset_endpoint_command_trb() {
let trb_bytes = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
0x02, 0x10,
];
let expected = CommandTrbVariant::ResetEndpoint(ResetEndpointCommandTrbData {
endpoint_id: 0x02,
slot_id: 0x10,
});
assert_eq!(CommandTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn command_completion_event_trb() {
let trb = EventTrb::new_command_completion_event_trb(
0x1122334455667780,
0xaabbcc,
CompletionCode::Success,
2,
);
assert_eq!(
[
0x80, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xcc, 0xbb, 0xaa, 0x01, 0x01, 0x84,
0x00, 0x02,
],
trb.to_bytes(true),
);
}
#[test]
fn port_status_change_event_trb() {
let trb = EventTrb::new_port_status_change_event_trb(2);
assert_eq!(
[
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x88,
0x00, 0x00,
],
trb.to_bytes(true),
);
}
#[test]
fn test_parse_normal_trb() {
let trb_bytes = [
0x11, 0x22, 0x44, 0x33, 0x66, 0x55, 0x88, 0x77, 0x12, 0x34, 0x00, 0x00, 0x30, 0x04,
0x00, 0x00,
];
let expected = TransferTrbVariant::Normal(NormalTrbData {
data_pointer: 0x7788556633442211,
transfer_length: 0x3412,
chain: true,
interrupt_on_completion: true,
immediate_data: false,
});
assert_eq!(TransferTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn test_parse_setup_stage_trb() {
let trb_bytes = [
0x11, 0x22, 0x44, 0x33, 0x66, 0x55, 0x88, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
0x00, 0x00,
];
let expected = TransferTrbVariant::SetupStage(SetupStageTrbData {
request_type: 0x11,
request: 0x22,
value: 0x3344,
index: 0x5566,
length: 0x7788,
interrupt_on_completion: false,
});
assert_eq!(TransferTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn test_parse_data_stage_trb() {
let trb_bytes = [
0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0c,
0x00, 0x00,
];
let expected = TransferTrbVariant::DataStage(DataStageTrbData {
data_pointer: 0x1122334455667788,
transfer_length: 0x0010,
chain: false,
interrupt_on_completion: false,
immediate_data: false,
direction: false,
});
assert_eq!(TransferTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn test_parse_status_stage_trb() {
let trb_bytes = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x10,
0x01, 0x00,
];
let expected = TransferTrbVariant::StatusStage(StatusStageTrbData {
chain: true,
interrupt_on_completion: true,
direction: true,
});
assert_eq!(TransferTrbVariant::parse(trb_bytes), expected);
}
#[test]
fn test_parse_event_data_trb() {
let trb_bytes = [
0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x10, 0x00, 0x00, 0x00, 0x30, 0x1c,
0x00, 0x00,
];
let expected = TransferTrbVariant::EventData(EventDataTrbData {
event_data: 0x1122334455667788,
chain: true,
interrupt_on_completion: true,
});
assert_eq!(TransferTrbVariant::parse(trb_bytes), expected);
}
}