libusb-rs icon indicating copy to clipboard operation
libusb-rs copied to clipboard

async API?

Open goertzenator opened this issue 8 years ago • 11 comments

It appears libusb-rs does not support the libusb async API. Any plans or thoughts on this?

I've used the async API from C++ before and am considering doing it from Rust.

goertzenator avatar Mar 07 '16 21:03 goertzenator

I started implementing safe Rust bindings to the libusb async transfer functions a while ago, but haven't gotten around to finishing it yet. I'll try to clean it up later this week.

kevinmehall avatar Mar 08 '16 17:03 kevinmehall

Thanks, I'll give that a look when you push it.

goertzenator avatar Mar 08 '16 17:03 goertzenator

@goertzenator Yeah, definitely. Sounds like @kevinmehall might be ahead of me on that. I'd love to see what he comes up with.

dcuddeback avatar Mar 09 '16 04:03 dcuddeback

Work in progress here: https://github.com/kevinmehall/libusb-rs/blob/async/src/async.rs

Example streaming from a device

The new concept (vs libusb) is the AsyncGroup (could use a better name), which takes ownership of transfers when they are submitted, manages completions, and returns the completed transfers back to you to be handled and/or resubmitted. Libusb does that with callbacks, but this wraps that error-prone API in a safe Rust interface. It's not quite zero-cost, because it keeps track of pending transfers in a HashSet and completed transfers in a VecDeque, but should have minimal overhead.

I still need to rework the buffer management. Right now a transfer takes &'d mut [u8]. That lifetime must outlive the AsyncGroup to guarantee that it stays valid while the transfer is pending, In reality, it only needs to live until the transfer completes, but references can't express that. It requires a mutable buffer because the same type is shared for both IN and OUT directions. I'm thinking the Transfer should own the buffer -- that solves the mutability question, and if it let you give ownership to the AsyncGroup on submit, and take it back on completion, you can stream owned buffers into or out of a USB endpoint.

Other TODOs (some could happen later);

  • A nonblocking version of wait_any (returns None immediately if there are no completions ready)
  • A variant of cancel_all that just requests cancellation, and lets you wait for it yourself, so you can collect data from already- or partially-completed transfers.
  • The ability to cancel individual transfers. submit could return something that contains the *mut libusb_transfer but only offers a cancel method.
  • The ability to associate user data with transfers. Right now it works if all the transfers are indistinguishable, but to use the same AsyncGroup / thread for multiple endpoints or devices, you want a way to distinguish them. AsyncGroup could be parametrized over an arbitrary token type that would be passed to submit, kept in a HashMap (instead of the HashSet), and returned from wait_any.
  • Support for control and isochronous transfers, which have specialized buffer layouts.

kevinmehall avatar Mar 11 '16 06:03 kevinmehall

Thanks Kevin. I'm actually interested in using my own event loop (mio), and this wrapper doesn't seem to support that... and I'm not certain it should since libusb doesn't provide it in a portable manner (no Windows support).

My inclination for my project at hand is to just code straight to Linux usbfs. Using the libusb usbfs layer as a guide, this seems very doable.

goertzenator avatar Mar 16 '16 18:03 goertzenator

I think it would be possible to make the AsyncGroup a mio Evented under the proposed new mio API such that you could add the AsyncGroup to your event loop and get notified when a completion is ready. Windows could use an event listener thread, or maybe someday libusb will expose the underlying IOCP handles on Windows.

kevinmehall avatar Mar 17 '16 04:03 kevinmehall

@kevinmehall What's the status here? I would really love to see this, especially because currently I need to read_interrupt on 2 interfaces while i read_control from a 3rd one. With threads this is pretty messy. But with your given "Example streaming from a device" it seems easily possible :)

Looking forward to seeing this!

oberien avatar Apr 22 '16 22:04 oberien

I have an async implementation for linux (and possibly mac, haven't tested it) that can be used with mio, but it made the api somewhat more complicated - most structs are generic to enable choosing whether you want to use the libusb sync or async api's (and to enable different async solutions for linux and windows) and if you want to store references, Rc's or Arc's to the Context. Sync and Async api's are also defined in traits. There is practically no documentation, errors are not thought through and it may be very broken (but it seems to work in one app). There is some design motivation written in the readme. Some code was copied from @kevinmehall code, I hope thats fine.

https://github.com/marjakm/libusb-rs/tree/no-lifetimes

marjakm avatar Mar 06 '18 22:03 marjakm

@kevinmehall Thanks for your code! I've found one serious issue with it though, fixed in commit whitequark/libusb-rs@9cc650c04fdc65125725b43ca513a16f220a522e.

whitequark avatar Mar 18 '19 18:03 whitequark

where did this end up? is there a way to do async USB I/O in rust with any library in 2020 or not?

brandonros avatar Jul 04 '20 19:07 brandonros

I am interested in this as well. My application does TCP communication via mio, and also uses rusb (currently only synchronously). Ideally, I would like to be able to wait for both sockets and asynchronous rusb requests using the same Poll.

jesultra avatar Mar 05 '22 00:03 jesultra