Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
},
DeviceInfo, Error, MaybeFuture, Speed,
};
use log::error;
use log::{error, warn};
use std::{
future::{poll_fn, Future},
io::ErrorKind,
Expand Down Expand Up @@ -635,10 +635,26 @@ impl<EpType: BulkOrInterrupt, Dir: EndpointDirection> Endpoint<EpType, Dir> {
/// For an OUT transfer, the buffer's `len` is the number of bytes
/// initialized, which will be sent to the device.
///
/// For an IN transfer, the buffer's `requested_len` is the number of
/// bytes requested. It must be a multiple of the endpoint's [maximum packet
/// size][`Self::max_packet_size`].
/// For an IN transfer, the buffer's `requested_len` is the number of bytes
/// requested. It must be a nonzero multiple of the endpoint's [maximum
/// packet size][`Self::max_packet_size`] or the transfer will fail with
/// `TransferError::InvalidArgument`. Up to `requested_len /
/// max_packet_size` packets will be received, ending early when any packet
/// is shorter than `max_packet_size`.
pub fn submit(&mut self, buf: Buffer) {
if Dir::DIR == Direction::In {
let req_len = buf.requested_len();
if req_len == 0 || req_len % self.max_packet_size() != 0 {
warn!(
"Submitting transfer with length {req_len} which is not a multiple of max packet size {} on IN endpoint {:02x}",
self.max_packet_size(),
self.endpoint_address(),
);

return self.backend.submit_err(buf, TransferError::InvalidArgument);
}
}

self.backend.submit(buf)
}

Expand All @@ -647,6 +663,12 @@ impl<EpType: BulkOrInterrupt, Dir: EndpointDirection> Endpoint<EpType, Dir> {
/// This future is cancel-safe: it can be cancelled and re-created without
/// side effects, enabling its use in `select!{}` or similar.
///
/// An OUT transfer completes when the specified data has been sent or an
/// error occurs. An IN transfer completes when a packet smaller than
/// `max_packet_size` is received, the full `requested_len` is received
/// (without waiting for or consuming any subsequent zero-length packet), or
/// an error occurs.
///
/// ## Panics
/// * if there are no transfers pending (that is, if [`Self::pending()`]
/// would return 0).
Expand Down
18 changes: 15 additions & 3 deletions src/platform/linux_usbfs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,18 +691,30 @@ impl LinuxEndpoint {
}
}

pub(crate) fn submit(&mut self, data: Buffer) {
let mut transfer = self.idle_transfer.take().unwrap_or_else(|| {
fn get_transfer(&mut self) -> Idle<TransferData> {
self.idle_transfer.take().unwrap_or_else(|| {
Idle::new(
self.inner.clone(),
super::TransferData::new(self.inner.address, self.inner.ep_type),
)
});
})
}

pub(crate) fn submit(&mut self, data: Buffer) {
let mut transfer = self.get_transfer();
transfer.set_buffer(data);
self.pending
.push_back(self.inner.interface.device.submit(transfer));
}

pub(crate) fn submit_err(&mut self, data: Buffer, error: TransferError) {
assert_eq!(error, TransferError::InvalidArgument);
let mut transfer = self.get_transfer();
transfer.set_buffer(data);
transfer.urb_mut().status = Errno::INVAL.raw_os_error();
self.pending.push_back(transfer.simulate_complete());
}

pub(crate) fn poll_next_complete(&mut self, cx: &mut Context) -> Poll<Completion> {
self.inner.notify.subscribe(cx);
if let Some(mut transfer) = take_completed_from_queue(&mut self.pending) {
Expand Down
3 changes: 2 additions & 1 deletion src/platform/linux_usbfs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ fn errno_to_transfer_error(e: Errno) -> TransferError {
Errno::PROTO | Errno::ILSEQ | Errno::OVERFLOW | Errno::COMM | Errno::TIME => {
TransferError::Fault
}
_ => TransferError::Unknown(e.raw_os_error()),
Errno::INVAL => TransferError::InvalidArgument,
_ => TransferError::Unknown(e.raw_os_error() as u32),
}
}
38 changes: 25 additions & 13 deletions src/platform/macos_iokit/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,25 +466,30 @@ impl MacEndpoint {
);
}

pub(crate) fn submit(&mut self, buffer: Buffer) {
fn make_transfer(&mut self, buffer: Buffer) -> Idle<TransferData> {
let mut transfer = self
.idle_transfer
.take()
.unwrap_or_else(|| Idle::new(self.inner.clone(), super::TransferData::new()));

let buffer = ManuallyDrop::new(buffer);
let endpoint = self.inner.address;
let dir = Direction::from_address(endpoint);
let pipe_ref = self.inner.pipe_ref;

transfer.buf = buffer.ptr;
transfer.capacity = buffer.capacity;
transfer.actual_len = 0;
let req_len = match dir {
let req_len = match Direction::from_address(self.inner.address) {
Direction::Out => buffer.len,
Direction::In => buffer.requested_len,
};
transfer.requested_len = req_len;
transfer
}

pub(crate) fn submit(&mut self, buffer: Buffer) {
let transfer = self.make_transfer(buffer);
let endpoint = self.inner.address;
let dir = Direction::from_address(endpoint);
let req_len = transfer.requested_len;
let buf_ptr = transfer.buf;

let transfer = transfer.pre_submit();
let ptr = transfer.as_ptr();
Expand All @@ -494,19 +499,19 @@ impl MacEndpoint {
Direction::Out => call_iokit_function!(
self.inner.interface.interface.raw,
WritePipeAsync(
pipe_ref,
buffer.ptr as *mut c_void,
buffer.len,
self.inner.pipe_ref,
buf_ptr as *mut c_void,
req_len,
transfer_callback,
ptr as *mut c_void
)
),
Direction::In => call_iokit_function!(
self.inner.interface.interface.raw,
ReadPipeAsync(
pipe_ref,
buffer.ptr as *mut c_void,
buffer.requested_len,
self.inner.pipe_ref,
buf_ptr as *mut c_void,
req_len,
transfer_callback,
ptr as *mut c_void
)
Expand All @@ -519,7 +524,7 @@ impl MacEndpoint {
"Submitted {dir:?} transfer {ptr:?} of len {req_len} on endpoint {endpoint:02X}"
);
} else {
error!("Failed to submit transfer {ptr:?} of len {req_len} on endpoint {endpoint:02X}: {res:x}");
error!("Failed to submit {dir:?} transfer {ptr:?} of len {req_len} on endpoint {endpoint:02X}: {res:x}");
unsafe {
// Complete the transfer in the place of the callback
(*ptr).status = res;
Expand All @@ -530,6 +535,13 @@ impl MacEndpoint {
self.pending.push_back(transfer);
}

pub(crate) fn submit_err(&mut self, buffer: Buffer, err: TransferError) {
assert_eq!(err, TransferError::InvalidArgument);
let mut transfer = self.make_transfer(buffer);
transfer.status = io_kit_sys::ret::kIOReturnBadArgument;
self.pending.push_back(transfer.simulate_complete());
}

pub(crate) fn poll_next_complete(&mut self, cx: &mut Context) -> Poll<Completion> {
self.inner.notify.subscribe(cx);
if let Some(mut transfer) = take_completed_from_queue(&mut self.pending) {
Expand Down
2 changes: 0 additions & 2 deletions src/platform/macos_iokit/iokit_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ pub(crate) const kIOUSBTransactionTimeout: c_int = SYS_IOKIT | SUB_IOKIT_USB | 0

pub(crate) const kIOUSBFindInterfaceDontCare: UInt16 = 0xFFFF;

//

//
// Type aliases.
//
Expand Down
7 changes: 5 additions & 2 deletions src/platform/macos_iokit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ fn status_to_transfer_result(status: IOReturn) -> Result<(), TransferError> {
match status {
io_kit_sys::ret::kIOReturnSuccess | io_kit_sys::ret::kIOReturnUnderrun => Ok(()),
io_kit_sys::ret::kIOReturnNoDevice => Err(TransferError::Disconnected),
io_kit_sys::ret::kIOReturnAborted => Err(TransferError::Cancelled),
io_kit_sys::ret::kIOReturnAborted | iokit_c::kIOUSBTransactionTimeout => {
Err(TransferError::Cancelled)
}
iokit_c::kIOUSBPipeStalled => Err(TransferError::Stall),
_ => Err(TransferError::Unknown(status as i32)),
io_kit_sys::ret::kIOReturnBadArgument => Err(TransferError::InvalidArgument), // used for `submit_err`
_ => Err(TransferError::Unknown(status as u32)),
}
}
44 changes: 33 additions & 11 deletions src/platform/windows_winusb/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ use windows_sys::Win32::{
WinUsb_SetPipePolicy, WinUsb_WritePipe, USB_DEVICE_DESCRIPTOR, WINUSB_INTERFACE_HANDLE,
WINUSB_SETUP_PACKET,
},
Foundation::{GetLastError, ERROR_IO_PENDING, ERROR_NOT_FOUND, FALSE, HANDLE, TRUE},
Foundation::{
GetLastError, ERROR_BAD_COMMAND, ERROR_DEVICE_NOT_CONNECTED, ERROR_FILE_NOT_FOUND,
ERROR_IO_PENDING, ERROR_NOT_FOUND, ERROR_NO_SUCH_DEVICE, FALSE, HANDLE, TRUE,
},
System::IO::{CancelIoEx, OVERLAPPED},
};

Expand Down Expand Up @@ -409,10 +412,6 @@ impl WindowsInterface {
data: ControlIn,
timeout: Duration,
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
if data.recipient == Recipient::Interface && data.index as u8 != self.interface_number {
warn!("WinUSB sends interface number instead of passed `index` when performing a control transfer with `Recipient::Interface`");
}

let mut t = TransferData::new(0x80);
t.set_buffer(Buffer::new(data.length as usize));

Expand All @@ -438,10 +437,6 @@ impl WindowsInterface {
data: ControlOut,
timeout: Duration,
) -> impl MaybeFuture<Output = Result<(), TransferError>> {
if data.recipient == Recipient::Interface && data.index as u8 != self.interface_number {
warn!("WinUSB sends interface number instead of passed `index` when performing a control transfer with `Recipient::Interface`");
}

let mut t = TransferData::new(0x00);
t.set_buffer(Buffer::from(data.data.to_vec()));

Expand Down Expand Up @@ -540,6 +535,7 @@ impl WindowsInterface {
let len = t.request_len;
let buf = t.buf;
t.overlapped.InternalHigh = 0;
t.error_from_submit = Ok(());

let t = t.pre_submit();
let ptr = t.as_ptr();
Expand Down Expand Up @@ -580,6 +576,15 @@ impl WindowsInterface {
let len = t.request_len;
let buf = t.buf;
t.overlapped.InternalHigh = 0;
t.error_from_submit = Ok(());

if pkt.RequestType & 0x1f == Recipient::Interface as u8
&& pkt.Index as u8 != self.interface_number
{
warn!("WinUSB requires control transfer with `Recipient::Interface` to pass the interface number in `index`");
t.error_from_submit = Err(TransferError::InvalidArgument);
return t.simulate_complete();
}

let t = t.pre_submit();
let ptr = t.as_ptr();
Expand Down Expand Up @@ -613,7 +618,13 @@ impl WindowsInterface {
// Safety: Transfer was not submitted, so we still own it
// and must complete it in place of the event thread.
unsafe {
(*t.as_ptr()).overlapped.Internal = err as _;
(*t.as_ptr()).error_from_submit = match err {
ERROR_BAD_COMMAND
| ERROR_FILE_NOT_FOUND
| ERROR_DEVICE_NOT_CONNECTED
| ERROR_NO_SUCH_DEVICE => Err(TransferError::Disconnected),
other => Err(TransferError::Unknown(other)),
};
notify_completion::<TransferData>(t.as_ptr());
}
}
Expand Down Expand Up @@ -672,15 +683,26 @@ impl WindowsEndpoint {
}
}

pub(crate) fn submit(&mut self, buffer: Buffer) {
fn make_transfer(&mut self, buffer: Buffer) -> Idle<TransferData> {
let mut t = self.idle_transfer.take().unwrap_or_else(|| {
Idle::new(self.inner.clone(), TransferData::new(self.inner.address))
});
t.set_buffer(buffer);
t
}

pub(crate) fn submit(&mut self, buffer: Buffer) {
let t = self.make_transfer(buffer);
let t = self.inner.interface.submit(t);
self.pending.push_back(t);
}

pub(crate) fn submit_err(&mut self, buffer: Buffer, err: TransferError) {
let mut t = self.make_transfer(buffer);
t.error_from_submit = Err(err);
self.pending.push_back(t.simulate_complete());
}

pub(crate) fn poll_next_complete(&mut self, cx: &mut Context) -> Poll<Completion> {
self.inner.notify.subscribe(cx);
if let Some(mut transfer) = take_completed_from_queue(&mut self.pending) {
Expand Down
29 changes: 17 additions & 12 deletions src/platform/windows_winusb/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct TransferData {
pub(crate) capacity: u32,
pub(crate) request_len: u32,
pub(crate) endpoint: u8,
pub(crate) error_from_submit: Result<(), TransferError>,
}

unsafe impl Send for TransferData {}
Expand All @@ -39,6 +40,7 @@ impl TransferData {
capacity: 0,
request_len: 0,
endpoint,
error_from_submit: Ok(()),
}
}

Expand All @@ -62,19 +64,22 @@ impl TransferData {
pub fn take_completion(&mut self, intf: &Interface) -> Completion {
let mut actual_len: u32 = 0;

unsafe { GetOverlappedResult(intf.handle, &self.overlapped, &mut actual_len, 0) };

let status = match unsafe { GetLastError() } {
ERROR_SUCCESS => Ok(()),
ERROR_GEN_FAILURE => Err(TransferError::Stall),
ERROR_REQUEST_ABORTED | ERROR_TIMEOUT | ERROR_SEM_TIMEOUT | ERROR_OPERATION_ABORTED => {
Err(TransferError::Cancelled)
}
ERROR_FILE_NOT_FOUND | ERROR_DEVICE_NOT_CONNECTED | ERROR_NO_SUCH_DEVICE => {
Err(TransferError::Disconnected)
let status = self.error_from_submit.and_then(|()| {
unsafe { GetOverlappedResult(intf.handle, &self.overlapped, &mut actual_len, 0) };

match unsafe { GetLastError() } {
ERROR_SUCCESS => Ok(()),
ERROR_GEN_FAILURE => Err(TransferError::Stall),
ERROR_REQUEST_ABORTED
| ERROR_TIMEOUT
| ERROR_SEM_TIMEOUT
| ERROR_OPERATION_ABORTED => Err(TransferError::Cancelled),
ERROR_FILE_NOT_FOUND | ERROR_DEVICE_NOT_CONNECTED | ERROR_NO_SUCH_DEVICE => {
Err(TransferError::Disconnected)
}
e => Err(TransferError::Unknown(e)),
}
e => Err(TransferError::Unknown(e as i32)),
};
});

let mut empty = ManuallyDrop::new(Vec::new());
let ptr = mem::replace(&mut self.buf, empty.as_mut_ptr());
Expand Down
6 changes: 6 additions & 0 deletions src/transfer/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ impl<P> Idle<P> {
ptr: unsafe { NonNull::new_unchecked(Box::into_raw(self.0)) },
}
}

pub(crate) fn simulate_complete(self) -> Pending<P> {
Pending {
ptr: unsafe { NonNull::new_unchecked(Box::into_raw(self.0)) },
}
}
}

impl<P> Deref for Idle<P> {
Expand Down
Loading
Loading