| 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, |
| }; |
| |
| /// A reference to a USB device. |
| #[derive(Eq, PartialEq)] |
| pub struct Device<T: UsbContext> { |
| context: T, |
| device: NonNull<libusb_device>, |
| } |
| |
| impl<T: UsbContext> Drop for Device<T> { |
| /// Releases the device reference. |
| 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> { |
| /// Get the raw libusb_device pointer, for advanced use in unsafe code |
| pub fn as_raw(&self) -> *mut libusb_device { |
| self.device.as_ptr() |
| } |
| |
| /// Get the context associated with this device |
| pub fn context(&self) -> &T { |
| &self.context |
| } |
| |
| /// # Safety |
| /// |
| /// Converts an existing `libusb_device` pointer into a `Device<T>`. |
| /// `device` must be a pointer to a valid `libusb_device`. Rusb increments refcount. |
| pub unsafe fn from_libusb(context: T, device: NonNull<libusb_device>) -> Device<T> { |
| libusb_ref_device(device.as_ptr()); |
| |
| Device { context, device } |
| } |
| |
| /// Reads the device descriptor. |
| pub fn device_descriptor(&self) -> crate::Result<DeviceDescriptor> { |
| let mut descriptor = mem::MaybeUninit::<libusb_device_descriptor>::uninit(); |
| |
| // since libusb 1.0.16, this function always succeeds |
| try_unsafe!(libusb_get_device_descriptor( |
| self.device.as_ptr(), |
| descriptor.as_mut_ptr() |
| )); |
| |
| Ok(device_descriptor::from_libusb(unsafe { |
| descriptor.assume_init() |
| })) |
| } |
| |
| /// Reads a configuration descriptor. |
| 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()) }) |
| } |
| |
| /// Reads the configuration descriptor for the current configuration. |
| 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()) }) |
| } |
| |
| /// Returns the number of the bus that the device is connected to. |
| pub fn bus_number(&self) -> u8 { |
| unsafe { libusb_get_bus_number(self.device.as_ptr()) } |
| } |
| |
| /// Returns the device's address on the bus that it's connected to. |
| pub fn address(&self) -> u8 { |
| unsafe { libusb_get_device_address(self.device.as_ptr()) } |
| } |
| |
| /// Returns the device's connection speed. |
| pub fn speed(&self) -> Speed { |
| fields::speed_from_libusb(unsafe { libusb_get_device_speed(self.device.as_ptr()) }) |
| } |
| |
| /// Opens the device. |
| 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) |
| }) |
| } |
| |
| /// Returns the device's port number |
| pub fn port_number(&self) -> u8 { |
| unsafe { libusb_get_port_number(self.device.as_ptr()) } |
| } |
| |
| /// Returns the device's parent |
| 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) }) |
| } |
| |
| /// Get the list of all port numbers from root for the specified device |
| pub fn port_numbers(&self) -> Result<Vec<u8>, Error> { |
| // As per the USB 3.0 specs, the current maximum limit for the depth is 7. |
| 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()) |
| } |
| } |