summary refs log blame commit diff
path: root/devices/src/usb/xhci/xhci_abi.rs
blob: e9be3c330f3b0ef78286d559744f61ff88cd1c1d (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                                         

                                      
                         
                              
                           
 
                
                
                                  









                                                          
                                                                                      




                                                                          

                               
 

                                     
 








































































                                                                               

     


                                           


     











                                                                 
          
                                                                         
                                                                   
                                                                    
                                

                                                  

                                    

                                                      

                                   

                                                     

                                     

                                                       

                               

                                                 

                              

                                                

                                   

                                                     

                              

                                                
             
                                                                                               
                                            

                                                              

                                              

                                                                

                                                  

                                                                    

                                                

                                                                  

                                              

                                                                

                                             

                                                               

                                                    

                                                                      

                                            

                                                              
             
                                                                                  
                                       

                                                         

                                                

                                                                  

                                               

                                                                 


             
 
 









                                                            
                      
                                                 







                                                                                   
                       
          








                                                                     
                                                           
                                                                   






                                  

                                        



                                
                                                  
                                                         
                                                                   
                                                                                            
                                                                   
             
                       








                                                                   





                                                                          
                                                   
                                                  
                                                    
                                                                   
                                                                                            
                                                                       
             
                           



         













































































































































































































































































































                                          
 



                               
 


                                          
 


                                        
 

                                            

 

                                           

 

                                             

 

                                       

 

                                      

 

                                           

 

                                      

 

                                                    

 


                                                      
 

                                                          

 

                                                        

 













                                                            

 




















                                                                                             

     





















                                                                          


     









































































                                                         










                                                         



























































































































                                                                                 
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use bit_field::Error as BitFieldError;
use bit_field::*;
use data_model::DataInit;
use std::fmt::{self, Display};
use sys_util::GuestAddress;

#[derive(Debug)]
pub enum Error {
    UnknownTrbType(BitFieldError),
    CannotCastTrb,
}

type Result<T> = std::result::Result<T, Error>;

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match self {
            UnknownTrbType(e) => write!(f, "we got an unknown trb type value: {}", e),
            CannotCastTrb => write!(f, "cannot cast trb from raw memory"),
        }
    }
}

// Fixed size of all TRB types.
const TRB_SIZE: usize = 16;

// Size of segment table.
const SEGMENT_TABLE_SIZE: usize = 16;

/// All kinds of trb.
#[bitfield]
#[bits = 6]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum TrbType {
    Reserved = 0,
    Normal = 1,
    SetupStage = 2,
    DataStage = 3,
    StatusStage = 4,
    Isoch = 5,
    Link = 6,
    EventData = 7,
    Noop = 8,
    EnableSlotCommand = 9,
    DisableSlotCommand = 10,
    AddressDeviceCommand = 11,
    ConfigureEndpointCommand = 12,
    EvaluateContextCommand = 13,
    ResetEndpointCommand = 14,
    StopEndpointCommand = 15,
    SetTRDequeuePointerCommand = 16,
    ResetDeviceCommand = 17,
    NoopCommand = 23,
    TransferEvent = 32,
    CommandCompletionEvent = 33,
    PortStatusChangeEvent = 34,
}

/// Completion code of trb types.
#[bitfield]
#[bits = 8]
#[derive(PartialEq, Debug)]
pub enum TrbCompletionCode {
    Success = 1,
    TransactionError = 4,
    TrbError = 5,
    NoSlotsAvailableError = 9,
    SlotNotEnabledError = 11,
    ShortPacket = 13,
    ContextStateError = 19,
}

/// State of device slot.
#[bitfield]
#[bits = 5]
#[derive(PartialEq, Debug)]
pub enum DeviceSlotState {
    // The same value (0) is used for both the enabled and disabled states. See
    // xhci spec table 60.
    DisabledOrEnabled = 0,
    Default = 1,
    Addressed = 2,
    Configured = 3,
}

