From 307a31518fa44125d7d56d26048c904c5f5fda21 Mon Sep 17 00:00:00 2001 From: "L. E. Segovia" Date: Fri, 25 Aug 2023 22:04:23 -0300 Subject: [PATCH 1/3] Fix package detection and build when cross-compiling from MSVC to GNU I and @dragonmux found two issues when building libusb from a MSVC host to a MinGW target. When the build.rs script is built for the MSVC host, - the vcpkg crate is used for detection (which correctly detects the MinGW target and reports an unsupported ABI) - a MSVC flag is applied to the build (and thus breaking the build altogether) This commit fixes both issues by changing vcpkg to a Windows-wide dependency, and then if CARGO_CFG_TARGET_ENV is indeed MSVC, it is used and the /source-charset:utf-8 flag is applied. --- libusb1-sys/Cargo.toml | 2 +- libusb1-sys/build.rs | 35 +++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/libusb1-sys/Cargo.toml b/libusb1-sys/Cargo.toml index 519accf..2612e78 100644 --- a/libusb1-sys/Cargo.toml +++ b/libusb1-sys/Cargo.toml @@ -39,7 +39,7 @@ vendored = [] [dependencies] libc = "0.2" -[target.'cfg(target_env = "msvc")'.build-dependencies] +[target.'cfg(target_os = "windows")'.build-dependencies] vcpkg = "0.2" [build-dependencies] diff --git a/libusb1-sys/build.rs b/libusb1-sys/build.rs index 0fcad31..e3b16d2 100644 --- a/libusb1-sys/build.rs +++ b/libusb1-sys/build.rs @@ -19,21 +19,6 @@ pub fn link_framework(name: &str) { println!("cargo:rustc-link-lib=framework={}", name); } -#[cfg(target_env = "msvc")] -fn find_libusb_pkg(_statik: bool) -> bool { - match vcpkg::Config::new().find_package("libusb") { - Ok(_) => true, - Err(e) => { - if pkg_config::probe_library("libusb-1.0").is_ok() { - true - } else { - println!("Can't find libusb pkg: {:?}", e); - false - } - } - } -} - fn get_macos_major_version() -> Option { if !cfg!(target_os = "macos") { return None; @@ -49,8 +34,21 @@ fn get_macos_major_version() -> Option { Some(major) } -#[cfg(not(target_env = "msvc"))] fn find_libusb_pkg(statik: bool) -> bool { + if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) { + #[cfg(target_os = "windows")] + return match vcpkg::Config::new().find_package("libusb") { + Ok(_) => true, + Err(e) => { + if pkg_config::probe_library("libusb-1.0").is_ok() { + true + } else { + println!("Can't find libusb pkg: {:?}", e); + false + } + } + }; + } // https://github.com/rust-lang/rust/issues/96943 let needs_rustc_issue_96943_workaround: bool = get_macos_major_version() .map(|major| major >= 11) @@ -175,8 +173,9 @@ fn make_source() { } if std::env::var("CARGO_CFG_TARGET_OS") == Ok("windows".into()) { - #[cfg(target_env = "msvc")] - base_config.flag("/source-charset:utf-8"); + if std::env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".into()) { + base_config.flag("/source-charset:utf-8"); + } base_config.warnings(false); base_config.define("OS_WINDOWS", Some("1")); From 7f5023e71443273eb33fa8a6c86c8a9d67db1496 Mon Sep 17 00:00:00 2001 From: Dmitry Zakablukov Date: Wed, 28 Feb 2024 12:12:42 +0300 Subject: [PATCH 2/3] Log callback API added --- src/context.rs | 82 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 2 +- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/context.rs b/src/context.rs index 07efe87..2add6b3 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,6 +1,9 @@ -use libc::{c_int, timeval}; +use libc::{c_char, c_int, c_void, timeval}; -use std::{cmp::Ordering, mem, ptr, sync::Arc, sync::Once, time::Duration}; +use std::{ + cmp::Ordering, ffi::CStr, mem, ptr, sync::Arc, sync::Mutex, sync::Once, sync::OnceLock, + time::Duration, +}; #[cfg(unix)] use std::os::unix::io::RawFd; @@ -45,6 +48,43 @@ impl Drop for ContextInner { unsafe impl Sync for Context {} unsafe impl Send for Context {} +type LogCallback = Box; + +struct LogCallbackMap { + map: std::collections::HashMap<*mut libusb_context, LogCallback>, +} + +unsafe impl Sync for LogCallbackMap {} +unsafe impl Send for LogCallbackMap {} + +impl LogCallbackMap { + pub fn new() -> Self { + Self { + map: std::collections::HashMap::new(), + } + } +} + +static LOG_CALLBACK_MAP: OnceLock> = OnceLock::new(); + +extern "system" fn static_log_callback( + context: *mut libusb_context, + level: c_int, + text: *mut c_void, +) { + if let Some(log_callback_map) = LOG_CALLBACK_MAP.get() { + if let Ok(locked_table) = log_callback_map.lock() { + if let Some(logger) = locked_table.map.get(&context) { + let c_str: &CStr = unsafe { CStr::from_ptr(text as *const c_char) }; + let str_slice: &str = c_str.to_str().unwrap_or(""); + let log_message = str_slice.to_owned(); + + logger(LogLevel::from_c_int(level), log_message); + } + } + } +} + pub trait UsbContext: Clone + Sized + Send + Sync { /// Get the raw libusb_context pointer, for advanced use in unsafe code. fn as_raw(&self) -> *mut libusb_context; @@ -104,6 +144,17 @@ pub trait UsbContext: Clone + Sized + Send + Sync { } } + fn set_log_callback(&mut self, log_callback: LogCallback, mode: LogCallbackMode) { + let log_callback_map = LOG_CALLBACK_MAP.get_or_init(|| Mutex::new(LogCallbackMap::new())); + if let Ok(mut locked_table) = log_callback_map.lock() { + locked_table.map.insert(self.as_raw(), log_callback); + } + + unsafe { + libusb_set_log_cb(self.as_raw(), Some(static_log_callback), mode.as_c_int()); + } + } + /// Register a callback to be called on hotplug events. The callback's /// [Hotplug::device_arrived] method is called when a new device is added to /// the bus, and [Hotplug::device_left] is called when it is removed. @@ -292,4 +343,31 @@ impl LogLevel { LogLevel::Debug => LIBUSB_LOG_LEVEL_DEBUG, } } + + fn from_c_int(value: c_int) -> LogLevel { + match value { + LIBUSB_LOG_LEVEL_ERROR => LogLevel::Error, + LIBUSB_LOG_LEVEL_WARNING => LogLevel::Warning, + LIBUSB_LOG_LEVEL_INFO => LogLevel::Info, + LIBUSB_LOG_LEVEL_DEBUG => LogLevel::Debug, + _ => LogLevel::None, + } + } +} + +pub enum LogCallbackMode { + /// Callback function handling all log messages. + Global, + + /// Callback function handling context related log messages. + Context, +} + +impl LogCallbackMode { + fn as_c_int(&self) -> c_int { + match *self { + LogCallbackMode::Global => LIBUSB_LOG_CB_GLOBAL, + LogCallbackMode::Context => LIBUSB_LOG_CB_CONTEXT, + } + } } diff --git a/src/lib.rs b/src/lib.rs index 10165d2..4c16277 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ pub use libusb1_sys::constants; pub use crate::options::disable_device_discovery; pub use crate::{ config_descriptor::{ConfigDescriptor, Interfaces}, - context::{Context, GlobalContext, LogLevel, UsbContext}, + context::{Context, GlobalContext, LogCallbackMode, LogLevel, UsbContext}, device::Device, device_descriptor::DeviceDescriptor, device_handle::DeviceHandle, From 5e8e36e4e3328c5f5a2753b0fac633b2d0ebd0ff Mon Sep 17 00:00:00 2001 From: alufers Date: Tue, 5 Mar 2024 15:41:32 +0100 Subject: [PATCH 3/3] fix: panic when trying to iterate over an interface with zero endpoints Some USB classes (UVC, UAC) provide alternate setting descriptors of interfaces with zero endpoints. Trying to call `endpoint_descriptors` on them caused a panic, because NULL was passed to `slice::from_raw_parts`. A branch was introduced to check for that case and create an iterator over an empty slice. See: https://stackoverflow.com/a/49244874/2948315 --- src/interface_descriptor.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/interface_descriptor.rs b/src/interface_descriptor.rs index eb6419f..d4c64c3 100644 --- a/src/interface_descriptor.rs +++ b/src/interface_descriptor.rs @@ -101,11 +101,9 @@ impl<'a> InterfaceDescriptor<'a> { /// Returns an iterator over the interface's endpoint descriptors. pub fn endpoint_descriptors(&self) -> EndpointDescriptors<'a> { - let endpoints = unsafe { - slice::from_raw_parts( - self.descriptor.endpoint, - self.descriptor.bNumEndpoints as usize, - ) + let endpoints = match self.descriptor.bNumEndpoints { + 0 => &[], + n => unsafe { slice::from_raw_parts(self.descriptor.endpoint, n as usize) }, }; EndpointDescriptors {