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

Richo/hotplug events #42

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
27 changes: 27 additions & 0 deletions examples/hotplug_events.rs
Original file line number Diff line number Diff line change
@@ -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<dyn Error>> {
let mut ctx = libusb::Context::new()?;
ctx.register_callback(Default::default(), |device, event| {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dcuddeback This is the user facing API, I'd love your thoughts on this part. I create a native &Device for the callback, and created an Event enum to mirror the libusb side but I'm open to other ideas here.

The libusb interface for filters doesn't really map onto rust super well so I made a builder style thing to let you create filters.

eprintln!("invoked");
println!("{:?} - {:?}", device.device_descriptor(), event);
});
loop {
ctx.handle_events();
}
Ok(())
}

1 change: 1 addition & 0 deletions src/config_descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
71 changes: 69 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -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<Pin<Box<CallbackWrapper>>>,
}

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);
}
}
Expand All @@ -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.
Expand Down Expand Up @@ -83,6 +96,38 @@ impl Context {
}
}


/// Register a callback to fire when a device attached or removed.
pub fn register_callback<F>(&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
Expand All @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions src/device_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/device_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
5 changes: 5 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[derive(Debug, Clone, Copy)]
pub enum HotPlugEvent {
Arrived,
Left,
}
71 changes: 71 additions & 0 deletions src/hotplug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use device::Device;
use event::HotPlugEvent;

use libusb::*;

pub struct CallbackWrapper {
pub cb: Box<dyn Fn(&Device, HotPlugEvent)>,
pub handle: i32,
}

#[derive(Default)]
pub struct HotplugFilter {
vendor: Option<i32>,
product: Option<i32>,
class: Option<i32>,
events: Option<i32>,
}

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
}
}


2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ mod context;
mod device_list;
mod device;
mod device_handle;
mod event;

mod fields;
mod device_descriptor;
mod config_descriptor;
mod interface_descriptor;
mod endpoint_descriptor;
mod language;
mod hotplug;