use std::{
fmt::{self, Debug},
mem,
ptr::NonNull,
};
use libusb1_sys::*;
use crate::{
config_descriptor::{self, ConfigDescriptor},
device_descriptor::{self, DeviceDescriptor},
device_handle::DeviceHandle,
error,
fields::{self, Speed},
Error, UsbContext,
};
#[derive(Eq, PartialEq)]
pub struct Device<T: UsbContext> {
context: T,
device: NonNull<libusb_device>,
}
impl<T: UsbContext> Drop for Device<T> {
fn drop(&mut self) {
unsafe {
libusb_unref_device(self.device.as_ptr());
}
}
}
impl<T: UsbContext> Clone for Device<T> {
fn clone(&self) -> Self {
unsafe {
Self::from_libusb(self.context.clone(), self.device)
}
}
}
unsafe impl<T: UsbContext> Send for Device<T> {}
unsafe impl<T: UsbContext> Sync for Device<T> {}
impl<T: UsbContext> Debug for Device<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let descriptor = match self.device_descriptor() {
Ok(descriptor) => descriptor,
Err(e) => {
return write!(f, "Can't read device descriptor {:?}", e);
}
};
write!(
f,
"Bus {:03} Device {:03}: ID {:04x}:{:04x}",
self.bus_number(),
self.address(),
descriptor.vendor_id(),
descriptor.product_id(),
)
}
}
impl<T: UsbContext> Device<T> {
pub fn as_raw(&self) -> *mut libusb_device {
self.device.as_ptr()
}
pub fn context(&self) -> &T {
&self.context
}
pub unsafe fn from_libusb(context: T, device: NonNull<libusb_device>) -> Device<T> {
libusb_ref_device(device.as_ptr());
Device { context, device }
}
pub fn device_descriptor(&self) -> crate::Result<DeviceDescriptor> {
let mut descriptor = mem::MaybeUninit::<libusb_device_descriptor>::uninit();
try_unsafe!(libusb_get_device_descriptor(
self.device.as_ptr(),
descriptor.as_mut_ptr()
));
Ok(device_descriptor::from_libusb(unsafe {
descriptor.assume_init()
}))
}
pub fn config_descriptor(&self, config_index: u8) -> crate::Result<ConfigDescriptor> {
let mut config = mem::MaybeUninit::<*const libusb_config_descriptor>::uninit();
try_unsafe!(libusb_get_config_descriptor(
self.device.as_ptr(),
config_index,
config.as_mut_ptr()
));
Ok(unsafe { config_descriptor::from_libusb(config.assume_init()) })
}
pub fn active_config_descriptor(&self) -> crate::Result<ConfigDescriptor> {
let mut config = mem::MaybeUninit::<*const libusb_config_descriptor>::uninit();
try_unsafe!(libusb_get_active_config_descriptor(
self.device.as_ptr(),
config.as_mut_ptr()
));
Ok(unsafe { config_descriptor::from_libusb(config.assume_init()) })
}
pub fn bus_number(&self) -> u8 {
unsafe { libusb_get_bus_number(self.device.as_ptr()) }
}
pub fn address(&self) -> u8 {
unsafe { libusb_get_device_address(self.device.as_ptr()) }
}
pub fn speed(&self) -> Speed {
fields::speed_from_libusb(unsafe { libusb_get_device_speed(self.device.as_ptr()) })
}
pub fn open(&self) -> crate::Result<DeviceHandle<T>> {
let mut handle = mem::MaybeUninit::<*mut libusb_device_handle>::uninit();
try_unsafe!(libusb_open(self.device.as_ptr(), handle.as_mut_ptr()));
Ok(unsafe {
let ptr = NonNull::new(handle.assume_init()).ok_or(Error::NoDevice)?;
DeviceHandle::from_libusb(self.context.clone(), ptr)
})
}
pub fn port_number(&self) -> u8 {
unsafe { libusb_get_port_number(self.device.as_ptr()) }
}
pub fn get_parent(&self) -> Option<Self> {
let device = unsafe { libusb_get_parent(self.device.as_ptr()) };
NonNull::new(device)
.map(|device| unsafe { Device::from_libusb(self.context.clone(), device) })
}
pub fn port_numbers(&self) -> Result<Vec<u8>, Error> {
let mut ports = [0; 7];
let result = unsafe {
libusb_get_port_numbers(self.device.as_ptr(), ports.as_mut_ptr(), ports.len() as i32)
};
let ports_number = if result < 0 {
return Err(error::from_libusb(result));
} else {
result
};
Ok(ports[0..ports_number as usize].to_vec())
}
}