From 9c41dff68557f7447950a2ecc0fcbd9b5c22a952 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Fri, 5 Feb 2021 18:57:05 -0800 Subject: [PATCH 1/8] WIP --- Cargo.toml | 2 +- src/config_descriptor.rs | 1 + src/context.rs | 71 ++++++++++++++++++++++++++++++++++++++-- src/device.rs | 2 ++ src/device_handle.rs | 1 + src/device_list.rs | 1 + src/lib.rs | 2 ++ 7 files changed, 77 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 577deec..ea5a909 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ keywords = ["usb", "libusb", "hardware", "bindings"] [dependencies] bit-set = "0.2.0" -libusb-sys = "0.2.3" +libusb-sys = { path = "../libusb-sys" } libc = "0.2" [dev-dependencies] diff --git a/src/config_descriptor.rs b/src/config_descriptor.rs index c982f2e..bdfd626 100644 --- a/src/config_descriptor.rs +++ b/src/config_descriptor.rs @@ -13,6 +13,7 @@ pub struct ConfigDescriptor { impl Drop for ConfigDescriptor { fn drop(&mut self) { + eprintln!("Dropping a config descriptor"); unsafe { libusb_free_config_descriptor(self.descriptor); } diff --git a/src/context.rs b/src/context.rs index 4033cb4..9dbd970 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,22 +1,32 @@ use std::marker::PhantomData; -use std::mem; +use std::mem::{self, ManuallyDrop}; use libc::c_int; use libusb::*; +use device::{self, Device}; use device_list::{self, DeviceList}; use device_handle::{self, DeviceHandle}; use error; +use event::HotPlugEvent; +use hotplug::{CallbackWrapper, HotplugFilter}; +use std::pin::Pin; /// A `libusb` context. pub struct Context { context: *mut libusb_context, + cbs: Vec>>, } impl Drop for Context { /// Closes the `libusb` context. fn drop(&mut self) { + eprintln!("Dropping a ctx"); + unsafe { + for ref cb in &self.cbs { + // TODO(richo) Deregister the callback + } libusb_exit(self.context); } } @@ -32,7 +42,10 @@ impl Context { try_unsafe!(libusb_init(&mut context)); - Ok(Context { context: context }) + Ok(Context { + context: context, + cbs: vec![], + }) } /// Sets the log level of a `libusb` context. @@ -83,6 +96,38 @@ impl Context { } } + + /// Register a callback to fire when a device attached or removed. + pub fn register_callback(&mut self, filter: HotplugFilter, closure: F) -> ::Result<()> + where F: Fn(&Device, HotPlugEvent) + 'static { + let mut wrapper = Box::pin(CallbackWrapper { + cb: Box::new(closure), + handle: 0, + }); + let mut handle = 0; + let res = unsafe { libusb_hotplug_register_callback( + self.context, + filter.get_events(), + LIBUSB_HOTPLUG_ENUMERATE, + filter.get_vendor(), + filter.get_product(), + filter.get_class(), + invoke_callback, + &mut *wrapper as *mut _ as *mut ::std::ffi::c_void, + &mut handle) + }; + if res != LIBUSB_SUCCESS { + panic!("Couldn't setup callback"); + } + wrapper.handle = handle; + self.cbs.push(wrapper); + Ok(()) + } + + pub fn handle_events(&self) { + unsafe { libusb_handle_events(self.context) }; + } + /// Convenience function to open a device by its vendor ID and product ID. /// /// This function is provided as a convenience for building prototypes without having to @@ -103,6 +148,28 @@ impl Context { } } +extern "C" fn invoke_callback(_ctx: *mut libusb_context, device: *const libusb_device, event: i32, closure: *mut std::ffi::c_void) -> i32 { + eprintln!("ffi invoked"); + let parsed = match event { + e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => HotPlugEvent::Arrived, + e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => HotPlugEvent::Left, + _ => { + // warn!("Unknown event type: {}", e); + return 0; + }, + }; + + let device = ManuallyDrop::new(unsafe { device::from_libusb(PhantomData, device as *mut libusb_device) }); + + let cb = closure as *mut CallbackWrapper; + + eprintln!("Handle: {}", unsafe { &(*cb).handle}) ; + + unsafe { ((*cb).cb)(&device, parsed) }; + + 0 +} + /// Library logging levels. pub enum LogLevel { diff --git a/src/device.rs b/src/device.rs index cbfda2b..fed2fcc 100644 --- a/src/device.rs +++ b/src/device.rs @@ -11,6 +11,7 @@ use fields::{self, Speed}; /// A reference to a USB device. +#[derive(Debug)] pub struct Device<'a> { context: PhantomData<&'a Context>, device: *mut libusb_device, @@ -19,6 +20,7 @@ pub struct Device<'a> { impl<'a> Drop for Device<'a> { /// Releases the device reference. fn drop(&mut self) { + eprintln!("Dropping a device"); unsafe { libusb_unref_device(self.device); } diff --git a/src/device_handle.rs b/src/device_handle.rs index eb9c360..f0d8e70 100644 --- a/src/device_handle.rs +++ b/src/device_handle.rs @@ -25,6 +25,7 @@ pub struct DeviceHandle<'a> { impl<'a> Drop for DeviceHandle<'a> { /// Closes the device. fn drop(&mut self) { + eprintln!("Dropping a device handle"); unsafe { for iface in self.interfaces.iter() { libusb_release_interface(self.handle, iface as c_int); diff --git a/src/device_list.rs b/src/device_list.rs index 8a22e54..da25a91 100644 --- a/src/device_list.rs +++ b/src/device_list.rs @@ -16,6 +16,7 @@ pub struct DeviceList<'a> { impl<'a> Drop for DeviceList<'a> { /// Frees the device list. fn drop(&mut self) { + eprintln!("Dropping a device lisst"); unsafe { libusb_free_device_list(self.list, 1); } diff --git a/src/lib.rs b/src/lib.rs index 55cb3b4..8e756de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ mod context; mod device_list; mod device; mod device_handle; +mod event; mod fields; mod device_descriptor; @@ -39,3 +40,4 @@ mod config_descriptor; mod interface_descriptor; mod endpoint_descriptor; mod language; +mod hotplug; From 99df435813a2c0740345077d481dfebf586e3eac Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Fri, 5 Feb 2021 18:57:11 -0800 Subject: [PATCH 2/8] Add an example --- examples/hotplug_events.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/hotplug_events.rs diff --git a/examples/hotplug_events.rs b/examples/hotplug_events.rs new file mode 100644 index 0000000..b91f268 --- /dev/null +++ b/examples/hotplug_events.rs @@ -0,0 +1,27 @@ +extern crate libusb; + +use std::error::Error; +use std::slice; +use std::str::FromStr; +use std::time::Duration; + +#[derive(Debug)] +struct Endpoint { + config: u8, + iface: u8, + setting: u8, + address: u8 +} + +fn main() -> Result<(), Box> { + let mut ctx = libusb::Context::new()?; + ctx.register_callback(Default::default(), |device, event| { + eprintln!("invoked"); + println!("{:?} - {:?}", device.device_descriptor(), event); + }); + loop { + ctx.handle_events(); + } + Ok(()) +} + From b262b0e66595232670ec985cc28f8b9224e23cee Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Fri, 5 Feb 2021 18:58:55 -0800 Subject: [PATCH 3/8] Add the other files I forgot --- src/event.rs | 5 ++++ src/hotplug.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/event.rs create mode 100644 src/hotplug.rs diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..1443b15 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, Copy)] +pub enum HotPlugEvent { + Arrived, + Left, +} diff --git a/src/hotplug.rs b/src/hotplug.rs new file mode 100644 index 0000000..bb01d3b --- /dev/null +++ b/src/hotplug.rs @@ -0,0 +1,71 @@ +use device::Device; +use event::HotPlugEvent; + +use libusb::*; + +pub struct CallbackWrapper { + pub cb: Box, + pub handle: i32, +} + +#[derive(Default)] +pub struct HotplugFilter { + vendor: Option, + product: Option, + class: Option, + events: Option, +} + +impl HotplugFilter { + pub fn new() -> Self { + Self { + vendor: None, + product: None, + class: None, + events: None, + } + } + + pub(crate) fn get_vendor(&self) -> i32 { + self.vendor.unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY) + } + + pub(crate) fn get_product(&self) -> i32 { + self.product.unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY) + } + + pub(crate) fn get_class(&self) -> i32 { + self.class.unwrap_or(LIBUSB_HOTPLUG_MATCH_ANY) + } + + pub(crate) fn get_events(&self) -> i32 { + self.events.unwrap_or(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + } + + pub fn vendor(mut self, vendor: i32) -> Self { + self.vendor = Some(vendor); + self + } + + pub fn product(mut self, product: i32) -> Self { + self.product = Some(product); + self + } + + pub fn class(mut self, class: i32) -> Self { + self.class = Some(class); + self + } + + pub fn arrived_only(mut self) -> Self { + self.events = Some(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); + self + } + + pub fn left_only(mut self) -> Self { + self.events = Some(LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); + self + } +} + + From 1651f59cf1263516513c6efaab32d00065014eee Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Sat, 6 Feb 2021 22:03:34 -0800 Subject: [PATCH 4/8] Start cleaning up the implem,entation --- examples/hotplug_events.rs | 2 +- src/context.rs | 12 +++++------- src/hotplug.rs | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/hotplug_events.rs b/examples/hotplug_events.rs index b91f268..cfbeecd 100644 --- a/examples/hotplug_events.rs +++ b/examples/hotplug_events.rs @@ -18,7 +18,7 @@ fn main() -> Result<(), Box> { ctx.register_callback(Default::default(), |device, event| { eprintln!("invoked"); println!("{:?} - {:?}", device.device_descriptor(), event); - }); + })?; loop { ctx.handle_events(); } diff --git a/src/context.rs b/src/context.rs index 9dbd970..c2d1e6e 100644 --- a/src/context.rs +++ b/src/context.rs @@ -148,24 +148,22 @@ impl Context { } } -extern "C" fn invoke_callback(_ctx: *mut libusb_context, device: *const libusb_device, event: i32, closure: *mut std::ffi::c_void) -> i32 { - eprintln!("ffi invoked"); +extern "C" fn invoke_callback(_ctx: *mut libusb_context, device: *const libusb_device, event: i32, data: *mut std::ffi::c_void) -> i32 { let parsed = match event { e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => HotPlugEvent::Arrived, e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => HotPlugEvent::Left, _ => { - // warn!("Unknown event type: {}", e); + // With no meaningful way to signal this error condition we simply don't dispatch the + // call and return. return 0; }, }; let device = ManuallyDrop::new(unsafe { device::from_libusb(PhantomData, device as *mut libusb_device) }); - let cb = closure as *mut CallbackWrapper; + let wrapper = data as *mut CallbackWrapper; - eprintln!("Handle: {}", unsafe { &(*cb).handle}) ; - - unsafe { ((*cb).cb)(&device, parsed) }; + unsafe { ((*wrapper).closure)(&device, parsed) }; 0 } diff --git a/src/hotplug.rs b/src/hotplug.rs index bb01d3b..26d5865 100644 --- a/src/hotplug.rs +++ b/src/hotplug.rs @@ -4,7 +4,7 @@ use event::HotPlugEvent; use libusb::*; pub struct CallbackWrapper { - pub cb: Box, + pub closure: Box, pub handle: i32, } From dc1b197f09f2fd5f3b70fb3746ff67b3d966363c Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Sat, 6 Feb 2021 22:10:01 -0800 Subject: [PATCH 5/8] Continue cleaning --- src/context.rs | 29 ++++++++++++++--------------- src/event.rs | 12 ++++++++++++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/context.rs b/src/context.rs index c2d1e6e..72416b6 100644 --- a/src/context.rs +++ b/src/context.rs @@ -101,7 +101,7 @@ impl Context { pub fn register_callback(&mut self, filter: HotplugFilter, closure: F) -> ::Result<()> where F: Fn(&Device, HotPlugEvent) + 'static { let mut wrapper = Box::pin(CallbackWrapper { - cb: Box::new(closure), + closure: Box::new(closure), handle: 0, }); let mut handle = 0; @@ -149,23 +149,22 @@ impl Context { } extern "C" fn invoke_callback(_ctx: *mut libusb_context, device: *const libusb_device, event: i32, data: *mut std::ffi::c_void) -> i32 { - let parsed = match event { - e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => HotPlugEvent::Arrived, - e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => HotPlugEvent::Left, - _ => { - // With no meaningful way to signal this error condition we simply don't dispatch the - // call and return. - return 0; - }, - }; - - let device = ManuallyDrop::new(unsafe { device::from_libusb(PhantomData, device as *mut libusb_device) }); + match HotPlugEvent::from_i32(event) { + Some(event) => { + let device = ManuallyDrop::new(unsafe { device::from_libusb(PhantomData, device as *mut libusb_device) }); - let wrapper = data as *mut CallbackWrapper; + let wrapper = data as *mut CallbackWrapper; - unsafe { ((*wrapper).closure)(&device, parsed) }; + unsafe { ((*wrapper).closure)(&device, event) }; - 0 + 0 + }, + None => { + // With no meaningful way to signal this error condition we simply don't dispatch the + // call and return. + return 0; + } + } } diff --git a/src/event.rs b/src/event.rs index 1443b15..14de886 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,5 +1,17 @@ +use libusb::*; + #[derive(Debug, Clone, Copy)] pub enum HotPlugEvent { Arrived, Left, } + +impl HotPlugEvent { + pub fn from_i32(value: i32) -> Option { + match value { + e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => Some(HotPlugEvent::Arrived), + e if e == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => Some(HotPlugEvent::Left), + _ => None, + } + } +} From efa84f8d96f8e78c58d2e6e410795226d0c5dc44 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Sat, 6 Feb 2021 22:14:44 -0800 Subject: [PATCH 6/8] Expose the filter api --- examples/hotplug_events.rs | 4 +++- src/context.rs | 2 +- src/hotplug.rs | 15 +++++++++++++++ src/lib.rs | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/examples/hotplug_events.rs b/examples/hotplug_events.rs index cfbeecd..18a74fd 100644 --- a/examples/hotplug_events.rs +++ b/examples/hotplug_events.rs @@ -15,7 +15,9 @@ struct Endpoint { fn main() -> Result<(), Box> { let mut ctx = libusb::Context::new()?; - ctx.register_callback(Default::default(), |device, event| { + let filter = libusb::HotplugFilter::new() + .enumerate(); + ctx.register_callback(filter, |device, event| { eprintln!("invoked"); println!("{:?} - {:?}", device.device_descriptor(), event); })?; diff --git a/src/context.rs b/src/context.rs index 72416b6..7bc8232 100644 --- a/src/context.rs +++ b/src/context.rs @@ -108,7 +108,7 @@ impl Context { let res = unsafe { libusb_hotplug_register_callback( self.context, filter.get_events(), - LIBUSB_HOTPLUG_ENUMERATE, + filter.get_flags(), filter.get_vendor(), filter.get_product(), filter.get_class(), diff --git a/src/hotplug.rs b/src/hotplug.rs index 26d5865..e3b820b 100644 --- a/src/hotplug.rs +++ b/src/hotplug.rs @@ -14,6 +14,7 @@ pub struct HotplugFilter { product: Option, class: Option, events: Option, + enumerate: bool, } impl HotplugFilter { @@ -23,6 +24,7 @@ impl HotplugFilter { product: None, class: None, events: None, + enumerate: false, } } @@ -42,6 +44,14 @@ impl HotplugFilter { self.events.unwrap_or(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) } + pub(crate) fn get_flags(&self) -> i32 { + if self.enumerate { + LIBUSB_HOTPLUG_ENUMERATE + } else { + 0 + } + } + pub fn vendor(mut self, vendor: i32) -> Self { self.vendor = Some(vendor); self @@ -66,6 +76,11 @@ impl HotplugFilter { self.events = Some(LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); self } + + pub fn enumerate(mut self) -> Self { + self.enumerate = true; + self + } } diff --git a/src/lib.rs b/src/lib.rs index 8e756de..22ed3bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub use context::{Context, LogLevel}; pub use device_list::{DeviceList, Devices}; pub use device::Device; pub use device_handle::DeviceHandle; +pub use hotplug::HotplugFilter; pub use fields::{Speed, TransferType, SyncType, UsageType, Direction, RequestType, Recipient, Version, request_type}; pub use device_descriptor::DeviceDescriptor; From 927ea295d8739e30605d2b72dd64ddb7e0e0edec Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Sun, 7 Feb 2021 19:55:10 -0800 Subject: [PATCH 7/8] More cleanup --- examples/hotplug_events.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/hotplug_events.rs b/examples/hotplug_events.rs index 18a74fd..133a2a1 100644 --- a/examples/hotplug_events.rs +++ b/examples/hotplug_events.rs @@ -1,9 +1,6 @@ extern crate libusb; use std::error::Error; -use std::slice; -use std::str::FromStr; -use std::time::Duration; #[derive(Debug)] struct Endpoint { @@ -24,6 +21,5 @@ fn main() -> Result<(), Box> { loop { ctx.handle_events(); } - Ok(()) } From 021168d1209ced7725b479e9685e19dbb8c62c09 Mon Sep 17 00:00:00 2001 From: Richo Healey Date: Sun, 7 Feb 2021 20:45:50 -0800 Subject: [PATCH 8/8] Turns out heap allocation is enough without needing to pin --- src/context.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/context.rs b/src/context.rs index 7bc8232..26e156a 100644 --- a/src/context.rs +++ b/src/context.rs @@ -10,12 +10,11 @@ use device_handle::{self, DeviceHandle}; use error; use event::HotPlugEvent; use hotplug::{CallbackWrapper, HotplugFilter}; -use std::pin::Pin; /// A `libusb` context. pub struct Context { context: *mut libusb_context, - cbs: Vec>>, + cbs: Vec>, } impl Drop for Context { @@ -100,7 +99,7 @@ impl Context { /// Register a callback to fire when a device attached or removed. pub fn register_callback(&mut self, filter: HotplugFilter, closure: F) -> ::Result<()> where F: Fn(&Device, HotPlugEvent) + 'static { - let mut wrapper = Box::pin(CallbackWrapper { + let mut wrapper = Box::new(CallbackWrapper { closure: Box::new(closure), handle: 0, });