summary refs log blame commit diff
path: root/usb_util/src/types.rs
blob: d669dcdf76f0cd52dc0b9b35eb5dcda7a07d9cd4 (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 assertions::const_assert;
use data_model::DataInit;

use std::mem::size_of;

/// Standard USB descriptor types.
pub enum DescriptorType {
    Device = 0x01,
    Configuration = 0x02,
    Interface = 0x04,
    Endpoint = 0x05,
}

/// Trait describing USB descriptors.
pub trait Descriptor {
    /// Get the expected bDescriptorType value for this type of descriptor.
    fn descriptor_type() -> DescriptorType;
}

/// Standard USB descriptor header common to all descriptor types.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct DescriptorHeader {
    pub bLength: u8,
    pub bDescriptorType: u8,
}

// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for DescriptorHeader {}

fn _assert_descriptor_header() {
    const_assert!(size_of::<DescriptorHeader>() == 2);
}

/// Standard USB device descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct DeviceDescriptor {
    pub bcdUSB: u16,
    pub bDeviceClass: u8,
    pub bDeviceSubClass: u8,
    pub bDeviceProtocol: u8,
    pub bMaxPacketSize0: u8,
    pub idVendor: u16,
    pub idProduct: u16,
    pub bcdDevice: u16,
    pub iManufacturer: u8,
    pub iProduct: u8,
    pub iSerialNumber: u8,
    pub bNumConfigurations: u8,
}

impl Descriptor for DeviceDescriptor {
    fn descriptor_type() -> DescriptorType {
        DescriptorType::Device
    }
}

// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for DeviceDescriptor {}

fn _assert_device_descriptor() {
    const_assert!(size_of::<DeviceDescriptor>() == 18 - 2);
}

/// Standard USB configuration descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct ConfigDescriptor {
    pub wTotalLength: u16,
    pub bNumInterfaces: u8,
    pub bConfigurationValue: u8,
    pub iConfiguration: u8,
    pub bmAttributes: u8,
    pub bMaxPower: u8,
}

impl Descriptor for ConfigDescriptor {
    fn descriptor_type() -> DescriptorType {
        DescriptorType::Configuration
    }
}

// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for ConfigDescriptor {}

fn _assert_config_descriptor() {
    const_assert!(size_of::<ConfigDescriptor>() == 9 - 2);
}

impl ConfigDescriptor {
    pub fn num_interfaces(&self) -> u8 {
        self.bNumInterfaces
    }
}

/// Standard USB interface descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct InterfaceDescriptor {
    pub bInterfaceNumber: u8,
    pub bAlternateSetting: u8,
    pub bNumEndpoints: u8,
    pub bInterfaceClass: u8,
    pub bInterfaceSubClass: u8,
    pub bInterfaceProtocol: u8,
    pub iInterface: u8,
}

impl Descriptor for InterfaceDescriptor {
    fn descriptor_type() -> DescriptorType {
        DescriptorType::Interface
    }
}

// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for InterfaceDescriptor {}

fn _assert_interface_descriptor() {
    const_assert!(size_of::<InterfaceDescriptor>() == 9 - 2);
}

/// Standard USB endpoint descriptor as defined in USB 2.0 chapter 9,
/// not including the standard header.
#[allow(non_snake_case)]
#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct EndpointDescriptor {
    pub bEndpointAddress: u8,
    pub bmAttributes: u8,
    pub wMaxPacketSize: u16,
    pub bInterval: u8,
}

impl Descriptor for EndpointDescriptor {
    fn descriptor_type() -> DescriptorType {
        DescriptorType::Endpoint
    }
}

// Safe because it only has data and has no implicit padding.
unsafe impl DataInit for EndpointDescriptor {}

fn _assert_endpoint_descriptor() {
    const_assert!(size_of::<EndpointDescriptor>() == 7 - 2);
}

const ENDPOINT_DESCRIPTOR_DIRECTION_MASK: u8 = 1 << 7;
const ENDPOINT_DESCRIPTOR_NUMBER_MASK: u8 = 0xf;
const ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK: u8 = 0x3;

