use crate::constants::{
LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_NO_FLAGS,
};
use crate::ffi::{
libusb_context, libusb_device, libusb_hotplug_callback_handle,
libusb_hotplug_deregister_callback, libusb_hotplug_event, libusb_hotplug_register_callback,
};
use crate::{error, Device, UsbContext};
use std::{
borrow::Borrow,
ffi::c_void,
fmt::{self, Debug},
os::raw::c_int,
};
pub trait Hotplug<T: UsbContext>: Send {
fn device_arrived(&mut self, device: Device<T>);
fn device_left(&mut self, device: Device<T>);
}
#[derive(Debug)]
#[must_use = "USB hotplug callbacks will be deregistered if the registration is dropped"]
pub struct Registration<T: UsbContext> {
handle: libusb_hotplug_callback_handle,
call_back: Box<CallbackData<T>>,
}
impl<T: UsbContext> Registration<T> {
fn get_handle(&self) -> libusb_hotplug_callback_handle {
self.handle
}
}
impl<T: UsbContext> Drop for Registration<T> {
fn drop(&mut self) {
unsafe {
libusb_hotplug_deregister_callback(self.call_back.context.as_raw(), self.get_handle())
}
}
}
#[derive(Copy, Clone, Debug, Default)]
#[doc(alias = "libusb_hotplug_register_callback")]
pub struct HotplugBuilder {
vendor_id: Option<u16>,
product_id: Option<u16>,
class: Option<u8>,
enumerate: bool,
}
impl HotplugBuilder {
pub fn new() -> Self {
HotplugBuilder {
vendor_id: None,
product_id: None,
class: None,
enumerate: false,
}
}
pub fn vendor_id(&mut self, vendor_id: u16) -> &mut Self {
self.vendor_id = Some(vendor_id);
self
}
pub fn product_id(&mut self, product_id: u16) -> &mut Self {
self.product_id = Some(product_id);
self
}
pub fn class(&mut self, class: u8) -> &mut Self {
self.class = Some(class);
self
}
pub fn enumerate(&mut self, enumerate: bool) -> &mut Self {
self.enumerate = enumerate;
self
}
pub fn register<U: UsbContext, T: Borrow<U>>(
self,
context: T,
callback: Box<dyn Hotplug<U>>,
) -> crate::Result<Registration<U>> {
let mut handle: libusb_hotplug_callback_handle = 0;
let mut call_back = Box::new(CallbackData {
context: context.borrow().clone(),
hotplug: callback,
});
let hotplug_flags = if self.enumerate {
LIBUSB_HOTPLUG_ENUMERATE
} else {
LIBUSB_HOTPLUG_NO_FLAGS
};
let user_data = &mut *call_back as *mut _ as *mut _;
let n = unsafe {
libusb_hotplug_register_callback(
context.borrow().as_raw(),
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
hotplug_flags,
self.vendor_id
.map(c_int::from)
.unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY),
self.product_id
.map(c_int::from)
.unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY),
self.class
.map(c_int::from)
.unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY),
hotplug_callback::<U>,
user_data,
&mut handle,
)
};
if n < 0 {
Err(error::from_libusb(n))
} else {
Ok(Registration { handle, call_back })
}
}
}
struct CallbackData<T: UsbContext> {
context: T,
hotplug: Box<dyn Hotplug<T>>,
}
impl<T> Debug for CallbackData<T>
where
T: UsbContext + Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("CallbackData")
.field("context", &self.context)
.finish()
}
}
pub extern "system" fn hotplug_callback<T: UsbContext>(
_ctx: *mut libusb_context,
device: *mut libusb_device,
event: libusb_hotplug_event,
user_data: *mut c_void,
) -> c_int {
let ret = std::panic::catch_unwind(|| {
let reg = unsafe { &mut *(user_data as *mut CallbackData<T>) };
let device = unsafe {
Device::from_libusb(
reg.context.clone(),
std::ptr::NonNull::new_unchecked(device),
)
};
match event {
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => reg.hotplug.device_arrived(device),
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => reg.hotplug.device_left(device),
_ => (),
};
});
match ret {
Ok(_) => 0,
Err(_) => 1,
}
}