rusb_async/
lib.rs

1use rusb::ffi::{self, constants::*};
2
3use std::convert::TryInto;
4use std::ptr::NonNull;
5
6use std::sync::atomic::{AtomicBool, Ordering};
7
8mod error;
9mod pool;
10
11use error::{Error, Result};
12
13pub use pool::TransferPool;
14
15struct Transfer {
16    ptr: NonNull<ffi::libusb_transfer>,
17    buffer: Vec<u8>,
18}
19
20impl Transfer {
21    // Invariant: Caller must ensure `device` outlives this transfer
22    unsafe fn bulk(
23        device: *mut ffi::libusb_device_handle,
24        endpoint: u8,
25        mut buffer: Vec<u8>,
26    ) -> Self {
27        // non-isochronous endpoints (e.g. control, bulk, interrupt) specify a value of 0
28        // This is step 1 of async API
29
30        let ptr =
31            NonNull::new(ffi::libusb_alloc_transfer(0)).expect("Could not allocate transfer!");
32
33        let user_data = Box::into_raw(Box::new(AtomicBool::new(false))).cast::<libc::c_void>();
34
35        let length = if endpoint & ffi::constants::LIBUSB_ENDPOINT_DIR_MASK
36            == ffi::constants::LIBUSB_ENDPOINT_OUT
37        {
38            // for OUT endpoints: the currently valid data in the buffer
39            buffer.len()
40        } else {
41            // for IN endpoints: the full capacity
42            buffer.capacity()
43        }
44        .try_into()
45        .unwrap();
46
47        ffi::libusb_fill_bulk_transfer(
48            ptr.as_ptr(),
49            device,
50            endpoint,
51            buffer.as_mut_ptr(),
52            length,
53            Self::transfer_cb,
54            user_data,
55            0,
56        );
57
58        Self { ptr, buffer }
59    }
60
61    // Invariant: Caller must ensure `device` outlives this transfer
62    unsafe fn control(
63        device: *mut ffi::libusb_device_handle,
64
65        request_type: u8,
66        request: u8,
67        value: u16,
68        index: u16,
69        data: &[u8],
70    ) -> Self {
71        let mut buf = Vec::with_capacity(data.len() + LIBUSB_CONTROL_SETUP_SIZE);
72
73        let length = data.len() as u16;
74
75        ffi::libusb_fill_control_setup(
76            buf.as_mut_ptr() as *mut u8,
77            request_type,
78            request,
79            value,
80            index,
81            length,
82        );
83        Self::control_raw(device, buf)
84    }
85
86    // Invariant: Caller must ensure `device` outlives this transfer
87    unsafe fn control_raw(device: *mut ffi::libusb_device_handle, mut buffer: Vec<u8>) -> Self {
88        // non-isochronous endpoints (e.g. control, bulk, interrupt) specify a value of 0
89        // This is step 1 of async API
90
91        let ptr =
92            NonNull::new(ffi::libusb_alloc_transfer(0)).expect("Could not allocate transfer!");
93
94        let user_data = Box::into_raw(Box::new(AtomicBool::new(false))).cast::<libc::c_void>();
95
96        ffi::libusb_fill_control_transfer(
97            ptr.as_ptr(),
98            device,
99            buffer.as_mut_ptr(),
100            Self::transfer_cb,
101            user_data,
102            0,
103        );
104
105        Self { ptr, buffer }
106    }
107
108    // Invariant: Caller must ensure `device` outlives this transfer
109    unsafe fn interrupt(
110        device: *mut ffi::libusb_device_handle,
111        endpoint: u8,
112        mut buffer: Vec<u8>,
113    ) -> Self {
114        // non-isochronous endpoints (e.g. control, bulk, interrupt) specify a value of 0
115        // This is step 1 of async API
116
117        let ptr =
118            NonNull::new(ffi::libusb_alloc_transfer(0)).expect("Could not allocate transfer!");
119
120        let user_data = Box::into_raw(Box::new(AtomicBool::new(false))).cast::<libc::c_void>();
121
122        let length = if endpoint & ffi::constants::LIBUSB_ENDPOINT_DIR_MASK
123            == ffi::constants::LIBUSB_ENDPOINT_OUT
124        {
125            // for OUT endpoints: the currently valid data in the buffer
126            buffer.len()
127        } else {
128            // for IN endpoints: the full capacity
129            buffer.capacity()
130        }
131        .try_into()
132        .unwrap();
133
134        ffi::libusb_fill_interrupt_transfer(
135            ptr.as_ptr(),
136            device,
137            endpoint,
138            buffer.as_mut_ptr(),
139            length,
140            Self::transfer_cb,
141            user_data,
142            0,
143        );
144
145        Self { ptr, buffer }
146    }
147
148    // Invariant: Caller must ensure `device` outlives this transfer
149    unsafe fn iso(
150        device: *mut ffi::libusb_device_handle,
151        endpoint: u8,
152        mut buffer: Vec<u8>,
153        iso_packets: i32,
154    ) -> Self {
155        // isochronous endpoints
156        // This is step 1 of async API
157        let ptr = NonNull::new(ffi::libusb_alloc_transfer(iso_packets))
158            .expect("Could not allocate transfer!");
159
160        let user_data = Box::into_raw(Box::new(AtomicBool::new(false))).cast::<libc::c_void>();
161
162        let length = if endpoint & ffi::constants::LIBUSB_ENDPOINT_DIR_MASK
163            == ffi::constants::LIBUSB_ENDPOINT_OUT
164        {
165            // for OUT endpoints: the currently valid data in the buffer
166            buffer.len()
167        } else {
168            // for IN endpoints: the full capacity
169            buffer.capacity()
170        }
171        .try_into()
172        .unwrap();
173
174        ffi::libusb_fill_iso_transfer(
175            ptr.as_ptr(),
176            device,
177            endpoint,
178            buffer.as_mut_ptr(),
179            length,
180            iso_packets,
181            Self::transfer_cb,
182            user_data,
183            0,
184        );
185        ffi::libusb_set_iso_packet_lengths(ptr.as_ptr(), (length / iso_packets) as u32);
186
187        Self { ptr, buffer }
188    }
189    // Part of step 4 of async API the transfer is finished being handled when
190    // `poll()` is called.
191    extern "system" fn transfer_cb(transfer: *mut ffi::libusb_transfer) {
192        // Safety: transfer is still valid because libusb just completed
193        // it but we haven't told anyone yet. user_data remains valid
194        // because it is freed only with the transfer.
195        // After the store to completed, these may no longer be valid if
196        // the polling thread freed it after seeing it completed.
197        let completed = unsafe {
198            let transfer = &mut *transfer;
199            &*transfer.user_data.cast::<AtomicBool>()
200        };
201        completed.store(true, Ordering::SeqCst);
202    }
203
204    fn transfer(&self) -> &ffi::libusb_transfer {
205        // Safety: transfer remains valid as long as self
206        unsafe { self.ptr.as_ref() }
207    }
208
209    fn completed_flag(&self) -> &AtomicBool {
210        // Safety: transfer and user_data remain valid as long as self
211        unsafe { &*self.transfer().user_data.cast::<AtomicBool>() }
212    }
213
214    /// Prerequisite: self.buffer ans self.ptr are both correctly set
215    fn swap_buffer(&mut self, new_buf: Vec<u8>) -> Vec<u8> {
216        let transfer_struct = unsafe { self.ptr.as_mut() };
217
218        let data = std::mem::replace(&mut self.buffer, new_buf);
219
220        // Update transfer struct for new buffer
221        transfer_struct.actual_length = 0; // TODO: Is this necessary?
222        transfer_struct.buffer = self.buffer.as_mut_ptr();
223        transfer_struct.length = self.buffer.capacity() as i32;
224
225        data
226    }
227
228    // Step 3 of async API
229    fn submit(&mut self) -> Result<()> {
230        self.completed_flag().store(false, Ordering::SeqCst);
231        let errno = unsafe { ffi::libusb_submit_transfer(self.ptr.as_ptr()) };
232
233        match errno {
234            0 => Ok(()),
235            LIBUSB_ERROR_NO_DEVICE => Err(Error::Disconnected),
236            LIBUSB_ERROR_BUSY => {
237                unreachable!("We shouldn't be calling submit on transfers already submitted!")
238            }
239            LIBUSB_ERROR_NOT_SUPPORTED => Err(Error::Other("Transfer not supported")),
240            LIBUSB_ERROR_INVALID_PARAM => {
241                Err(Error::Other("Transfer size bigger than OS supports"))
242            }
243            _ => Err(Error::Errno("Error while submitting transfer: ", errno)),
244        }
245    }
246
247    fn cancel(&mut self) {
248        unsafe {
249            ffi::libusb_cancel_transfer(self.ptr.as_ptr());
250        }
251    }
252
253    fn handle_completed(&mut self) -> Result<Vec<u8>> {
254        assert!(self.completed_flag().load(Ordering::Relaxed));
255        let err = match self.transfer().status {
256            LIBUSB_TRANSFER_COMPLETED => {
257                debug_assert!(self.transfer().length >= self.transfer().actual_length);
258                unsafe {
259                    self.buffer.set_len(self.transfer().actual_length as usize);
260                }
261                let data = self.swap_buffer(Vec::new());
262                return Ok(data);
263            }
264            LIBUSB_TRANSFER_CANCELLED => Error::Cancelled,
265            LIBUSB_TRANSFER_ERROR => Error::Other("Error occurred during transfer execution"),
266            LIBUSB_TRANSFER_TIMED_OUT => {
267                unreachable!("We are using timeout=0 which means no timeout")
268            }
269            LIBUSB_TRANSFER_STALL => Error::Stall,
270            LIBUSB_TRANSFER_NO_DEVICE => Error::Disconnected,
271            LIBUSB_TRANSFER_OVERFLOW => Error::Overflow,
272            _ => panic!("Found an unexpected error value for transfer status"),
273        };
274        Err(err)
275    }
276}
277
278/// Invariant: transfer must not be pending
279impl Drop for Transfer {
280    fn drop(&mut self) {
281        unsafe {
282            drop(Box::from_raw(self.transfer().user_data));
283            ffi::libusb_free_transfer(self.ptr.as_ptr());
284        }
285    }
286}