| 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, |
| }; |
| |
| /// When handling a [method@Hotplug::device_arrived] event it is considered safe to call |
| /// any `rusb` function that takes a [`Device`]. It also safe to open a device and |
| /// submit **asynchronous** transfers. |
| /// However, most other functions that take a [`DeviceHandle`] are **not safe** to call. |
| /// Examples of such functions are any of the synchronous API functions or |
| /// the blocking functions that retrieve various USB descriptors. |
| /// These functions must be used outside of the context of the [Hotplug] functions. |
| /// |
| /// [`Device`]: crate::Device |
| /// [`DeviceHandle`]: crate::DeviceHandle |
| /// [`Context::unregister_callback`]: method@crate::Context::unregister_callback |
| 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")] |
| /// Builds hotplug [Registration] with custom configuration values. |
| pub struct HotplugBuilder { |
| vendor_id: Option<u16>, |
| product_id: Option<u16>, |
| class: Option<u8>, |
| enumerate: bool, |
| } |
| |
| impl HotplugBuilder { |
| /// Returns a new builder with the no filter |
| /// Devices can optionally be filtered by [HotplugBuilder::vendor_id] |
| /// and [HotplugBuilder::product_id] |
| /// |
| /// Registration is done by by calling [`register`]. |
| /// |
| /// [`register`]: method@Self::register |
| pub fn new() -> Self { |
| HotplugBuilder { |
| vendor_id: None, |
| product_id: None, |
| class: None, |
| enumerate: false, |
| } |
| } |
| |
| /// Devices can optionally be filtered by vendor |
| pub fn vendor_id(&mut self, vendor_id: u16) -> &mut Self { |
| self.vendor_id = Some(vendor_id); |
| self |
| } |
| |
| /// Devices can optionally be filtered by product id |
| pub fn product_id(&mut self, product_id: u16) -> &mut Self { |
| self.product_id = Some(product_id); |
| self |
| } |
| |
| /// Devices can optionally be filtered by class |
| pub fn class(&mut self, class: u8) -> &mut Self { |
| self.class = Some(class); |
| self |
| } |
| |
| /// If `enumerate` is `true`, then devices that are already |
| /// connected will cause your callback's [Hotplug::device_arrived] method to be |
| /// called for them. |
| pub fn enumerate(&mut self, enumerate: bool) -> &mut Self { |
| self.enumerate = enumerate; |
| self |
| } |
| |
| /// Register a `callback` to be called on hotplug events. The callback's |
| /// [method@Hotplug::device_arrived] method is called when a new device is added to |
| /// the bus, and [method@Hotplug::device_left] is called when it is removed. |
| /// |
| /// The callback will remain registered until the returned [Registration] is |
| /// dropped, which can be done explicitly with [`Context::unregister_callback`]. |
| /// |
| /// When handling a [method@Hotplug::device_arrived] event it is considered safe to call |
| /// any `rusb` function that takes a [`Device`]. It also safe to open a device and |
| /// submit **asynchronous** transfers. |
| /// However, most other functions that take a [`DeviceHandle`] are **not safe** to call. |
| /// Examples of such functions are any of the synchronous API functions or |
| /// the blocking functions that retrieve various USB descriptors. |
| /// These functions must be used outside of the context of the [Hotplug] functions. |
| /// |
| /// [`Device`]: crate::Device |
| /// [`DeviceHandle`]: crate::DeviceHandle |
| /// [`Context::unregister_callback`]: method@crate::Context::unregister_callback |
| 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, |
| } |
| } |