Skip to content

Commit

Permalink
Merge pull request #46 from kevinmehall/clear-halt
Browse files Browse the repository at this point in the history
Add Interface::clear_halt
  • Loading branch information
kevinmehall authored Mar 3, 2024
2 parents 2379cb1 + 083eb65 commit 0a9b493
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 4 deletions.
14 changes: 14 additions & 0 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,20 @@ impl Interface {
pub fn interrupt_out_queue(&self, endpoint: u8) -> Queue<Vec<u8>> {
Queue::new(self.backend.clone(), endpoint, EndpointType::Interrupt)
}

/// Clear a bulk or interrupt endpoint's halt / stall condition.
///
/// Sends a `CLEAR_FEATURE` `ENDPOINT_HALT` control transfer to tell the
/// device to reset the endpoint's data toggle and clear the halt / stall
/// condition, and resets the host-side data toggle.
///
/// Use this after receiving [`TransferError::Stall`] to clear the error and
/// resume use of the endpoint.
///
/// This should not be called when transfers are pending on the endpoint.
pub fn clear_halt(&self, endpoint: u8) -> Result<(), Error> {
self.backend.clear_halt(endpoint)
}
}

#[test]
Expand Down
5 changes: 5 additions & 0 deletions src/platform/linux_usbfs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@ impl LinuxInterface {
alt_setting,
)?)
}

pub fn clear_halt(&self, endpoint: u8) -> Result<(), Error> {
debug!("Clear halt, endpoint {endpoint:02x}");
Ok(usbfs::clear_halt(&self.device.fd, endpoint)?)
}
}

impl Drop for LinuxInterface {
Expand Down
8 changes: 8 additions & 0 deletions src/platform/linux_usbfs/usbfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,11 @@ pub fn control<Fd: AsFd>(fd: Fd, transfer: CtrlTransfer) -> io::Result<usize> {
ioctl::ioctl(fd, ctl)
}
}

pub fn clear_halt<Fd: AsFd>(fd: Fd, endpoint: u8) -> io::Result<()> {
unsafe {
let ctl =
ioctl::Setter::<ioctl::ReadOpcode<b'U', 21, c_uint>, c_uint>::new(endpoint.into());
ioctl::ioctl(fd, ctl)
}
}
14 changes: 14 additions & 0 deletions src/platform/macos_iokit/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,20 @@ impl MacInterface {
))
}
}

pub fn clear_halt(&self, endpoint: u8) -> Result<(), Error> {
debug!("Clear halt, endpoint {endpoint:02x}");
let ep = self
.endpoints
.get(&endpoint)
.ok_or_else(|| Error::new(ErrorKind::NotFound, "Endpoint not found"))?;
unsafe {
check_iokit_return(call_iokit_function!(
self.interface.raw,
ClearPipeStallBothEnds(ep.pipe_ref)
))
}
}
}

impl Drop for MacInterface {
Expand Down
19 changes: 16 additions & 3 deletions src/platform/windows_winusb/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use std::{
use log::{debug, error, info, warn};
use windows_sys::Win32::{
Devices::Usb::{
WinUsb_ControlTransfer, WinUsb_Free, WinUsb_Initialize, WinUsb_SetCurrentAlternateSetting,
WinUsb_SetPipePolicy, PIPE_TRANSFER_TIMEOUT, WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET,
WinUsb_ControlTransfer, WinUsb_Free, WinUsb_Initialize, WinUsb_ResetPipe,
WinUsb_SetCurrentAlternateSetting, WinUsb_SetPipePolicy, PIPE_TRANSFER_TIMEOUT,
WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET,
},
Foundation::{GetLastError, FALSE, TRUE},
};
Expand Down Expand Up @@ -265,7 +266,19 @@ impl WindowsInterface {

pub fn set_alt_setting(&self, alt_setting: u8) -> Result<(), Error> {
unsafe {
let r = WinUsb_SetCurrentAlternateSetting(raw_handle(&self.handle), alt_setting.into());
let r = WinUsb_SetCurrentAlternateSetting(self.winusb_handle, alt_setting.into());
if r == TRUE {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
}

pub fn clear_halt(&self, endpoint: u8) -> Result<(), Error> {
debug!("Clear halt, endpoint {endpoint:02x}");
unsafe {
let r = WinUsb_ResetPipe(self.winusb_handle, endpoint);
if r == TRUE {
Ok(())
} else {
Expand Down
6 changes: 6 additions & 0 deletions src/transfer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ pub enum TransferError {
Cancelled,

/// Endpoint in a STALL condition.
///
/// This is used by the device to signal that an error occurred. For bulk
/// and interrupt endpoints, the stall condition can be cleared with
/// [`Interface::clear_halt`][crate::Interface::clear_halt]. For control
/// requests, the stall is automatically cleared when another request is
/// submitted.
Stall,

/// Device disconnected.
Expand Down
16 changes: 15 additions & 1 deletion src/transfer/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
task::{Context, Poll},
};

use crate::platform;
use crate::{platform, Error};

use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRequest};

Expand Down Expand Up @@ -209,6 +209,20 @@ where
transfer.cancel();
}
}

/// Clear the endpoint's halt / stall condition.
///
/// Sends a `CLEAR_FEATURE` `ENDPOINT_HALT` control transfer to tell the
/// device to reset the endpoint's data toggle and clear the halt / stall
/// condition, and resets the host-side data toggle.
///
/// Use this after receiving [`TransferError::Stall`] to clear the error and
/// resume use of the endpoint.
///
/// This should not be called when transfers are pending on the endpoint.
pub fn clear_halt(&mut self) -> Result<(), Error> {
self.interface.clear_halt(self.endpoint)
}
}

impl<R: TransferRequest> Drop for Queue<R> {
Expand Down

0 comments on commit 0a9b493

Please sign in to comment.