use std::ptr::addr_of_mut;
use crate::{ffi, try_d3xx, D3xxError, Pipe, PipeType, Result};
pub struct DeviceDescriptor {
inner: ffi::FT_DEVICE_DESCRIPTOR,
serial_number: String,
manufacturer: String,
product: String,
}
impl DeviceDescriptor {
pub(crate) fn new(handle: ffi::FT_HANDLE) -> Result<Self> {
let mut inner = ffi::FT_DEVICE_DESCRIPTOR::default();
try_d3xx!(unsafe { ffi::FT_GetDeviceDescriptor(handle, addr_of_mut!(inner)) })?;
Ok(Self {
inner,
serial_number: descriptor_string(handle, inner.iSerialNumber)?,
manufacturer: descriptor_string(handle, inner.iManufacturer)?,
product: descriptor_string(handle, inner.iProduct)?,
})
}
#[must_use]
pub fn serial_number(&self) -> &str {
&self.serial_number
}
#[must_use]
pub fn manufacturer(&self) -> &str {
&self.manufacturer
}
#[must_use]
pub fn product(&self) -> &str {
&self.product
}
#[must_use]
pub fn vendor_id(&self) -> usize {
usize::from(self.inner.idVendor)
}
#[must_use]
pub fn product_id(&self) -> usize {
usize::from(self.inner.idProduct)
}
#[must_use]
pub fn usb_version(&self) -> UsbVersion {
UsbVersion(usize::from(self.inner.bcdUSB))
}
#[must_use]
pub fn max_packet_size(&self) -> usize {
usize::from(self.inner.bMaxPacketSize0)
}
#[must_use]
pub fn class_codes(&self) -> ClassCodes {
ClassCodes::new(
self.inner.bDeviceClass,
self.inner.bDeviceSubClass,
self.inner.bDeviceProtocol,
)
}
}
pub struct InterfaceDescriptor {
inner: ffi::FT_INTERFACE_DESCRIPTOR,
description: String,
}
impl InterfaceDescriptor {
pub(crate) fn new(handle: ffi::FT_HANDLE, index: u8) -> Result<Self> {
let mut inner = ffi::FT_INTERFACE_DESCRIPTOR::default();
try_d3xx!(unsafe { ffi::FT_GetInterfaceDescriptor(handle, index, addr_of_mut!(inner)) })?;
Ok(Self {
inner,
description: descriptor_string(handle, inner.iInterface)?,
})
}
#[must_use]
pub fn interface_number(&self) -> usize {
usize::from(self.inner.bInterfaceNumber)
}
#[must_use]
pub fn class_codes(&self) -> ClassCodes {
ClassCodes::new(
self.inner.bInterfaceClass,
self.inner.bInterfaceSubClass,
self.inner.bInterfaceProtocol,
)
}
#[must_use]
pub fn endpoints(&self) -> usize {
usize::from(self.inner.bNumEndpoints)
}
#[must_use]
pub fn alternate_setting(&self) -> u8 {
self.inner.bAlternateSetting
}
#[must_use]
pub fn description(&self) -> &str {
&self.description
}
}
pub struct ConfigurationDescriptor {
inner: ffi::FT_CONFIGURATION_DESCRIPTOR,
description: String,
}
impl ConfigurationDescriptor {
pub(crate) fn new(handle: ffi::FT_HANDLE) -> Result<Self> {
let mut inner = ffi::FT_CONFIGURATION_DESCRIPTOR::default();
try_d3xx!(unsafe { ffi::FT_GetConfigurationDescriptor(handle, addr_of_mut!(inner)) })?;
Ok(Self {
inner,
description: descriptor_string(handle, inner.iConfiguration)?,
})
}
#[must_use]
pub fn interfaces(&self) -> usize {
usize::from(self.inner.bNumInterfaces)
}
#[must_use]
pub fn configuration_value(&self) -> u8 {
self.inner.bConfigurationValue
}
#[must_use]
pub fn description(&self) -> &str {
&self.description
}
#[must_use]
pub fn max_power(&self) -> u8 {
self.inner.MaxPower * 2
}
#[must_use]
pub fn self_powered(&self) -> bool {
self.inner.bmAttributes & CONFIGURATION_ATTRIBUTE_SELF_POWERED != 0
}
#[must_use]
pub fn remote_wakeup(&self) -> bool {
self.inner.bmAttributes & CONFIGURATION_ATTRIBUTE_REMOTE_WAKEUP != 0
}
}
const CONFIGURATION_ATTRIBUTE_SELF_POWERED: u8 = 0b0100_0000;
const CONFIGURATION_ATTRIBUTE_REMOTE_WAKEUP: u8 = 0b0010_0000;
pub struct UsbVersion(usize);
impl UsbVersion {
#[must_use]
pub fn major(&self) -> usize {
self.0 >> 8
}
#[must_use]
pub fn minor(&self) -> usize {
self.0 & 0xFF
}
}
pub struct PipeInfo {
pipe: Pipe,
pipe_type: PipeType,
max_packet_size: usize,
interval: u8,
}
impl PipeInfo {
pub(crate) fn new(info: ffi::FT_PIPE_INFORMATION) -> Result<Self> {
Ok(Self {
pipe_type: PipeType::from(info.PipeType),
pipe: Pipe::try_from(info.PipeId).or(Err(D3xxError::OtherError))?,
max_packet_size: info.MaximumPacketSize as usize,
interval: info.Interval,
})
}
#[must_use]
pub fn pipe_type(&self) -> PipeType {
self.pipe_type
}
#[must_use]
pub fn id(&self) -> Pipe {
self.pipe
}
#[must_use]
pub fn max_packet_size(&self) -> usize {
self.max_packet_size
}
#[must_use]
pub fn interval(&self) -> u8 {
self.interval
}
}
pub struct ClassCodes {
class: u8,
subclass: u8,
protocol: u8,
}
impl ClassCodes {
fn new(class: u8, subclass: u8, protocol: u8) -> Self {
Self {
class,
subclass,
protocol,
}
}
#[must_use]
pub fn class(&self) -> u8 {
self.class
}
#[must_use]
pub fn subclass(&self) -> u8 {
self.subclass
}
#[must_use]
pub fn protocol(&self) -> u8 {
self.protocol
}
}
fn descriptor_string(handle: ffi::FT_HANDLE, index: u8) -> Result<String> {
let mut descriptor = ffi::FT_STRING_DESCRIPTOR::default();
try_d3xx!(unsafe { ffi::FT_GetStringDescriptor(handle, index, addr_of_mut!(descriptor)) })?;
Ok(widestring::U16CStr::from_slice(&descriptor.szString)
.or(Err(D3xxError::OtherError))?
.to_string_lossy())
}
#[cfg(test)]
mod test {
use crate::{descriptor::PipeInfo, ffi, Pipe, PipeType};
#[test]
fn pipe_info_try_from() {
let info = ffi::FT_PIPE_INFORMATION {
PipeType: ffi::FT_PIPE_TYPE::FTPipeTypeControl,
PipeId: 0x82,
MaximumPacketSize: 64,
Interval: 0,
};
let info = PipeInfo::new(info).unwrap();
assert_eq!(info.pipe_type(), PipeType::Control);
assert_eq!(info.id(), Pipe::In0);
assert_eq!(info.max_packet_size(), 64);
assert_eq!(info.interval(), 0);
}
#[test]
fn class_code() {
let codes = super::ClassCodes::new(0x00, 0x00, 0x00);
assert_eq!(codes.class(), 0x00);
assert_eq!(codes.subclass(), 0x00);
assert_eq!(codes.protocol(), 0x00);
let codes = super::ClassCodes::new(0x01, 0x02, 0x03);
assert_eq!(codes.class(), 0x01);
assert_eq!(codes.subclass(), 0x02);
assert_eq!(codes.protocol(), 0x03);
}
#[test]
fn usb_version() {
let version = super::UsbVersion(0x0200);
assert_eq!(version.major(), 2);
assert_eq!(version.minor(), 0);
let version = super::UsbVersion(0x0210);
assert_eq!(version.major(), 2);
assert_eq!(version.minor(), 16);
}
}