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