1 use libc::{c_char, c_int, c_void, timeval};
2 
3 use std::{
4     cmp::Ordering, ffi::CStr, mem, ptr, sync::Arc, sync::Mutex, sync::Once, sync::OnceLock,
5     time::Duration,
6 };
7 
8 #[cfg(unix)]
9 use std::os::unix::io::RawFd;
10 
11 use crate::hotplug::{Hotplug, HotplugBuilder, Registration};
12 use crate::{device_handle::DeviceHandle, device_list::DeviceList, error};
13 use libusb1_sys::{constants::*, *};
14 
15 #[cfg(windows)]
16 type Seconds = ::libc::c_long;
17 #[cfg(windows)]
18 type MicroSeconds = ::libc::c_long;
19 
20 #[cfg(not(windows))]
21 type Seconds = ::libc::time_t;
22 #[cfg(not(windows))]
23 type MicroSeconds = ::libc::suseconds_t;
24 
25 #[derive(Copy, Clone, Eq, PartialEq, Default)]
26 pub struct GlobalContext {}
27 
28 /// A `libusb` context.
29 #[derive(Clone, Debug, Eq, PartialEq)]
30 pub struct Context {
31     context: Arc<ContextInner>,
32 }
33 
34 #[derive(Debug, Eq, PartialEq)]
35 struct ContextInner {
36     inner: ptr::NonNull<libusb_context>,
37 }
38 
39 impl Drop for ContextInner {
40     /// Closes the `libusb` context.
drop(&mut self)41     fn drop(&mut self) {
42         unsafe {
43             libusb_exit(self.inner.as_ptr());
44         }
45     }
46 }
47 
48 unsafe impl Sync for Context {}
49 unsafe impl Send for Context {}
50 
51 type LogCallback = Box<dyn Fn(LogLevel, String)>;
52 
53 struct LogCallbackMap {
54     map: std::collections::HashMap<*mut libusb_context, LogCallback>,
55 }
56 
57 unsafe impl Sync for LogCallbackMap {}
58 unsafe impl Send for LogCallbackMap {}
59 
60 impl LogCallbackMap {
new() -> Self61     pub fn new() -> Self {
62         Self {
63             map: std::collections::HashMap::new(),
64         }
65     }
66 }
67 
68 static LOG_CALLBACK_MAP: OnceLock<Mutex<LogCallbackMap>> = OnceLock::new();
69 
static_log_callback( context: *mut libusb_context, level: c_int, text: *mut c_void, )70 extern "system" fn static_log_callback(
71     context: *mut libusb_context,
72     level: c_int,
73     text: *mut c_void,
74 ) {
75     if let Some(log_callback_map) = LOG_CALLBACK_MAP.get() {
76         if let Ok(locked_table) = log_callback_map.lock() {
77             if let Some(logger) = locked_table.map.get(&context) {
78                 let c_str: &CStr = unsafe { CStr::from_ptr(text as *const c_char) };
79                 let str_slice: &str = c_str.to_str().unwrap_or("");
80                 let log_message = str_slice.to_owned();
81 
82                 logger(LogLevel::from_c_int(level), log_message);
83             }
84         }
85     }
86 }
87 
88 pub trait UsbContext: Clone + Sized + Send + Sync {
89     /// Get the raw libusb_context pointer, for advanced use in unsafe code.
as_raw(&self) -> *mut libusb_context90     fn as_raw(&self) -> *mut libusb_context;
91 
92     /// Returns a list of the current USB devices.
devices(&self) -> crate::Result<DeviceList<Self>>93     fn devices(&self) -> crate::Result<DeviceList<Self>> {
94         DeviceList::new_with_context(self.clone())
95     }
96 
97     /// Convenience function to open a device by its vendor ID and product ID.
98     ///
99     /// This function is provided as a convenience for building prototypes without having to
100     /// iterate a [`DeviceList`](struct.DeviceList.html). It is not meant for production
101     /// applications.
102     ///
103     /// Returns a device handle for the first device found matching `vendor_id` and `product_id`.
104     /// On error, or if the device could not be found, it returns `None`.
open_device_with_vid_pid( &self, vendor_id: u16, product_id: u16, ) -> Option<DeviceHandle<Self>>105     fn open_device_with_vid_pid(
106         &self,
107         vendor_id: u16,
108         product_id: u16,
109     ) -> Option<DeviceHandle<Self>> {
110         let handle =
111             unsafe { libusb_open_device_with_vid_pid(self.as_raw(), vendor_id, product_id) };
112         let ptr = std::ptr::NonNull::new(handle)?;
113         Some(unsafe { DeviceHandle::from_libusb(self.clone(), ptr) })
114     }
115 
116     /// Opens the device with a pre-opened file descriptor.
117     ///
118     /// This is UNIX-only and platform-specific. It is currently working with
119     /// Linux/Android, but might work with other systems in the future.
120     ///
121     /// Note: This function does not take ownership of the specified file
122     /// descriptor. The caller has the responsibility of keeping it opened for
123     /// as long as the device handle.
124     #[cfg(unix)]
125     #[doc(alias = "libusb_wrap_sys_device")]
open_device_with_fd(&self, fd: RawFd) -> crate::Result<DeviceHandle<Self>>126     unsafe fn open_device_with_fd(&self, fd: RawFd) -> crate::Result<DeviceHandle<Self>> {
127         let mut handle = mem::MaybeUninit::<*mut libusb_device_handle>::uninit();
128 
129         match libusb_wrap_sys_device(self.as_raw(), fd as _, handle.as_mut_ptr()) {
130             0 => {
131                 let ptr =
132                     std::ptr::NonNull::new(handle.assume_init()).ok_or(crate::Error::NoDevice)?;
133 
134                 Ok(DeviceHandle::from_libusb(self.clone(), ptr))
135             }
136             err => Err(error::from_libusb(err)),
137         }
138     }
139 
140     /// Sets the log level of a `libusb` for context.
set_log_level(&mut self, level: LogLevel)141     fn set_log_level(&mut self, level: LogLevel) {
142         unsafe {
143             libusb_set_debug(self.as_raw(), level.as_c_int());
144         }
145     }
146 
set_log_callback(&mut self, log_callback: LogCallback, mode: LogCallbackMode)147     fn set_log_callback(&mut self, log_callback: LogCallback, mode: LogCallbackMode) {
148         let log_callback_map = LOG_CALLBACK_MAP.get_or_init(|| Mutex::new(LogCallbackMap::new()));
149         if let Ok(mut locked_table) = log_callback_map.lock() {
150             locked_table.map.insert(self.as_raw(), log_callback);
151         }
152 
153         unsafe {
154             libusb_set_log_cb(self.as_raw(), Some(static_log_callback), mode.as_c_int());
155         }
156     }
157 
158     /// Register a callback to be called on hotplug events. The callback's
159     /// [Hotplug::device_arrived] method is called when a new device is added to
160     /// the bus, and [Hotplug::device_left] is called when it is removed.
161     ///
162     /// Devices can optionally be filtered by vendor (`vendor_id`) and device id
163     /// (`product_id`).
164     ///
165     /// The callback will remain registered until the returned [Registration] is
166     /// dropped, which can be done explicitly with [Context::unregister_callback].
167     ///
168     /// When handling a [Hotplug::device_arrived] event it is considered safe to call
169     /// any `rusb` function that takes a [crate::Device]. It also safe to open a device and
170     /// submit **asynchronous** transfers.
171     /// However, most other functions that take a [DeviceHandle] are **not safe** to call.
172     /// Examples of such functions are any of the synchronous API functions or
173     /// the blocking functions that retrieve various USB descriptors.
174     /// These functions must be used outside of the context of the [Hotplug] functions.
175     #[deprecated(since = "0.9.0", note = "Use HotplugBuilder")]
register_callback( &self, vendor_id: Option<u16>, product_id: Option<u16>, class: Option<u8>, callback: Box<dyn Hotplug<Self>>, ) -> crate::Result<Registration<Self>>176     fn register_callback(
177         &self,
178         vendor_id: Option<u16>,
179         product_id: Option<u16>,
180         class: Option<u8>,
181         callback: Box<dyn Hotplug<Self>>,
182     ) -> crate::Result<Registration<Self>> {
183         let mut builder = HotplugBuilder::new();
184 
185         let mut builder = &mut builder;
186         if let Some(vendor_id) = vendor_id {
187             builder = builder.vendor_id(vendor_id)
188         }
189         if let Some(product_id) = product_id {
190             builder = builder.product_id(product_id)
191         }
192         if let Some(class) = class {
193             builder = builder.class(class)
194         }
195 
196         builder.register(self, callback)
197     }
198 
199     /// Unregisters the callback corresponding to the given registration. The
200     /// same thing can be achieved by dropping the registration.
unregister_callback(&self, _reg: Registration<Self>)201     fn unregister_callback(&self, _reg: Registration<Self>) {}
202 
203     /// Handle any pending events.
204     /// If timeout less then 1 microseconds then this function will handle any already-pending
205     /// events and then immediately return in non-blocking style.
206     /// If timeout is [None] then function will handle any pending events in blocking mode.
handle_events(&self, timeout: Option<Duration>) -> crate::Result<()>207     fn handle_events(&self, timeout: Option<Duration>) -> crate::Result<()> {
208         let n = unsafe {
209             match timeout {
210                 Some(t) => {
211                     let tv = timeval {
212                         tv_sec: t.as_secs() as Seconds,
213                         tv_usec: t.subsec_nanos() as MicroSeconds / 1000,
214                     };
215                     libusb_handle_events_timeout_completed(self.as_raw(), &tv, ptr::null_mut())
216                 }
217                 None => libusb_handle_events_completed(self.as_raw(), ptr::null_mut()),
218             }
219         };
220         if n < 0 {
221             Err(error::from_libusb(n as c_int))
222         } else {
223             Ok(())
224         }
225     }
226 
227     /// Interrupt any active thread that is handling events (for example with
228     /// [handle_events][`Self::handle_events()`]).
229     #[doc(alias = "libusb_interrupt_event_handler")]
interrupt_handle_events(&self)230     fn interrupt_handle_events(&self) {
231         unsafe { libusb_interrupt_event_handler(self.as_raw()) }
232     }
233 
next_timeout(&self) -> crate::Result<Option<Duration>>234     fn next_timeout(&self) -> crate::Result<Option<Duration>> {
235         let mut tv = timeval {
236             tv_sec: 0,
237             tv_usec: 0,
238         };
239         let n = unsafe { libusb_get_next_timeout(self.as_raw(), &mut tv) };
240 
241         match n.cmp(&0) {
242             Ordering::Less => Err(error::from_libusb(n as c_int)),
243             Ordering::Equal => Ok(None),
244             Ordering::Greater => {
245                 let duration = Duration::new(tv.tv_sec as _, (tv.tv_usec * 1000) as _);
246                 Ok(Some(duration))
247             }
248         }
249     }
250 }
251 
252 impl UsbContext for Context {
as_raw(&self) -> *mut libusb_context253     fn as_raw(&self) -> *mut libusb_context {
254         self.context.inner.as_ptr()
255     }
256 }
257 
258 impl UsbContext for GlobalContext {
as_raw(&self) -> *mut libusb_context259     fn as_raw(&self) -> *mut libusb_context {
260         static mut USB_CONTEXT: *mut libusb_context = ptr::null_mut();
261         static ONCE: Once = Once::new();
262 
263         ONCE.call_once(|| {
264             let mut context = mem::MaybeUninit::<*mut libusb_context>::uninit();
265             unsafe {
266                 USB_CONTEXT = match libusb_init(context.as_mut_ptr()) {
267                     0 => context.assume_init(),
268                     err => panic!(
269                         "Can't init Global usb context, error {:?}",
270                         error::from_libusb(err)
271                     ),
272                 }
273             };
274         });
275         // Clone data that is safe to use concurrently.
276         unsafe { USB_CONTEXT }
277     }
278 }
279 
280 impl Context {
281     /// Opens a new `libusb` context.
new() -> crate::Result<Self>282     pub fn new() -> crate::Result<Self> {
283         let mut context = mem::MaybeUninit::<*mut libusb_context>::uninit();
284 
285         try_unsafe!(libusb_init(context.as_mut_ptr()));
286 
287         Ok(unsafe { Self::from_raw(context.assume_init()) })
288     }
289 
290     /// Creates a new `libusb` context and sets runtime options.
with_options(opts: &[crate::UsbOption]) -> crate::Result<Self>291     pub fn with_options(opts: &[crate::UsbOption]) -> crate::Result<Self> {
292         let mut this = Self::new()?;
293 
294         for opt in opts {
295             opt.apply(&mut this)?;
296         }
297 
298         Ok(this)
299     }
300 
301     /// Creates rusb Context from existing libusb context.
302     /// Note: This transfers ownership of the context to Rust.
303     /// # Safety
304     /// This is unsafe because it does not check if the context is valid,
305     /// so the caller must guarantee that libusb_context is created properly.
from_raw(raw: *mut libusb_context) -> Self306     pub unsafe fn from_raw(raw: *mut libusb_context) -> Self {
307         Context {
308             context: Arc::new(ContextInner {
309                 inner: ptr::NonNull::new_unchecked(raw),
310             }),
311         }
312     }
313 }
314 
315 /// Library logging levels.
316 #[derive(Clone, Copy)]
317 pub enum LogLevel {
318     /// No messages are printed by `libusb` (default).
319     None,
320 
321     /// Error messages printed to `stderr`.
322     Error,
323 
324     /// Warning and error messages are printed to `stderr`.
325     Warning,
326 
327     /// Informational messages are printed to `stdout`. Warnings and error messages are printed to
328     /// `stderr`.
329     Info,
330 
331     /// Debug and informational messages are printed to `stdout`. Warnings and error messages are
332     /// printed to `stderr`.
333     Debug,
334 }
335 
336 impl LogLevel {
as_c_int(self) -> c_int337     pub(crate) fn as_c_int(self) -> c_int {
338         match self {
339             LogLevel::None => LIBUSB_LOG_LEVEL_NONE,
340             LogLevel::Error => LIBUSB_LOG_LEVEL_ERROR,
341             LogLevel::Warning => LIBUSB_LOG_LEVEL_WARNING,
342             LogLevel::Info => LIBUSB_LOG_LEVEL_INFO,
343             LogLevel::Debug => LIBUSB_LOG_LEVEL_DEBUG,
344         }
345     }
346 
from_c_int(value: c_int) -> LogLevel347     fn from_c_int(value: c_int) -> LogLevel {
348         match value {
349             LIBUSB_LOG_LEVEL_ERROR => LogLevel::Error,
350             LIBUSB_LOG_LEVEL_WARNING => LogLevel::Warning,
351             LIBUSB_LOG_LEVEL_INFO => LogLevel::Info,
352             LIBUSB_LOG_LEVEL_DEBUG => LogLevel::Debug,
353             _ => LogLevel::None,
354         }
355     }
356 }
357 
358 pub enum LogCallbackMode {
359     /// Callback function handling all log messages.
360     Global,
361 
362     /// Callback function handling context related log messages.
363     Context,
364 }
365 
366 impl LogCallbackMode {
as_c_int(&self) -> c_int367     fn as_c_int(&self) -> c_int {
368         match *self {
369             LogCallbackMode::Global => LIBUSB_LOG_CB_GLOBAL,
370             LogCallbackMode::Context => LIBUSB_LOG_CB_CONTEXT,
371         }
372     }
373 }
374