feat: add Interface::clear_halt(endpoint: u8) for direct endpoint halt clearing
Some checks failed
Rust / format (push) Has been cancelled
Rust / build (macos-latest, 1.79) (push) Has been cancelled
Rust / build (macos-latest, stable) (push) Has been cancelled
Rust / build (ubuntu-latest, 1.79) (push) Has been cancelled
Rust / build (ubuntu-latest, stable) (push) Has been cancelled
Rust / build (windows-latest, 1.79) (push) Has been cancelled
Rust / build (windows-latest, stable) (push) Has been cancelled
Rust / build_android (push) Has been cancelled

Allows clearing a halt/stall condition by raw endpoint address without
needing to construct a typed Endpoint object. Implemented on all three
platform backends (Linux usbfs, macOS IOKit, Windows WinUSB).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-03-31 15:13:39 +00:00
parent 1239c6765a
commit a45a3e08be
4 changed files with 55 additions and 0 deletions

View file

@ -391,6 +391,19 @@ impl Interface {
self.backend.clone().set_alt_setting(alt_setting)
}
/// Clear a halt / stall condition on the given endpoint and reset the
/// host-side data toggle.
///
/// Use this after receiving
/// [`TransferError::Stall`][crate::transfer::TransferError::Stall] to clear
/// the error and resume use of the endpoint.
///
/// Unlike [`Endpoint::clear_halt`], this operates directly by endpoint
/// address without requiring an open [`Endpoint`].
pub fn clear_halt(&self, endpoint: u8) -> impl MaybeFuture<Output = Result<(), Error>> {
self.backend.clone().clear_halt(endpoint)
}
/// Get the current alternate setting of this interface.
pub fn get_alt_setting(&self) -> u8 {
self.backend.get_alt_setting()

View file

@ -757,6 +757,16 @@ impl LinuxInterface {
})
}
pub fn clear_halt(self: Arc<Self>, endpoint: u8) -> impl MaybeFuture<Output = Result<(), Error>> {
Blocking::new(move || {
debug!("Clear halt, endpoint {endpoint:02x}");
usbfs::clear_halt(&self.device.fd, endpoint).map_err(|e| match e {
Errno::NODEV => Error::new_os(ErrorKind::Disconnected, "device disconnected", e),
_ => Error::new_os(ErrorKind::Other, "failed to clear halt", e),
})
})
}
pub fn endpoint(
self: &Arc<Self>,
descriptor: EndpointDescriptor,

View file

@ -422,6 +422,23 @@ impl MacInterface {
})
}
pub fn clear_halt(self: Arc<Self>, endpoint: u8) -> impl MaybeFuture<Output = Result<(), Error>> {
Blocking::new(move || {
debug!("Clear halt, endpoint {endpoint:02x}");
let pipe_ref = self.interface.find_pipe_ref(endpoint).ok_or_else(|| {
Error::new(ErrorKind::NotFound, "endpoint not found on interface")
})?;
self.interface
.clear_pipe_stall_both_ends(pipe_ref)
.map_err(|e| match e {
io_kit_sys::ret::kIOReturnNoDevice => {
Error::new_os(ErrorKind::Disconnected, "device disconnected", e)
}
_ => Error::new_os(ErrorKind::Other, "failed to clear halt on endpoint", e),
})
})
}
pub fn get_alt_setting(&self) -> u8 {
self.state.lock().unwrap().alt_setting
}

View file

@ -508,6 +508,21 @@ impl WindowsInterface {
})
}
pub fn clear_halt(self: Arc<Self>, endpoint: u8) -> impl MaybeFuture<Output = Result<(), Error>> {
Blocking::new(move || {
debug!("Clear halt, endpoint {endpoint:02x}");
unsafe {
if WinUsb_ResetPipe(self.winusb_handle, endpoint) == TRUE {
Ok(())
} else {
Err(match GetLastError() {
e => Error::new_os(ErrorKind::Other, "failed to clear halt", e),
})
}
}
})
}
pub fn get_alt_setting(&self) -> u8 {
self.state.lock().unwrap().alt_setting
}