use libc::c_int; use libusb1_sys::constants::*; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Device speeds. Indicates the speed at which a device is operating. /// - [libusb_supported_speed](http://libusb.sourceforge.net/api-1.0/group__libusb__dev.html#ga1454797ecc0de4d084c1619c420014f6) /// - [USB release versions](https://en.wikipedia.org/wiki/USB#Release_versions) #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[non_exhaustive] pub enum Speed { /// The operating system doesn't know the device speed. Unknown, /// The device is operating at low speed (1.5 Mbps). Low, /// The device is operating at full speed (12 Mbps). Full, /// The device is operating at high speed (480 Mbps). High, /// The device is operating at super speed (5 Gbps). Super, /// The device is operating at super speed (10 Gbps). SuperPlus, } #[doc(hidden)] pub(crate) fn speed_from_libusb(n: c_int) -> Speed { match n { LIBUSB_SPEED_SUPER_PLUS => Speed::SuperPlus, LIBUSB_SPEED_SUPER => Speed::Super, LIBUSB_SPEED_HIGH => Speed::High, LIBUSB_SPEED_FULL => Speed::Full, LIBUSB_SPEED_LOW => Speed::Low, LIBUSB_SPEED_UNKNOWN | _ => Speed::Unknown, } } /// Transfer and endpoint directions. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Direction { /// Direction for read (device to host) transfers. In, /// Direction for write (host to device) transfers. Out, } /// An endpoint's transfer type. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum TransferType { /// Control endpoint. Control, /// Isochronous endpoint. Isochronous, /// Bulk endpoint. Bulk, /// Interrupt endpoint. Interrupt, } /// Isochronous synchronization mode. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum SyncType { /// No synchronisation. NoSync, /// Asynchronous. Asynchronous, /// Adaptive. Adaptive, /// Synchronous. Synchronous, } /// Isochronous usage type. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum UsageType { /// Data endpoint. Data, /// Feedback endpoint. Feedback, /// Explicit feedback data endpoint. FeedbackData, /// Reserved. Reserved, } /// Types of control transfers. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum RequestType { /// Requests that are defined by the USB standard. Standard, /// Requests that are defined by a device class, e.g., HID. Class, /// Vendor-specific requests. Vendor, /// Reserved for future use. Reserved, } /// Recipients of control transfers. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Recipient { /// The recipient is a device. Device, /// The recipient is an interface. Interface, /// The recipient is an endpoint. Endpoint, /// Other. Other, } /// A three-part version consisting of major, minor, and sub minor components. /// /// This can be used to represent versions of the format `J.M.N`, where `J` is the major version, /// `M` is the minor version, and `N` is the sub minor version. A version is constructed by /// providing the fields in the same order to the tuple. For example: /// /// ``` /// rusb::Version(0, 2, 1); /// ``` /// /// represents the version 0.2.1. /// /// The intended use case of `Version` is to extract meaning from the version fields in USB /// descriptors, such as `bcdUSB` and `bcdDevice` in device descriptors. #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Version(pub u8, pub u8, pub u8); impl Version { /// Extracts a version from a binary coded decimal (BCD) field. BCD fields exist in USB /// descriptors as 16-bit integers encoding a version as `0xJJMN`, where `JJ` is the major /// version, `M` is the minor version, and `N` is the sub minor version. For example, 2.0 is /// encoded as `0x0200` and 1.1 is encoded as `0x0110`. pub fn from_bcd(mut raw: u16) -> Self { let sub_minor: u8 = (raw & 0x000F) as u8; raw >>= 4; let minor: u8 = (raw & 0x000F) as u8; raw >>= 4; let mut major: u8 = (raw & 0x000F) as u8; raw >>= 4; major += (10 * raw) as u8; Version(major, minor, sub_minor) } /// Returns the major version. pub fn major(self) -> u8 { let Version(major, _, _) = self; major } /// Returns the minor version. pub fn minor(self) -> u8 { let Version(_, minor, _) = self; minor } /// Returns the sub minor version. pub fn sub_minor(self) -> u8 { let Version(_, _, sub_minor) = self; sub_minor } } impl std::fmt::Display for Version { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}.{}.{}", self.major(), self.minor(), self.sub_minor()) } } /// Builds a value for the `bmRequestType` field of a control transfer setup packet. /// /// The `bmRequestType` field of a USB control transfer setup packet is a bit field specifying /// three parameters, which are given to this function by corresponding enum values. /// /// ## Examples /// /// The following example returns a `bmRequestType` value for a standard inbound transfer from the /// device, which could be used for reading a device's descriptors: /// /// ```no_run /// use rusb::{Direction,RequestType,Recipient}; /// /// rusb::request_type(Direction::In, RequestType::Standard, Recipient::Device); /// ``` pub const fn request_type( direction: Direction, request_type: RequestType, recipient: Recipient, ) -> u8 { let mut value: u8 = match direction { Direction::Out => LIBUSB_ENDPOINT_OUT, Direction::In => LIBUSB_ENDPOINT_IN, }; value |= match request_type { RequestType::Standard => LIBUSB_REQUEST_TYPE_STANDARD, RequestType::Class => LIBUSB_REQUEST_TYPE_CLASS, RequestType::Vendor => LIBUSB_REQUEST_TYPE_VENDOR, RequestType::Reserved => LIBUSB_REQUEST_TYPE_RESERVED, }; value |= match recipient { Recipient::Device => LIBUSB_RECIPIENT_DEVICE, Recipient::Interface => LIBUSB_RECIPIENT_INTERFACE, Recipient::Endpoint => LIBUSB_RECIPIENT_ENDPOINT, Recipient::Other => LIBUSB_RECIPIENT_OTHER, }; value } #[cfg(test)] mod test { use super::*; // Version #[test] fn version_returns_major_version() { assert_eq!(1, Version(1, 0, 0).major()); assert_eq!(2, Version(2, 0, 0).major()); } #[test] fn version_returns_minor_version() { assert_eq!(1, Version(0, 1, 0).minor()); assert_eq!(2, Version(0, 2, 0).minor()); } #[test] fn version_returns_sub_minor_version() { assert_eq!(1, Version(0, 0, 1).sub_minor()); assert_eq!(2, Version(0, 0, 2).sub_minor()); } #[test] fn version_parses_major_version() { assert_eq!(3, Version::from_bcd(0x0300).major()); } #[test] fn version_parses_long_major_version() { assert_eq!(12, Version::from_bcd(0x1200).major()); } #[test] fn version_parses_minor_version() { assert_eq!(1, Version::from_bcd(0x0010).minor()); assert_eq!(2, Version::from_bcd(0x0020).minor()); } #[test] fn version_parses_sub_minor_version() { assert_eq!(1, Version::from_bcd(0x0001).sub_minor()); assert_eq!(2, Version::from_bcd(0x0002).sub_minor()); } #[test] fn version_parses_full_version() { assert_eq!(Version(12, 3, 4), Version::from_bcd(0x1234)); } #[test] fn version_display() { assert_eq!(Version(2, 45, 13).to_string(), "2.45.13"); } #[test] fn version_ord() { assert!(Version(0, 0, 0) < Version(1, 2, 3)); assert!(Version(1, 0, 0) < Version(1, 2, 3)); assert!(Version(1, 2, 0) < Version(1, 2, 3)); assert!(Version(1, 2, 0) < Version(1, 3, 0)); assert!(Version(255, 255, 255) > Version(254, 0, 0)); assert!(Version(0, 255, 0) > Version(0, 254, 255)); } // request_type for direction #[test] fn request_type_builds_value_for_out_direction() { assert_eq!( request_type(Direction::Out, RequestType::Standard, Recipient::Device) & 0x80, 0x00 ); } #[test] fn request_type_builds_value_for_in_direction() { assert_eq!( request_type(Direction::In, RequestType::Standard, Recipient::Device) & 0x80, 0x80 ); } // request_type for request type #[test] fn request_type_builds_value_for_standard_request() { assert_eq!( request_type(Direction::Out, RequestType::Standard, Recipient::Device) & 0x60, 0x00 ); } #[test] fn request_type_builds_value_for_class_request() { assert_eq!( request_type(Direction::Out, RequestType::Class, Recipient::Device) & 0x60, 0x20 ); } #[test] fn request_type_builds_value_for_vendor_request() { assert_eq!( request_type(Direction::Out, RequestType::Vendor, Recipient::Device) & 0x60, 0x40 ); } #[test] fn request_type_builds_value_for_reserved_request() { assert_eq!( request_type(Direction::Out, RequestType::Reserved, Recipient::Device) & 0x60, 0x60 ); } // request_type for recipient #[test] fn request_type_builds_value_for_device_recipient() { assert_eq!( request_type(Direction::Out, RequestType::Standard, Recipient::Device) & 0x0F, 0x00 ); } #[test] fn request_type_builds_value_for_interface_recipient() { assert_eq!( request_type(Direction::Out, RequestType::Standard, Recipient::Interface) & 0x0F, 0x01 ); } #[test] fn request_type_builds_value_for_endpoint_recipient() { assert_eq!( request_type(Direction::Out, RequestType::Standard, Recipient::Endpoint) & 0x0F, 0x02 ); } #[test] fn request_type_builds_value_for_other_recipient() { assert_eq!( request_type(Direction::Out, RequestType::Standard, Recipient::Other) & 0x0F, 0x03 ); } }