/// Endpoint types.
#[derive(PartialEq)]
pub enum EndpointType {
    Control,
    Isochronous,
    Bulk,
    Interrupt,
}

/// Endpoint Directions.
#[derive(PartialEq, Clone, Copy)]
pub enum EndpointDirection {
    HostToDevice = 0,
    DeviceToHost = 1,
}
/// Endpoint direction offset.
pub const ENDPOINT_DIRECTION_OFFSET: u8 = 7;

impl EndpointDescriptor {
    // Get direction of this endpoint.
    pub fn get_direction(&self) -> EndpointDirection {
        let direction = self.bEndpointAddress & ENDPOINT_DESCRIPTOR_DIRECTION_MASK;
        if direction != 0 {
            EndpointDirection::DeviceToHost
        } else {
            EndpointDirection::HostToDevice
        }
    }

    // Get endpoint number.
    pub fn get_endpoint_number(&self) -> u8 {
        self.bEndpointAddress & ENDPOINT_DESCRIPTOR_NUMBER_MASK
    }

    // Get endpoint type.
    pub fn get_endpoint_type(&self) -> Option<EndpointType> {
        let ep_type = self.bmAttributes & ENDPOINT_DESCRIPTOR_ATTRIBUTES_TYPE_MASK;
        match ep_type {
            0 => Some(EndpointType::Control),
            1 => Some(EndpointType::Isochronous),
            2 => Some(EndpointType::Bulk),
            3 => Some(EndpointType::Interrupt),
            _ => None,
        }
    }
}

/// Offset of data phase transfer direction.
pub const DATA_PHASE_DIRECTION_OFFSET: u8 = 7;
/// Bit mask of data phase transfer direction.
pub const DATA_PHASE_DIRECTION: u8 = 1u8 << DATA_PHASE_DIRECTION_OFFSET;
// Types of data phase transfer directions.
#[derive(Copy, Clone, PartialEq)]
pub enum ControlRequestDataPhaseTransferDirection {
    HostToDevice = 0,
    DeviceToHost = 1,
}

/// Offset of control request type.
pub const CONTROL_REQUEST_TYPE_OFFSET: u8 = 5;
/// Bit mask of control request type.
pub const CONTROL_REQUEST_TYPE: u8 = 0b11 << CONTROL_REQUEST_TYPE_OFFSET;
/// Request types.
#[derive(PartialEq)]
pub enum ControlRequestType {
    Standard = 0,
    Class = 1,
    Vendor = 2,
    Reserved = 3,
}

/// Recipient type bits.
pub const REQUEST_RECIPIENT_TYPE: u8 = 0b1111;
/// Recipient type of control request.
#[derive(PartialEq)]
pub enum ControlRequestRecipient {
    Device = 0,
    Interface = 1,
    Endpoint = 2,
    Other = 3,
    Reserved,
}

/// Standard request defined in usb spec.
#[derive(PartialEq)]
pub enum StandardControlRequest {
    GetStatus = 0x00,
    ClearFeature = 0x01,
    SetFeature = 0x03,
    SetAddress = 0x05,
    GetDescriptor = 0x06,
    SetDescriptor = 0x07,
    GetConfiguration = 0x08,
    SetConfiguration = 0x09,
    GetInterface = 0x0a,
    SetInterface = 0x11,
    SynchFrame = 0x12,
}

/// RequestSetup is first part of control transfer buffer.
#[repr(C, packed)]
#[derive(Copy, Clone, Debug)]
pub struct UsbRequestSetup {
    // USB Device Request. USB spec. rev. 2.0 9.3
    pub request_type: u8, // bmRequestType
    pub request: u8,      // bRequest
    pub value: u16,       // wValue
    pub index: u16,       // wIndex
    pub length: u16,      // wLength
}

fn _assert_usb_request_setup() {
    const_assert!(size_of::<UsbRequestSetup>() == 8);
}

unsafe impl DataInit for UsbRequestSetup {}

