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