Open goertzenator opened 8 years ago
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.
Thanks, I'll give that a look when you push it.
@goertzenator Yeah, definitely. Sounds like @kevinmehall might be ahead of me on that. I'd love to see what he comes up with.
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);
wait_any
(returns None
immediately if there are no completions ready)cancel_all
that just requests cancellation, and lets you wait for it yourself, so
you can collect data from already- or partially-completed transfers.submit
could return something that contains the
*mut libusb_transfer
but only offers a cancel method.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
. 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.
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 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!
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.
@kevinmehall Thanks for your code! I've found one serious issue with it though, fixed in commit whitequark/libusb-rs@9cc650c04fdc65125725b43ca513a16f220a522e.
where did this end up? is there a way to do async USB I/O in rust with any library in 2020 or not?
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.
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.