impl UsbRequestSetup {
    pub fn new(
        request_type: u8,
        request: u8,
        value: u16,
        index: u16,
        length: u16,
    ) -> UsbRequestSetup {
        UsbRequestSetup {
            request_type,
            request,
            value,
            index,
            length,
        }
    }

    /// Get type of request.
    pub fn get_type(&self) -> ControlRequestType {
        let ty = (self.request_type & CONTROL_REQUEST_TYPE) >> CONTROL_REQUEST_TYPE_OFFSET;
        match ty {
            0 => ControlRequestType::Standard,
            1 => ControlRequestType::Class,
            2 => ControlRequestType::Vendor,
            _ => ControlRequestType::Reserved,
        }
    }

    /// Get request direction.
    pub fn get_direction(&self) -> ControlRequestDataPhaseTransferDirection {
        let dir = (self.request_type & DATA_PHASE_DIRECTION) >> DATA_PHASE_DIRECTION_OFFSET;
        match dir {
            0 => ControlRequestDataPhaseTransferDirection::HostToDevice,
            _ => ControlRequestDataPhaseTransferDirection::DeviceToHost,
        }
    }

    /// Get recipient of this control transfer.
    pub fn get_recipient(&self) -> ControlRequestRecipient {
        let recipient = self.request_type & REQUEST_RECIPIENT_TYPE;
        match recipient {
            0 => ControlRequestRecipient::Device,
            1 => ControlRequestRecipient::Interface,
            2 => ControlRequestRecipient::Endpoint,
            3 => ControlRequestRecipient::Other,
            _ => ControlRequestRecipient::Reserved,
        }
    }

    /// Return the type of standard control request.
    pub fn get_standard_request(&self) -> Option<StandardControlRequest> {
        if self.get_type() != ControlRequestType::Standard {
            return None;
        }
        match self.request {
            0x00 => Some(StandardControlRequest::GetStatus),
            0x01 => Some(StandardControlRequest::ClearFeature),
            0x03 => Some(StandardControlRequest::SetFeature),
            0x05 => Some(StandardControlRequest::SetAddress),
            0x06 => Some(StandardControlRequest::GetDescriptor),
            0x07 => Some(StandardControlRequest::SetDescriptor),
            0x08 => Some(StandardControlRequest::GetConfiguration),
            0x09 => Some(StandardControlRequest::SetConfiguration),
            0x0a => Some(StandardControlRequest::GetInterface),
            0x11 => Some(StandardControlRequest::SetInterface),
            0x12 => Some(StandardControlRequest::SynchFrame),
            _ => None,
        }
    }
}

/// Construct a bmRequestType value for a control request.
pub fn control_request_type(
    type_: ControlRequestType,
    dir: ControlRequestDataPhaseTransferDirection,
    recipient: ControlRequestRecipient,
) -> u8 {
    ((type_ as u8) << CONTROL_REQUEST_TYPE_OFFSET)
        | ((dir as u8) << DATA_PHASE_DIRECTION_OFFSET)
        | (recipient as u8)
}

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

    #[test]
    fn control_request_types() {
        assert_eq!(
            control_request_type(
                ControlRequestType::Standard,
                ControlRequestDataPhaseTransferDirection::HostToDevice,
                ControlRequestRecipient::Device
            ),
            0b0_00_00000
        );
        assert_eq!(
            control_request_type(
                ControlRequestType::Standard,
                ControlRequestDataPhaseTransferDirection::DeviceToHost,
                ControlRequestRecipient::Device
            ),
            0b1_00_00000
        );
        assert_eq!(
            control_request_type(
                ControlRequestType::Standard,
                ControlRequestDataPhaseTransferDirection::HostToDevice,
                ControlRequestRecipient::Interface
            ),
            0b0_00_00001
        );
        assert_eq!(
            control_request_type(
                ControlRequestType::Class,
                ControlRequestDataPhaseTransferDirection::HostToDevice,
                ControlRequestRecipient::Device
            ),
            0b0_01_00000
        );
    }
}