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")); 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/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 { 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,