/// State of endpoint.
#[bitfield]
#[bits = 3]
#[derive(PartialEq, Debug)]
pub enum EndpointState {
    Disabled = 0,
    Running = 1,
}

#[bitfield]
#[bits = 60]
#[derive(PartialEq, Debug)]
pub struct DequeuePtr(u64);

impl DequeuePtr {
    pub fn new(addr: GuestAddress) -> Self {
        DequeuePtr(addr.0 >> 4)
    }

    // Get the guest physical address.
    pub fn get_gpa(&self) -> GuestAddress {
        GuestAddress(self.0 << 4)
    }
}

// Generic TRB struct containing only fields common to all types.
#[bitfield]
#[derive(Clone, Copy, PartialEq)]
pub struct Trb {
    parameter: B64,
    status: B32,
    cycle: bool,
    flags: B9,
    trb_type: TrbType,
    control: B16,
}

impl Trb {
    fn fmt_helper(&self, f: &mut fmt::Formatter) -> Result<fmt::Result> {
        match self.get_trb_type().map_err(Error::UnknownTrbType)? {
            TrbType::Reserved => Ok(write!(f, "reserved trb type")),
            TrbType::Normal => {
                let t = self.cast::<NormalTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::SetupStage => {
                let t = self.cast::<SetupStageTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::DataStage => {
                let t = self.cast::<DataStageTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::StatusStage => {
                let t = self.cast::<StatusStageTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::Isoch => {
                let t = self.cast::<IsochTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::Link => {
                let t = self.cast::<LinkTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::EventData => {
                let t = self.cast::<EventDataTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::Noop => {
                let t = self.cast::<NoopTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::EnableSlotCommand => Ok(write!(f, "trb: enable slot command {:?}", self)),
            TrbType::DisableSlotCommand => {
                let t = self.cast::<DisableSlotCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::AddressDeviceCommand => {
                let t = self.cast::<AddressDeviceCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::ConfigureEndpointCommand => {
                let t = self.cast::<ConfigureEndpointCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::EvaluateContextCommand => {
                let t = self.cast::<EvaluateContextCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::ResetEndpointCommand => {
                let t = self.cast::<ResetEndpointCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::StopEndpointCommand => {
                let t = self.cast::<StopEndpointCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::SetTRDequeuePointerCommand => {
                let t = self.cast::<SetTRDequeuePointerCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::ResetDeviceCommand => {
                let t = self.cast::<ResetDeviceCommandTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::NoopCommand => Ok(write!(f, "trb: noop command {:?}", self)),
            TrbType::TransferEvent => {
                let t = self.cast::<TransferEventTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::CommandCompletionEvent => {
                let t = self.cast::<CommandCompletionEventTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
            TrbType::PortStatusChangeEvent => {
                let t = self.cast::<PortStatusChangeEventTrb>()?;
                Ok(write!(f, "trb: {:?}", t))
            }
        }
    }
}

impl Display for Trb {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.fmt_helper(f) {
            Ok(f) => f,
            Err(e) => write!(f, "fail to format trb {}", e),
        }
    }
}

impl Trb {
    /// Get chain bit.
    pub fn get_chain_bit(&self) -> Result<bool> {
        Ok(match self.get_trb_type() {
            Ok(TrbType::Normal) => self.cast::<NormalTrb>()?.get_chain(),
            Ok(TrbType::DataStage) => self.cast::<DataStageTrb>()?.get_chain(),
            Ok(TrbType::StatusStage) => self.cast::<StatusStageTrb>()?.get_chain(),
            Ok(TrbType::Isoch) => self.cast::<IsochTrb>()?.get_chain(),
            Ok(TrbType::Noop) => self.cast::<NoopTrb>()?.get_chain(),
            Ok(TrbType::Link) => self.cast::<LinkTrb>()?.get_chain(),
            Ok(TrbType::EventData) => self.cast::<EventDataTrb>()?.get_chain(),
            _ => false,
        })
    }

    /// Get interrupt target.
    pub fn interrupter_target(&self) -> u8 {
        const STATUS_INTERRUPTER_TARGET_OFFSET: u8 = 22;
        (self.get_status() >> STATUS_INTERRUPTER_TARGET_OFFSET) as u8
    }

    /// Only some of trb types could appear in transfer ring.
    pub fn can_be_in_transfer_ring(&self) -> Result<bool> {
        match self.get_trb_type().map_err(Error::UnknownTrbType)? {
            TrbType::Normal
            | TrbType::SetupStage
            | TrbType::DataStage
            | TrbType::StatusStage
            | TrbType::Isoch
            | TrbType::Link
            | TrbType::EventData
            | TrbType::Noop => Ok(true),
            _ => Ok(false),
        }
    }

    /// Length of this transfer.
    pub fn transfer_length(&self) -> Result<u32> {
        const STATUS_TRANSFER_LENGTH_MASK: u32 = 0x1ffff;
        match self.get_trb_type().map_err(Error::UnknownTrbType)? {
            TrbType::Normal | TrbType::SetupStage | TrbType::DataStage | TrbType::Isoch => {
                Ok(self.get_status() & STATUS_TRANSFER_LENGTH_MASK)
            }
            _ => Ok(0),
        }
    }

    /// Returns true if interrupt is required on completion.
    pub fn interrupt_on_completion(&self) -> bool {
        const FLAGS_INTERRUPT_ON_COMPLETION_MASK: u16 = 0x10;
        (self.get_flags() & FLAGS_INTERRUPT_ON_COMPLETION_MASK) > 0
    }

    /// Returns true if interrupt is required on transfer of short packet.
    pub fn interrupt_on_short_packet(&self) -> bool {
        const FLAGS_INTERRUPT_ON_SHORT_PACKET: u16 = 0x2;
        (self.get_flags() & FLAGS_INTERRUPT_ON_SHORT_PACKET) > 0
    }

    /// Returns true if this trb is immediate data.
    pub fn immediate_data(&self) -> Result<bool> {
        const FLAGS_IMMEDIATE_DATA_MASK: u16 = 0x20;
        match self.get_trb_type().map_err(Error::UnknownTrbType)? {
            TrbType::Normal | TrbType::SetupStage | TrbType::DataStage | TrbType::Isoch => {
                Ok((self.get_flags() & FLAGS_IMMEDIATE_DATA_MASK) != 0)
            }
            _ => Ok(false),
        }
    }
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct NormalTrb {
    data_buffer: B64,
    trb_transfer_length: B17,
    td_size: B5,
    interrupter_target: B10,
    cycle: bool,
    evaluate_next_trb: B1,
    interrupt_on_short_packet: B1,
    no_snoop: B1,
    chain: bool,
    interrupt_on_completion: B1,
    immediate_data: B1,
    reserved: B2,
    block_event_interrupt: B1,
    trb_type: TrbType,
    reserved1: B16,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct SetupStageTrb {
    request_type: B8,
    request: B8,
    value: B16,
    index: B16,
    length: B16,
    trb_transfer_length: B17,
    reserved0: B5,
    interrupter_target: B10,
    cycle: bool,
    reserved1: B4,
    interrupt_on_completion: B1,
    immediate_data: B1,
    reserved2: B3,
    trb_type: TrbType,
    transfer_type: B2,
    reserved3: B14,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct DataStageTrb {
    data_buffer_pointer: B64,
    trb_transfer_length: B17,
    td_size: B5,
    interrupter_target: B10,
    cycle: bool,
    evaluate_next_trb: B1,
    interrupt_on_short_packet: B1,
    no_snoop: B1,
    chain: bool,
    interrupt_on_completion: B1,
    immediate_data: B1,
    reserved0: B3,
    trb_type: TrbType,
    direction: B1,
    reserved1: B15,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct StatusStageTrb {
    reserved0: B64,
    reserved1: B22,
    interrupter_target: B10,
    cycle: bool,
    evaluate_next_trb: B1,
    reserved2: B2,
    chain: bool,
    interrupt_on_completion: B1,
    reserved3: B4,
    trb_type: TrbType,
    direction: B1,
    reserved4: B15,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct IsochTrb {
    data_buffer_pointer: B64,
    trb_transfer_length: B17,
    td_size: B5,
    interrupter_target: B10,
    cycle: bool,
    evaulate_nex_trb: B1,
    interrupt_on_short_packet: B1,
    no_snoop: B1,
    chain: bool,
    interrupt_on_completion: B1,
    immediate_data: B1,
    transfer_burst_count: B2,
    block_event_interrupt: B1,
    trb_type: TrbType,
    tlbpc: B4,
    frame_id: B11,
    sia: B1,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct LinkTrb {
    ring_segment_pointer: B64,
    reserved0: B22,
    interrupter_target: B10,
    cycle: bool,
    toggle_cycle: bool,
    reserved1: B2,
    chain: bool,
    interrupt_on_completion: bool,
    reserved2: B4,
    trb_type: TrbType,
    reserved3: B16,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct EventDataTrb {
    event_data: B64,
    reserved0: B22,
    interrupter_target: B10,
    cycle: bool,
    evaluate_next_trb: B1,
    reserved1: B2,
    chain: bool,
    interrupt_on_completion: B1,
    reserved2: B3,
    block_event_interrupt: B1,
    trb_type: TrbType,
    reserved3: B16,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct NoopTrb {
    reserved0: B64,
    reserved1: B22,
    interrupter_target: B10,
    cycle: bool,
    evaluate_next_trb: B1,
    reserved2: B2,
    chain: bool,
    interrupt_on_completion: B1,
    reserved3: B4,
    trb_type: TrbType,
    reserved4: B16,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct DisableSlotCommandTrb {
    reserved0: B32,
    reserved1: B32,
    reserved2: B32,
    cycle: bool,
    reserved3: B9,
    trb_type: TrbType,
    reserved4: B8,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct AddressDeviceCommandTrb {
    input_context_pointer: B64,
    reserved: B32,
    cycle: bool,
    reserved2: B8,
    block_set_address_request: bool,
    trb_type: TrbType,
    reserved3: B8,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct ConfigureEndpointCommandTrb {
    input_context_pointer: B64,
    reserved0: B32,
    cycle: bool,
    reserved1: B8,
    deconfigure: bool,
    trb_type: TrbType,
    reserved2: B8,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct EvaluateContextCommandTrb {
    input_context_pointer: B64,
    reserved0: B32,
    cycle: bool,
    reserved1: B9,
    trb_type: TrbType,
    reserved2: B8,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct ResetEndpointCommandTrb {
    reserved0: B32,
    reserved1: B32,
    reserved2: B32,
    cycle: bool,
    reserved3: B8,
    transfer_state_preserve: B1,
    trb_type: TrbType,
    endpoint_id: B5,
    reserved4: B3,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct StopEndpointCommandTrb {
    reserved0: B32,
    reserved1: B32,
    reserved2: B32,
    cycle: bool,
    reserved3: B9,
    trb_type: TrbType,
    endpoint_id: B5,
    reserved4: B2,
    suspend: B1,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct SetTRDequeuePointerCommandTrb {
    dequeue_cycle_state: bool,
    stream_context_type: B3,
    dequeue_ptr: DequeuePtr,
    reserved0: B16,
    stream_id: B16,
    cycle: bool,
    reserved1: B9,
    trb_type: TrbType,
    endpoint_id: B5,
    reserved3: B2,
    suspend: B1,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct ResetDeviceCommandTrb {
    reserved0: B32,
    reserved1: B32,
    reserved2: B32,
    cycle: bool,
    reserved3: B9,
    trb_type: TrbType,
    reserved4: B8,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct TransferEventTrb {
    trb_pointer: B64,
    trb_transfer_length: B24,
    completion_code: TrbCompletionCode,
    cycle: bool,
    reserved0: B1,
    event_data: B1,
    reserved1: B7,
    trb_type: TrbType,
    endpoint_id: B5,
    reserved2: B3,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct CommandCompletionEventTrb {
    trb_pointer: B64,
    command_completion_parameter: B24,
    completion_code: TrbCompletionCode,
    cycle: bool,
    reserved: B9,
    trb_type: TrbType,
    vf_id: B8,
    slot_id: B8,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct PortStatusChangeEventTrb {
    reserved0: B24,
    port_id: B8,
    reserved1: B32,
    reserved2: B24,
    completion_code: TrbCompletionCode,
    cycle: bool,
    reserved3: B9,
    trb_type: TrbType,
    reserved4: B16,
}

/// Associate real type of trb.
pub trait TypedTrb {
    const TY: TrbType;
}

impl TypedTrb for Trb {
    const TY: TrbType = TrbType::Reserved;
}

impl TypedTrb for NormalTrb {
    const TY: TrbType = TrbType::Normal;
}

impl TypedTrb for SetupStageTrb {
    const TY: TrbType = TrbType::SetupStage;
}

impl TypedTrb for DataStageTrb {
    const TY: TrbType = TrbType::DataStage;
}

impl TypedTrb for StatusStageTrb {
    const TY: TrbType = TrbType::StatusStage;
}

impl TypedTrb for IsochTrb {
    const TY: TrbType = TrbType::Isoch;
}

impl TypedTrb for LinkTrb {
    const TY: TrbType = TrbType::Link;
}

impl TypedTrb for EventDataTrb {
    const TY: TrbType = TrbType::EventData;
}

impl TypedTrb for NoopTrb {
    const TY: TrbType = TrbType::Noop;
}

impl TypedTrb for DisableSlotCommandTrb {
    const TY: TrbType = TrbType::DisableSlotCommand;
}

impl TypedTrb for AddressDeviceCommandTrb {
    const TY: TrbType = TrbType::AddressDeviceCommand;
}

impl TypedTrb for ConfigureEndpointCommandTrb {
    const TY: TrbType = TrbType::ConfigureEndpointCommand;
}

impl TypedTrb for EvaluateContextCommandTrb {
    const TY: TrbType = TrbType::EvaluateContextCommand;
}

impl TypedTrb for ResetEndpointCommandTrb {
    const TY: TrbType = TrbType::ResetEndpointCommand;
}

impl TypedTrb for StopEndpointCommandTrb {
    const TY: TrbType = TrbType::StopEndpointCommand;
}

impl TypedTrb for SetTRDequeuePointerCommandTrb {
    const TY: TrbType = TrbType::SetTRDequeuePointerCommand;
}

impl TypedTrb for ResetDeviceCommandTrb {
    const TY: TrbType = TrbType::ResetDeviceCommand;
}

impl TypedTrb for TransferEventTrb {
    const TY: TrbType = TrbType::TransferEvent;
}

impl TypedTrb for CommandCompletionEventTrb {
    const TY: TrbType = TrbType::CommandCompletionEvent;
}

impl TypedTrb for PortStatusChangeEventTrb {
    const TY: TrbType = TrbType::PortStatusChangeEvent;
}

/// All trb structs have the same size. One trb could be safely casted to another, though the
/// values might be invalid.
pub unsafe trait TrbCast: DataInit + TypedTrb {
    fn cast<T: TrbCast>(&self) -> Result<&T> {
        T::from_slice(self.as_slice()).ok_or(Error::CannotCastTrb)
    }

    fn cast_mut<T: TrbCast>(&mut self) -> Result<&mut T> {
        T::from_mut_slice(self.as_mut_slice()).ok_or(Error::CannotCastTrb)
    }

    fn checked_cast<T: TrbCast>(&self) -> Result<&T> {
        if Trb::from_slice(self.as_slice())
            .ok_or(Error::CannotCastTrb)?
            .get_trb_type()
            .map_err(Error::UnknownTrbType)?
            != T::TY
        {
            return Err(Error::CannotCastTrb);
        }
        T::from_slice(self.as_slice()).ok_or(Error::CannotCastTrb)
    }

    fn checked_mut_cast<T: TrbCast>(&mut self) -> Result<&mut T> {
        if Trb::from_slice(self.as_slice())
            .ok_or(Error::CannotCastTrb)?
            .get_trb_type()
            .map_err(Error::UnknownTrbType)?
            != T::TY
        {
            return Err(Error::CannotCastTrb);
        }
        T::from_mut_slice(self.as_mut_slice()).ok_or(Error::CannotCastTrb)
    }
}

unsafe impl DataInit for Trb {}
unsafe impl DataInit for NormalTrb {}
unsafe impl DataInit for SetupStageTrb {}
unsafe impl DataInit for DataStageTrb {}
unsafe impl DataInit for StatusStageTrb {}
unsafe impl DataInit for IsochTrb {}
unsafe impl DataInit for LinkTrb {}
unsafe impl DataInit for EventDataTrb {}
unsafe impl DataInit for NoopTrb {}
unsafe impl DataInit for DisableSlotCommandTrb {}
unsafe impl DataInit for AddressDeviceCommandTrb {}
unsafe impl DataInit for ConfigureEndpointCommandTrb {}
unsafe impl DataInit for EvaluateContextCommandTrb {}
unsafe impl DataInit for ResetEndpointCommandTrb {}
unsafe impl DataInit for StopEndpointCommandTrb {}
unsafe impl DataInit for SetTRDequeuePointerCommandTrb {}
unsafe impl DataInit for ResetDeviceCommandTrb {}
unsafe impl DataInit for TransferEventTrb {}
unsafe impl DataInit for CommandCompletionEventTrb {}
unsafe impl DataInit for PortStatusChangeEventTrb {}
unsafe impl DataInit for EventRingSegmentTableEntry {}
unsafe impl DataInit for InputControlContext {}
unsafe impl DataInit for SlotContext {}
unsafe impl DataInit for EndpointContext {}

unsafe impl DataInit for DeviceContext {}
unsafe impl DataInit for AddressedTrb {}

unsafe impl TrbCast for Trb {}
unsafe impl TrbCast for NormalTrb {}
unsafe impl TrbCast for SetupStageTrb {}
unsafe impl TrbCast for DataStageTrb {}
unsafe impl TrbCast for StatusStageTrb {}
unsafe impl TrbCast for IsochTrb {}
unsafe impl TrbCast for LinkTrb {}
unsafe impl TrbCast for EventDataTrb {}
unsafe impl TrbCast for NoopTrb {}
unsafe impl TrbCast for DisableSlotCommandTrb {}
unsafe impl TrbCast for AddressDeviceCommandTrb {}
unsafe impl TrbCast for ConfigureEndpointCommandTrb {}
unsafe impl TrbCast for EvaluateContextCommandTrb {}
unsafe impl TrbCast for ResetEndpointCommandTrb {}
unsafe impl TrbCast for StopEndpointCommandTrb {}
unsafe impl TrbCast for SetTRDequeuePointerCommandTrb {}
unsafe impl TrbCast for ResetDeviceCommandTrb {}
unsafe impl TrbCast for TransferEventTrb {}
unsafe impl TrbCast for CommandCompletionEventTrb {}
unsafe impl TrbCast for PortStatusChangeEventTrb {}

#[bitfield]
#[derive(Clone, Copy)]
pub struct EventRingSegmentTableEntry {
    ring_segment_base_address: B64,
    ring_segment_size: B16,
    reserved2: B48,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct InputControlContext {
    // Xhci spec 6.2.5.1.
    drop_context_flags: B32,
    add_context_flags: B32,
    reserved0: B32,
    reserved1: B32,
    reserved2: B32,
    reserved3: B32,
    reserved4: B32,
    configuration_value: B8,
    interface_number: B8,
    alternate_setting: B8,
    reserved5: B8,
}

impl InputControlContext {
    /// Get drop context flag.
    pub fn drop_context_flag(&self, idx: u8) -> bool {
        (self.get_drop_context_flags() & (1 << idx)) != 0
    }

    /// Get add context flag.
    pub fn add_context_flag(&self, idx: u8) -> bool {
        (self.get_add_context_flags() & (1 << idx)) != 0
    }
}

// Size of device context entries (SlotContext and EndpointContext).
pub const DEVICE_CONTEXT_ENTRY_SIZE: usize = 32usize;

#[bitfield]
#[derive(Clone, Copy)]
pub struct SlotContext {
    route_string: B20,
    speed: B4,
    reserved1: B1,
    mtt: B1,
    hub: B1,
    context_entries: B5,
    max_exit_latency: B16,
    root_hub_port_number: B8,
    num_ports: B8,
    tt_hub_slot_id: B8,
    tt_port_number: B8,
    tt_think_time: B2,
    reserved2: B4,
    interrupter_target: B10,
    usb_device_address: B8,
    reserved3: B19,
    slot_state: DeviceSlotState,
    reserved4: B32,
    reserved5: B32,
    reserved6: B32,
    reserved7: B32,
}

#[bitfield]
#[derive(Clone, Copy)]
pub struct EndpointContext {
    endpoint_state: EndpointState,
    reserved1: B5,
    mult: B2,
    max_primary_streams: B5,
    linear_stream_array: B1,
    interval: B8,
    max_esit_payload_hi: B8,
    reserved2: B1,
    error_count: B2,
    endpoint_type: B3,
    reserved3: B1,
    host_initiate_disable: B1,
    max_burst_size: B8,
    max_packet_size: B16,
    dequeue_cycle_state: bool,
    reserved4: B3,
    tr_dequeue_pointer: DequeuePtr,
    average_trb_length: B16,
    max_esit_payload_lo: B16,
    reserved5: B32,
    reserved6: B32,
    reserved7: B32,
}

/// Device context.
#[derive(Clone, Copy, Debug)]
pub struct DeviceContext {
    pub slot_context: SlotContext,
    pub endpoint_context: [EndpointContext; 31],
}

/// POD struct associates a TRB with its address in guest memory.  This is
/// useful because transfer and command completion event TRBs must contain
/// pointers to the original TRB that generated the event.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AddressedTrb {
    pub trb: Trb,
    pub gpa: u64,
}

pub type TransferDescriptor = Vec<AddressedTrb>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn check_struct_sizes() {
        assert_eq!(std::mem::size_of::<Trb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<NormalTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<SetupStageTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<DataStageTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<StatusStageTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<IsochTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<LinkTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<EventDataTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<NoopTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<DisableSlotCommandTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<AddressDeviceCommandTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<ConfigureEndpointCommandTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<EvaluateContextCommandTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<ResetEndpointCommandTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<StopEndpointCommandTrb>(), TRB_SIZE);
        assert_eq!(
            std::mem::size_of::<SetTRDequeuePointerCommandTrb>(),
            TRB_SIZE
        );
        assert_eq!(std::mem::size_of::<ResetDeviceCommandTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<TransferEventTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<CommandCompletionEventTrb>(), TRB_SIZE);
        assert_eq!(std::mem::size_of::<PortStatusChangeEventTrb>(), TRB_SIZE);

        assert_eq!(
            std::mem::size_of::<EventRingSegmentTableEntry>(),
            SEGMENT_TABLE_SIZE
        );
        assert_eq!(std::mem::size_of::<InputControlContext>(), 32);
        assert_eq!(
            std::mem::size_of::<SlotContext>(),
            DEVICE_CONTEXT_ENTRY_SIZE
        );
        assert_eq!(
            std::mem::size_of::<EndpointContext>(),
            DEVICE_CONTEXT_ENTRY_SIZE
        );
        assert_eq!(
            std::mem::size_of::<DeviceContext>(),
            32 * DEVICE_CONTEXT_ENTRY_SIZE
        );
    }
}