diff options
author | Daniel Verkamp <dverkamp@chromium.org> | 2019-07-09 17:21:54 -0700 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-10-17 00:20:24 +0000 |
commit | 6494117e1766337f5d688b98bfc3df999932c3ac (patch) | |
tree | 0c8b6fd24a615ae146bb0a2d6f4d8ce332f5cc42 /usb_util/src/types.rs | |
parent | bed8b0017d2cb283c20dc50241adb4f5b2668489 (diff) | |
download | crosvm-6494117e1766337f5d688b98bfc3df999932c3ac.tar crosvm-6494117e1766337f5d688b98bfc3df999932c3ac.tar.gz crosvm-6494117e1766337f5d688b98bfc3df999932c3ac.tar.bz2 crosvm-6494117e1766337f5d688b98bfc3df999932c3ac.tar.lz crosvm-6494117e1766337f5d688b98bfc3df999932c3ac.tar.xz crosvm-6494117e1766337f5d688b98bfc3df999932c3ac.tar.zst crosvm-6494117e1766337f5d688b98bfc3df999932c3ac.zip |
usb: replace libusb with Rust usb_util library
Drop the dependency on libusb and reimplement the host USB backend using usb_sys to wrap the Linux usbdevfs ioctls. This allows sandboxing to work without any dependency on libusb patches, and it gives us the flexibility to modify and update the USB backend without depending on an external third-party library. BUG=chromium:987833 TEST=`adb logcat` on nami with Nexus 5 attached TEST=deploy app to phone with Android Studio TEST=Run EdgeTPU USB accelerator demo (including DFU mode transition) Cq-Depend: chromium:1773695 Change-Id: I4321c2b6142caac15f48f197795a37d59d268831 Signed-off-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1783601 Reviewed-by: Zach Reizner <zachr@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com>
Diffstat (limited to 'usb_util/src/types.rs')
-rw-r--r-- | usb_util/src/types.rs | 259 |
1 files changed, 232 insertions, 27 deletions
diff --git a/usb_util/src/types.rs b/usb_util/src/types.rs index f40dd6c..d669dcd 100644 --- a/usb_util/src/types.rs +++ b/usb_util/src/types.rs @@ -7,35 +7,159 @@ use data_model::DataInit; use std::mem::size_of; -use crate::bindings; - -/// Speed of usb device. See usb spec for more details. -#[derive(Debug)] -pub enum Speed { - /// The OS doesn't report or know the device speed. - Unknown, - /// The device is operating at low speed (1.5MBit/s). - Low, - /// The device is operating at full speed (12MBit/s). - Full, - /// The device is operating at high speed (480MBit/s). - High, - /// The device is operating at super speed (5000MBit/s). - Super, -} - -impl From<bindings::libusb_speed> for Speed { - fn from(speed: bindings::libusb_speed) -> Speed { - match speed { - bindings::LIBUSB_SPEED_LOW => Speed::Low, - bindings::LIBUSB_SPEED_FULL => Speed::Full, - bindings::LIBUSB_SPEED_HIGH => Speed::High, - bindings::LIBUSB_SPEED_SUPER => Speed::Super, - _ => Speed::Unknown, - } +/// 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 { @@ -54,6 +178,35 @@ pub enum EndpointDirection { /// 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. @@ -118,7 +271,7 @@ pub struct UsbRequestSetup { pub length: u16, // wLength } -fn _assert() { +fn _assert_usb_request_setup() { const_assert!(size_of::<UsbRequestSetup>() == 8); } @@ -194,3 +347,55 @@ impl UsbRequestSetup { } } } + +/// 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 + ); + } +} |