blob: 2ead8912a918e27f07ab25aa6c966e28fe5fbb91 [file] [log] [blame]
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
);
}
}