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 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
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
25 changes: 25 additions & 0 deletions examples/hotplug_events.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
extern crate libusb;

use std::error::Error;

#[derive(Debug)]
struct Endpoint {
config: u8,
iface: u8,
setting: u8,
address: u8
}

fn main() -> Result<(), Box<dyn Error>> {
let mut ctx = libusb::Context::new()?;
let filter = libusb::HotplugFilter::new()
.enumerate();
ctx.register_callback(filter, |device, event| {
eprintln!("invoked");
println!("{:?} - {:?}", device.device_descriptor(), event);
})?;
loop {
ctx.handle_events();
}
}

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
67 changes: 65 additions & 2 deletions src/context.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
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};

/// A `libusb` context.
pub struct Context {
context: *mut libusb_context,
cbs: Vec<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 +41,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 +95,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::new(CallbackWrapper {
closure: Box::new(closure),
handle: 0,
});
let mut handle = 0;
let res = unsafe { libusb_hotplug_register_callback(
self.context,
filter.get_events(),
filter.get_flags(),
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 +147,25 @@ impl Context {
}
}

extern "C" fn invoke_callback(_ctx: *mut libusb_context, device: *const libusb_device, event: i32, data: *mut std::ffi::c_void) -> i32 {
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;

unsafe { ((*wrapper).closure)(&device, event) };

0
},
None => {
// With no meaningful way to signal this error condition we simply don't dispatch the
// call and return.
return 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
17 changes: 17 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use libusb::*;

#[derive(Debug, Clone, Copy)]
pub enum HotPlugEvent {
Arrived,
Left,
}

impl HotPlugEvent {
pub fn from_i32(value: i32) -> Option<HotPlugEvent> {
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,
}
}
}
86 changes: 86 additions & 0 deletions src/hotplug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use device::Device;
use event::HotPlugEvent;

use libusb::*;

pub struct CallbackWrapper {
pub closure: 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>,
enumerate: bool,
}

impl HotplugFilter {
pub fn new() -> Self {
Self {
vendor: None,
product: None,
class: None,
events: None,
enumerate: false,
}
}

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

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
}

pub fn enumerate(mut self) -> Self {
self.enumerate = true;
self
}
}


3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,10 +33,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;