diff --git a/examples/read_async.rs b/examples/read_async.rs new file mode 100644 index 0000000..82d92ed --- /dev/null +++ b/examples/read_async.rs @@ -0,0 +1,42 @@ +use rusb::{AsyncTransfer, CbResult, Context, UsbContext}; + +use std::str::FromStr; +use std::time::Duration; + +fn main() { + let args: Vec = std::env::args().collect(); + + if args.len() < 4 { + eprintln!("Usage: read_async "); + return; + } + + let vid: u16 = FromStr::from_str(args[1].as_ref()).unwrap(); + let pid: u16 = FromStr::from_str(args[2].as_ref()).unwrap(); + let endpoint: u8 = FromStr::from_str(args[3].as_ref()).unwrap(); + + let ctx = Context::new().expect("Could not initialize libusb"); + let device = ctx + .open_device_with_vid_pid(vid, pid) + .expect("Could not find device"); + + const NUM_TRANSFERS: usize = 32; + const BUF_SIZE: usize = 1024; + let mut main_buffer = Box::new([0u8; BUF_SIZE * NUM_TRANSFERS]); + + let mut transfers = Vec::new(); + for buf in main_buffer.chunks_exact_mut(BUF_SIZE) { + let mut transfer = + AsyncTransfer::new_bulk(&device, endpoint, buf, callback, Duration::from_secs(10)); + transfer.submit().expect("Could not submit transfer"); + transfers.push(transfer); + } + + loop { + rusb::poll_transfers(&ctx, Duration::from_secs(10)); + } +} + +fn callback(result: CbResult) { + println!("{:?}", result) +} diff --git a/src/device_handle/async_api.rs b/src/device_handle/async_api.rs index 54e99a1..efd52a3 100644 --- a/src/device_handle/async_api.rs +++ b/src/device_handle/async_api.rs @@ -8,8 +8,9 @@ use std::convert::{TryFrom, TryInto}; use std::marker::{PhantomData, PhantomPinned}; use std::pin::Pin; use std::ptr::NonNull; +use std::time::Duration; -type CbResult<'a> = Result<&'a [u8], TransferError>; +pub type CbResult<'a> = Result<&'a [u8], TransferError>; #[derive(Error, Debug)] pub enum TransferError { @@ -94,7 +95,7 @@ impl<'d, 'b, C: 'd + UsbContext, F: FnMut(CbResult<'b>) + Send> AsyncTransfer<'d /// returned Err, or the callback has gotten an Err. // Step 3 of async API #[allow(unused)] - pub fn submit(&mut self) -> Result<(), TransferError> { + pub fn submit(self: &mut Pin>) -> Result<(), TransferError> { let errno = unsafe { ffi::libusb_submit_transfer(self.ptr.as_ptr()) }; use ffi::constants::*; match errno { @@ -189,3 +190,19 @@ impl Drop for AsyncTransfer<'_, '_, C, F> { Self::drop_helper(unsafe { Pin::new_unchecked(self) }); } } + +/// Polls for transfers and executes their callbacks. Will block until the +/// given timeout, or return immediately if timeout is zero. +pub fn poll_transfers(ctx: &impl UsbContext, timeout: Duration) { + let timeval = libc::timeval { + tv_sec: timeout.as_secs() as i64, + tv_usec: timeout.subsec_millis() as i64, + }; + unsafe { + ffi::libusb_handle_events_timeout_completed( + ctx.as_raw(), + std::ptr::addr_of!(timeval), + std::ptr::null_mut(), + ) + }; +} diff --git a/src/device_handle/mod.rs b/src/device_handle/mod.rs index e4102ac..b80873b 100644 --- a/src/device_handle/mod.rs +++ b/src/device_handle/mod.rs @@ -1,4 +1,4 @@ -mod async_api; +pub mod async_api; use std::{mem, ptr::NonNull, time::Duration, u8}; diff --git a/src/lib.rs b/src/lib.rs index 5dfc530..b0c54ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ pub use crate::{ context::{Context, GlobalContext, Hotplug, LogLevel, Registration, UsbContext}, device::Device, device_descriptor::DeviceDescriptor, + device_handle::async_api::{poll_transfers, AsyncTransfer, CbResult}, device_handle::DeviceHandle, device_list::{DeviceList, Devices}, endpoint_descriptor::EndpointDescriptor, @@ -106,6 +107,11 @@ pub fn open_device_with_vid_pid( if handle.is_null() { None } else { - Some(unsafe { DeviceHandle::from_libusb(GlobalContext::default(), std::ptr::NonNull::new_unchecked(handle)) }) + Some(unsafe { + DeviceHandle::from_libusb( + GlobalContext::default(), + std::ptr::NonNull::new_unchecked(handle), + ) + }) } }