Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log callback API added #194

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading