From 37912159fdde5dd5e8bb0406b83cd3c2cecf7f24 Mon Sep 17 00:00:00 2001 From: John Stanford on deathStar Date: Thu, 21 Jan 2021 14:31:21 -0600 Subject: [PATCH 1/4] First attempt at as_raw on Context --- src/context.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/context.rs b/src/context.rs index b9ef7a7..4a5e522 100644 --- a/src/context.rs +++ b/src/context.rs @@ -217,6 +217,11 @@ impl Context { }) } + /// Get the raw libusb_context pointer, for advanced use in unsafe code. + pub fn as_raw(&self) -> *mut libusb_context { + self.inner.as_ptr() + } + /// Creates a new `libusb` context and sets runtime options. pub fn with_options(opts: &[crate::UsbOption]) -> crate::Result { let mut this = Self::new()?; From 70b304381169f466fc5681f3b22383b2e3e7f954 Mon Sep 17 00:00:00 2001 From: John Stanford on deathStar Date: Thu, 21 Jan 2021 14:49:12 -0600 Subject: [PATCH 2/4] Fix to as_raw --- src/context.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/context.rs b/src/context.rs index 4a5e522..da3845a 100644 --- a/src/context.rs +++ b/src/context.rs @@ -204,6 +204,9 @@ struct CallbackData { impl Context { /// Opens a new `libusb` context. pub fn new() -> crate::Result { + + eprintln!("Context::new() from async branch of rusb repo"); + let mut context = mem::MaybeUninit::<*mut libusb_context>::uninit(); try_unsafe!(libusb_init(context.as_mut_ptr())); @@ -219,7 +222,7 @@ impl Context { /// Get the raw libusb_context pointer, for advanced use in unsafe code. pub fn as_raw(&self) -> *mut libusb_context { - self.inner.as_ptr() + self.context.inner.as_ptr() } /// Creates a new `libusb` context and sets runtime options. From 97f8490a58c2dbaf767f5378759400d03f304850 Mon Sep 17 00:00:00 2001 From: John Stanford on deathStar Date: Fri, 22 Jan 2021 22:15:19 -0600 Subject: [PATCH 3/4] Added async interface --- src/async_interface.rs | 109 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ 2 files changed, 112 insertions(+) create mode 100644 src/async_interface.rs diff --git a/src/async_interface.rs b/src/async_interface.rs new file mode 100644 index 0000000..0feb1b9 --- /dev/null +++ b/src/async_interface.rs @@ -0,0 +1,109 @@ + +use std::collections::VecDeque; +use std::pin::Pin; + +use libc::c_void; +use libusb1_sys::libusb_transfer; + +use crate::Context; +use crate::DeviceHandle; + +#[derive(Debug)] +pub enum TransferStatus { + Completed(Vec), + Error, + TimedOut, + Stall, + NoDevice, + Overflow, + Unknown(i32) +} + +// Putting the libusb_transfer pointer and the buffer in the struct that owns both ties the lifetimes +// together and ensures that `buff` will live as long as `ptr`. We also know that the receiving VecDeque +// will live as long as the pointer and buffer because it's owned by the same struct. We put the pointer +// inside `recv` to the `libusb_transfer` struct, so it's important for `recv` to stay at the same address, +// which is why it's wrapped in a Pin<_> +// This is not thread-safe at all. If you try to implement Send or Sync on it, it'll have to be an unsafe impl. +pub struct AsyncTransfer { + pub ptr: *mut libusb_transfer, + pub buff: Vec, + pub recv: Pin>>, +} + +impl AsyncTransfer { + + extern "system" fn callback(xfer_ptr: *mut libusb_transfer) { + + unsafe { + let xfer = match (*xfer_ptr).status { + 0 => { + // Clone the memory into a vector + let slice:&[u8] = std::slice::from_raw_parts((*xfer_ptr).buffer, (*xfer_ptr).actual_length as usize); + TransferStatus::Completed(slice.to_vec()) + }, + 1 => TransferStatus::Error, + 2 => TransferStatus::TimedOut, + 3 => TransferStatus::Stall, + 4 => TransferStatus::NoDevice, + 5 => TransferStatus::Overflow, + n => TransferStatus::Unknown(n), + }; + + // Update the parser stored in the user data field + let xfer_deque:*mut VecDeque = (*xfer_ptr).user_data as *mut VecDeque; + (*xfer_deque).push_back(xfer); + + // Resubmit the transfer + assert!(libusb1_sys::libusb_submit_transfer(xfer_ptr) == 0); + } + + } + + pub fn bulk(handle: &mut DeviceHandle, addr:u8) -> Self { + + // The steps in the comments follow the steps described in the libusb documentation + + // Step 1: Allocation + let ptr:*mut libusb_transfer = unsafe{ libusb1_sys::libusb_alloc_transfer(0) }; + let mut buff:Vec = vec![0u8; 256]; + assert!(!ptr.is_null()); + + let mut user_data = Box::new(VecDeque::new()); + + // Step 2: Filling + let default_timeout_ms = 1000; + unsafe { + let rp_ptr:&mut VecDeque = &mut user_data; + libusb1_sys::libusb_fill_bulk_transfer(ptr, handle.as_raw(), addr, + buff.as_mut_ptr(), buff.len() as i32, Self::callback, + rp_ptr as *mut VecDeque as *mut c_void, default_timeout_ms); + } + + // Step 3: Submission + unsafe { + assert!(libusb1_sys::libusb_submit_transfer(ptr) == 0); + } + + // Pin protects the location in memory by making possible to access the data in the + // pointer type, but not possible to get the actual location in memory. You can't + // move it if you don't know where it is. We got the location in memory before we + // wrapped this pointer in a Pin<_> so that we could populate the bulk transfer. Now + // we know it's going to stay there until the memory gets freed. + let recv:Pin>> = Pin::new(user_data); + + Self{ ptr, buff, recv } + } + +} + +impl std::ops::Drop for AsyncTransfer { + + fn drop(&mut self) { + unsafe{ libusb1_sys::libusb_free_transfer(self.ptr); } + } + +} + + + diff --git a/src/lib.rs b/src/lib.rs index 5dfc530..b756e6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub use libusb1_sys::constants; pub use crate::{ + async_interface::{TransferStatus, AsyncTransfer}, config_descriptor::{ConfigDescriptor, Interfaces}, context::{Context, GlobalContext, Hotplug, LogLevel, Registration, UsbContext}, device::Device, @@ -44,6 +45,8 @@ mod interface_descriptor; mod language; mod options; +mod async_interface; + /// Tests whether the running `libusb` library supports capability API. pub fn has_capability() -> bool { GlobalContext::default().as_raw(); From 87c18218c5d0a579b59cff09e7634145501cdbdc Mon Sep 17 00:00:00 2001 From: John Stanford on deathStar Date: Wed, 27 Jan 2021 20:50:00 -0600 Subject: [PATCH 4/4] Cancel transfer in std::impl::Drop --- src/async_interface.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/async_interface.rs b/src/async_interface.rs index 0feb1b9..c2b9356 100644 --- a/src/async_interface.rs +++ b/src/async_interface.rs @@ -100,7 +100,10 @@ impl AsyncTransfer { impl std::ops::Drop for AsyncTransfer { fn drop(&mut self) { - unsafe{ libusb1_sys::libusb_free_transfer(self.ptr); } + unsafe{ + libusb1_sys::libusb_cancel_transfer(self.ptr); + libusb1_sys::libusb_free_transfer(self.ptr); + } } }