1 use crate::constants::{
2     LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
3     LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_NO_FLAGS,
4 };
5 use crate::ffi::{
6     libusb_context, libusb_device, libusb_hotplug_callback_handle,
7     libusb_hotplug_deregister_callback, libusb_hotplug_event, libusb_hotplug_register_callback,
8 };
9 use crate::{error, Device, UsbContext};
10 use std::{
11     borrow::Borrow,
12     ffi::c_void,
13     fmt::{self, Debug},
14     os::raw::c_int,
15 };
16 
17 /// When handling a [method@Hotplug::device_arrived] event it is considered safe to call
18 /// any `rusb` function that takes a [`Device`]. It also safe to open a device and
19 /// submit **asynchronous** transfers.
20 /// However, most other functions that take a [`DeviceHandle`] are **not safe** to call.
21 /// Examples of such functions are any of the synchronous API functions or
22 /// the blocking functions that retrieve various USB descriptors.
23 /// These functions must be used outside of the context of the [Hotplug] functions.
24 ///
25 /// [`Device`]: crate::Device
26 /// [`DeviceHandle`]: crate::DeviceHandle
27 /// [`Context::unregister_callback`]: method@crate::Context::unregister_callback
28 pub trait Hotplug<T: UsbContext>: Send {
device_arrived(&mut self, device: Device<T>)29     fn device_arrived(&mut self, device: Device<T>);
device_left(&mut self, device: Device<T>)30     fn device_left(&mut self, device: Device<T>);
31 }
32 
33 #[derive(Debug)]
34 #[must_use = "USB hotplug callbacks will be deregistered if the registration is dropped"]
35 pub struct Registration<T: UsbContext> {
36     handle: libusb_hotplug_callback_handle,
37     call_back: Box<CallbackData<T>>,
38 }
39 
40 impl<T: UsbContext> Registration<T> {
get_handle(&self) -> libusb_hotplug_callback_handle41     fn get_handle(&self) -> libusb_hotplug_callback_handle {
42         self.handle
43     }
44 }
45 
46 impl<T: UsbContext> Drop for Registration<T> {
drop(&mut self)47     fn drop(&mut self) {
48         unsafe {
49             libusb_hotplug_deregister_callback(self.call_back.context.as_raw(), self.get_handle())
50         }
51     }
52 }
53 
54 #[derive(Copy, Clone, Debug, Default)]
55 #[doc(alias = "libusb_hotplug_register_callback")]
56 /// Builds hotplug [Registration] with custom configuration values.
57 pub struct HotplugBuilder {
58     vendor_id: Option<u16>,
59     product_id: Option<u16>,
60     class: Option<u8>,
61     enumerate: bool,
62 }
63 
64 impl HotplugBuilder {
65     /// Returns a new builder with the no filter
66     /// Devices can optionally be filtered by [HotplugBuilder::vendor_id]
67     /// and [HotplugBuilder::product_id]
68     ///
69     /// Registration is done by by calling [`register`].
70     ///
71     /// [`register`]: method@Self::register
new() -> Self72     pub fn new() -> Self {
73         HotplugBuilder {
74             vendor_id: None,
75             product_id: None,
76             class: None,
77             enumerate: false,
78         }
79     }
80 
81     /// Devices can optionally be filtered by vendor
vendor_id(&mut self, vendor_id: u16) -> &mut Self82     pub fn vendor_id(&mut self, vendor_id: u16) -> &mut Self {
83         self.vendor_id = Some(vendor_id);
84         self
85     }
86 
87     /// Devices can optionally be filtered by product id
product_id(&mut self, product_id: u16) -> &mut Self88     pub fn product_id(&mut self, product_id: u16) -> &mut Self {
89         self.product_id = Some(product_id);
90         self
91     }
92 
93     /// Devices can optionally be filtered by class
class(&mut self, class: u8) -> &mut Self94     pub fn class(&mut self, class: u8) -> &mut Self {
95         self.class = Some(class);
96         self
97     }
98 
99     /// If `enumerate` is `true`, then devices that are already
100     /// connected will cause your callback's [Hotplug::device_arrived] method to be
101     /// called for them.
enumerate(&mut self, enumerate: bool) -> &mut Self102     pub fn enumerate(&mut self, enumerate: bool) -> &mut Self {
103         self.enumerate = enumerate;
104         self
105     }
106 
107     /// Register a `callback` to be called on hotplug events. The callback's
108     /// [method@Hotplug::device_arrived] method is called when a new device is added to
109     /// the bus, and [method@Hotplug::device_left] is called when it is removed.
110     ///
111     /// The callback will remain registered until the returned [Registration] is
112     /// dropped, which can be done explicitly with [`Context::unregister_callback`].
113     ///
114     /// When handling a [method@Hotplug::device_arrived] event it is considered safe to call
115     /// any `rusb` function that takes a [`Device`]. It also safe to open a device and
116     /// submit **asynchronous** transfers.
117     /// However, most other functions that take a [`DeviceHandle`] are **not safe** to call.
118     /// Examples of such functions are any of the synchronous API functions or
119     /// the blocking functions that retrieve various USB descriptors.
120     /// These functions must be used outside of the context of the [Hotplug] functions.
121     ///
122     /// [`Device`]: crate::Device
123     /// [`DeviceHandle`]: crate::DeviceHandle
124     /// [`Context::unregister_callback`]: method@crate::Context::unregister_callback
register<U: UsbContext, T: Borrow<U>>( self, context: T, callback: Box<dyn Hotplug<U>>, ) -> crate::Result<Registration<U>>125     pub fn register<U: UsbContext, T: Borrow<U>>(
126         self,
127         context: T,
128         callback: Box<dyn Hotplug<U>>,
129     ) -> crate::Result<Registration<U>> {
130         let mut handle: libusb_hotplug_callback_handle = 0;
131         let mut call_back = Box::new(CallbackData {
132             context: context.borrow().clone(),
133             hotplug: callback,
134         });
135 
136         let hotplug_flags = if self.enumerate {
137             LIBUSB_HOTPLUG_ENUMERATE
138         } else {
139             LIBUSB_HOTPLUG_NO_FLAGS
140         };
141 
142         let user_data = &mut *call_back as *mut _ as *mut _;
143 
144         let n = unsafe {
145             libusb_hotplug_register_callback(
146                 context.borrow().as_raw(),
147                 LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
148                 hotplug_flags,
149                 self.vendor_id
150                     .map(c_int::from)
151                     .unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY),
152                 self.product_id
153                     .map(c_int::from)
154                     .unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY),
155                 self.class
156                     .map(c_int::from)
157                     .unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY),
158                 hotplug_callback::<U>,
159                 user_data,
160                 &mut handle,
161             )
162         };
163         if n < 0 {
164             Err(error::from_libusb(n))
165         } else {
166             Ok(Registration { handle, call_back })
167         }
168     }
169 }
170 
171 struct CallbackData<T: UsbContext> {
172     context: T,
173     hotplug: Box<dyn Hotplug<T>>,
174 }
175 
176 impl<T> Debug for CallbackData<T>
177 where
178     T: UsbContext + Debug,
179 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result180     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181         f.debug_struct("CallbackData")
182             .field("context", &self.context)
183             .finish()
184     }
185 }
186 
hotplug_callback<T: UsbContext>( _ctx: *mut libusb_context, device: *mut libusb_device, event: libusb_hotplug_event, user_data: *mut c_void, ) -> c_int187 pub extern "system" fn hotplug_callback<T: UsbContext>(
188     _ctx: *mut libusb_context,
189     device: *mut libusb_device,
190     event: libusb_hotplug_event,
191     user_data: *mut c_void,
192 ) -> c_int {
193     let ret = std::panic::catch_unwind(|| {
194         let reg = unsafe { &mut *(user_data as *mut CallbackData<T>) };
195         let device = unsafe {
196             Device::from_libusb(
197                 reg.context.clone(),
198                 std::ptr::NonNull::new_unchecked(device),
199             )
200         };
201         match event {
202             LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => reg.hotplug.device_arrived(device),
203             LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => reg.hotplug.device_left(device),
204             _ => (),
205         };
206     });
207     match ret {
208         Ok(_) => 0,
209         Err(_) => 1,
210     }
211 }
212