Skip to content

Commit

Permalink
Merge pull request #194 from passware/log_callback_api
Browse files Browse the repository at this point in the history
Log callback API added
  • Loading branch information
a1ien authored Mar 5, 2024
2 parents aa11ec9 + 7f5023e commit 145c12c
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 3 deletions.
82 changes: 80 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -45,6 +48,43 @@ impl Drop for ContextInner {
unsafe impl Sync for Context {}
unsafe impl Send for Context {}

type LogCallback = Box<dyn Fn(LogLevel, String)>;

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<Mutex<LogCallbackMap>> = 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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
}
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 145c12c

Please sign in to comment.