Merge pull request #117 from kevinmehall/redesign
Redesign endpoint and transfer API
This commit is contained in:
commit
e1796a80dd
25 changed files with 2239 additions and 2205 deletions
|
|
@ -22,7 +22,7 @@ env_logger = "0.10.0"
|
|||
futures-lite = "1.13.0"
|
||||
|
||||
[target.'cfg(any(target_os="linux", target_os="android"))'.dependencies]
|
||||
rustix = { version = "1.0.1", features = ["fs", "event", "net"] }
|
||||
rustix = { version = "1.0.1", features = ["fs", "event", "net", "time", "mm"] }
|
||||
linux-raw-sys = { version = "0.9.2", features = ["ioctl"] }
|
||||
|
||||
[target.'cfg(target_os="windows")'.dependencies]
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use nusb::{
|
||||
transfer::{Control, ControlType, Recipient},
|
||||
MaybeFuture,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
let di = nusb::list_devices()
|
||||
.wait()
|
||||
.unwrap()
|
||||
.find(|d| d.vendor_id() == 0x59e3 && d.product_id() == 0x0a23)
|
||||
.expect("device should be connected");
|
||||
|
||||
println!("Device info: {di:?}");
|
||||
|
||||
let device = di.open().wait().unwrap();
|
||||
|
||||
// Linux can make control transfers without claiming an interface
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
let result = device.control_out_blocking(
|
||||
Control {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
},
|
||||
&[1, 2, 3, 4],
|
||||
Duration::from_secs(1),
|
||||
);
|
||||
println!("{result:?}");
|
||||
|
||||
let mut buf = [0; 64];
|
||||
|
||||
let len = device
|
||||
.control_in_blocking(
|
||||
Control {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
},
|
||||
&mut buf,
|
||||
Duration::from_secs(1),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("{result:?}, {data:?}", data = &buf[..len]);
|
||||
}
|
||||
|
||||
// but we also provide an API on the `Interface` to support Windows
|
||||
let interface = device.claim_interface(0).wait().unwrap();
|
||||
|
||||
let result = interface.control_out_blocking(
|
||||
Control {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
},
|
||||
&[1, 2, 3, 4, 5],
|
||||
Duration::from_secs(1),
|
||||
);
|
||||
println!("{result:?}");
|
||||
|
||||
let mut buf = [0; 64];
|
||||
|
||||
let len = interface
|
||||
.control_in_blocking(
|
||||
Control {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
},
|
||||
&mut buf,
|
||||
Duration::from_secs(1),
|
||||
)
|
||||
.unwrap();
|
||||
println!("{data:?}", data = &buf[..len]);
|
||||
}
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
use futures_lite::future::block_on;
|
||||
use nusb::{transfer::RequestBuffer, MaybeFuture};
|
||||
use std::time::Duration;
|
||||
|
||||
use nusb::{
|
||||
transfer::{Bulk, In, Out},
|
||||
MaybeFuture,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
|
@ -13,21 +17,27 @@ fn main() {
|
|||
|
||||
let device = di.open().wait().unwrap();
|
||||
let interface = device.claim_interface(0).wait().unwrap();
|
||||
|
||||
block_on(interface.bulk_out(0x02, Vec::from([1, 2, 3, 4, 5])))
|
||||
.into_result()
|
||||
let mut ep_out = interface.endpoint::<Bulk, Out>(0x02).unwrap();
|
||||
let mut ep_in = interface.endpoint::<Bulk, In>(0x81).unwrap();
|
||||
ep_out.submit(vec![1, 2, 3, 4, 5].into());
|
||||
ep_out
|
||||
.wait_next_complete(Duration::from_millis(1000))
|
||||
.unwrap()
|
||||
.status
|
||||
.unwrap();
|
||||
|
||||
let mut queue = interface.bulk_in_queue(0x81);
|
||||
|
||||
loop {
|
||||
while queue.pending() < 8 {
|
||||
queue.submit(RequestBuffer::new(256));
|
||||
while ep_in.pending() < 8 {
|
||||
let buffer = ep_in.allocate(4096);
|
||||
ep_in.submit(buffer);
|
||||
}
|
||||
let result = block_on(queue.next_complete());
|
||||
let result = ep_in
|
||||
.wait_next_complete(Duration::from_millis(1000))
|
||||
.unwrap();
|
||||
println!("{result:?}");
|
||||
if result.status.is_err() {
|
||||
break;
|
||||
}
|
||||
ep_in.submit(result.buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use futures_lite::future::block_on;
|
||||
use std::time::Duration;
|
||||
|
||||
use nusb::{
|
||||
transfer::{ControlIn, ControlOut, ControlType, Recipient},
|
||||
MaybeFuture,
|
||||
|
|
@ -19,47 +20,67 @@ fn main() {
|
|||
// Linux can make control transfers without claiming an interface
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
let result = block_on(device.control_out(ControlOut {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
data: &[1, 2, 3, 4],
|
||||
}));
|
||||
let result = device
|
||||
.control_out(
|
||||
ControlOut {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
data: &[1, 2, 3, 4],
|
||||
},
|
||||
Duration::from_millis(100),
|
||||
)
|
||||
.wait();
|
||||
println!("{result:?}");
|
||||
|
||||
let result = block_on(device.control_in(ControlIn {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
length: 256,
|
||||
}));
|
||||
let result = device
|
||||
.control_in(
|
||||
ControlIn {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
length: 256,
|
||||
},
|
||||
Duration::from_millis(100),
|
||||
)
|
||||
.wait();
|
||||
println!("{result:?}");
|
||||
}
|
||||
|
||||
// but we also provide an API on the `Interface` to support Windows
|
||||
let interface = device.claim_interface(0).wait().unwrap();
|
||||
|
||||
let result = block_on(interface.control_out(ControlOut {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
data: &[1, 2, 3, 4],
|
||||
}));
|
||||
let result = interface
|
||||
.control_out(
|
||||
ControlOut {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
data: &[1, 2, 3, 4],
|
||||
},
|
||||
Duration::from_millis(100),
|
||||
)
|
||||
.wait();
|
||||
println!("{result:?}");
|
||||
|
||||
let result = block_on(interface.control_in(ControlIn {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
length: 256,
|
||||
}));
|
||||
let result = interface
|
||||
.control_in(
|
||||
ControlIn {
|
||||
control_type: ControlType::Vendor,
|
||||
recipient: Recipient::Device,
|
||||
request: 0x81,
|
||||
value: 0x9999,
|
||||
index: 0x9999,
|
||||
length: 256,
|
||||
},
|
||||
Duration::from_millis(100),
|
||||
)
|
||||
.wait();
|
||||
println!("{result:?}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ fn inspect_device(dev: DeviceInfo) {
|
|||
|
||||
let languages: Vec<u16> = dev
|
||||
.get_string_descriptor_supported_languages(timeout)
|
||||
.wait()
|
||||
.map(|i| i.collect())
|
||||
.unwrap_or_default();
|
||||
println!(" Languages: {languages:02x?}");
|
||||
|
|
@ -40,17 +41,23 @@ fn inspect_device(dev: DeviceInfo) {
|
|||
let language = languages.first().copied().unwrap_or(US_ENGLISH);
|
||||
|
||||
if let Some(i_manufacturer) = dev_descriptor.manufacturer_string_index() {
|
||||
let s = dev.get_string_descriptor(i_manufacturer, language, timeout);
|
||||
let s = dev
|
||||
.get_string_descriptor(i_manufacturer, language, timeout)
|
||||
.wait();
|
||||
println!(" Manufacturer({i_manufacturer}): {s:?}");
|
||||
}
|
||||
|
||||
if let Some(i_product) = dev_descriptor.product_string_index() {
|
||||
let s = dev.get_string_descriptor(i_product, language, timeout);
|
||||
let s = dev
|
||||
.get_string_descriptor(i_product, language, timeout)
|
||||
.wait();
|
||||
println!(" Product({i_product}): {s:?}");
|
||||
}
|
||||
|
||||
if let Some(i_serial) = dev_descriptor.serial_number_string_index() {
|
||||
let s = dev.get_string_descriptor(i_serial, language, timeout);
|
||||
let s = dev
|
||||
.get_string_descriptor(i_serial, language, timeout)
|
||||
.wait();
|
||||
println!(" Serial({i_serial}): {s:?}");
|
||||
}
|
||||
|
||||
|
|
|
|||
26
src/bitset.rs
Normal file
26
src/bitset.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/// Bitset capable of storing 0x00..=0x0f and 0x80..=0x8f
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct EndpointBitSet(u32);
|
||||
|
||||
impl EndpointBitSet {
|
||||
pub fn mask(ep: u8) -> u32 {
|
||||
let bit = ((ep & 0x0f) << 1) | (ep >> 7);
|
||||
1 << bit
|
||||
}
|
||||
|
||||
pub fn is_set(&self, bit: u8) -> bool {
|
||||
self.0 & Self::mask(bit) != 0
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
pub fn set(&mut self, bit: u8) {
|
||||
self.0 |= Self::mask(bit)
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, bit: u8) {
|
||||
self.0 &= !Self::mask(bit)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,10 +13,7 @@ use std::{
|
|||
|
||||
use log::warn;
|
||||
|
||||
use crate::{
|
||||
transfer::{Direction, TransferType},
|
||||
Error,
|
||||
};
|
||||
use crate::{transfer::Direction, Error};
|
||||
|
||||
pub(crate) const DESCRIPTOR_TYPE_DEVICE: u8 = 0x01;
|
||||
pub(crate) const DESCRIPTOR_LEN_DEVICE: u8 = 18;
|
||||
|
|
@ -696,6 +693,23 @@ impl<'a> Debug for EndpointDescriptor<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Endpoint type.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum TransferType {
|
||||
/// Control endpoint.
|
||||
Control = 0,
|
||||
|
||||
/// Isochronous endpoint.
|
||||
Isochronous = 1,
|
||||
|
||||
/// Bulk endpoint.
|
||||
Bulk = 2,
|
||||
|
||||
/// Interrupt endpoint.
|
||||
Interrupt = 3,
|
||||
}
|
||||
|
||||
/// Error from [`crate::Device::active_configuration`]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ActiveConfigurationError {
|
||||
|
|
|
|||
519
src/device.rs
519
src/device.rs
|
|
@ -5,13 +5,21 @@ use crate::{
|
|||
},
|
||||
platform,
|
||||
transfer::{
|
||||
Control, ControlIn, ControlOut, Queue, RequestBuffer, TransferError, TransferFuture,
|
||||
TransferType,
|
||||
Buffer, BulkOrInterrupt, Completion, ControlIn, ControlOut, Direction, EndpointDirection,
|
||||
EndpointType, TransferError,
|
||||
},
|
||||
DeviceInfo, Error, MaybeFuture, Speed,
|
||||
};
|
||||
use log::error;
|
||||
use std::{io::ErrorKind, num::NonZeroU8, sync::Arc, time::Duration};
|
||||
use std::{
|
||||
future::{poll_fn, Future},
|
||||
io::ErrorKind,
|
||||
marker::PhantomData,
|
||||
num::NonZeroU8,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// An opened USB device.
|
||||
///
|
||||
|
|
@ -146,7 +154,7 @@ impl Device {
|
|||
/// Set the device configuration.
|
||||
///
|
||||
/// The argument is the desired configuration's `bConfigurationValue`
|
||||
/// descriptor field from [`Configuration::configuration_value`] or `0` to
|
||||
/// descriptor field from [`ConfigurationDescriptor::configuration_value`] or `0` to
|
||||
/// unconfigure the device.
|
||||
///
|
||||
/// ### Platform-specific notes
|
||||
|
|
@ -173,11 +181,12 @@ impl Device {
|
|||
desc_index: u8,
|
||||
language_id: u16,
|
||||
timeout: Duration,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, Error>> {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let _ = timeout;
|
||||
self.backend
|
||||
.clone()
|
||||
.get_descriptor(desc_type, desc_index, language_id)
|
||||
}
|
||||
|
||||
|
|
@ -186,21 +195,18 @@ impl Device {
|
|||
const STANDARD_REQUEST_GET_DESCRIPTOR: u8 = 0x06;
|
||||
use crate::transfer::{ControlType, Recipient};
|
||||
|
||||
let mut buf = vec![0; 4096];
|
||||
let len = self.control_in_blocking(
|
||||
Control {
|
||||
self.control_in(
|
||||
ControlIn {
|
||||
control_type: ControlType::Standard,
|
||||
recipient: Recipient::Device,
|
||||
request: STANDARD_REQUEST_GET_DESCRIPTOR,
|
||||
value: ((desc_type as u16) << 8) | desc_index as u16,
|
||||
index: language_id,
|
||||
length: 4096,
|
||||
},
|
||||
&mut buf,
|
||||
timeout,
|
||||
)?;
|
||||
|
||||
buf.truncate(len);
|
||||
Ok(buf)
|
||||
)
|
||||
.map(|r| Ok(r?))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -212,22 +218,24 @@ impl Device {
|
|||
pub fn get_string_descriptor_supported_languages(
|
||||
&self,
|
||||
timeout: Duration,
|
||||
) -> Result<impl Iterator<Item = u16>, Error> {
|
||||
let data = self.get_descriptor(DESCRIPTOR_TYPE_STRING, 0, 0, timeout)?;
|
||||
) -> impl MaybeFuture<Output = Result<impl Iterator<Item = u16>, Error>> {
|
||||
self.get_descriptor(DESCRIPTOR_TYPE_STRING, 0, 0, timeout)
|
||||
.map(move |r| {
|
||||
let data = r?;
|
||||
if !validate_string_descriptor(&data) {
|
||||
error!("String descriptor language list read {data:?}, not a valid string descriptor");
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"string descriptor data was invalid",
|
||||
));
|
||||
}
|
||||
|
||||
if !validate_string_descriptor(&data) {
|
||||
error!("String descriptor language list read {data:?}, not a valid string descriptor");
|
||||
return Err(Error::new(
|
||||
ErrorKind::InvalidData,
|
||||
"string descriptor data was invalid",
|
||||
));
|
||||
}
|
||||
|
||||
//TODO: Use array_chunks once stable
|
||||
let mut iter = data.into_iter().skip(2);
|
||||
Ok(std::iter::from_fn(move || {
|
||||
Some(u16::from_le_bytes([iter.next()?, iter.next()?]))
|
||||
}))
|
||||
//TODO: Use array_chunks once stable
|
||||
let mut iter = data.into_iter().skip(2);
|
||||
Ok(std::iter::from_fn(move || {
|
||||
Some(u16::from_le_bytes([iter.next()?, iter.next()?]))
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
/// Request a string descriptor from the device.
|
||||
|
|
@ -244,22 +252,25 @@ impl Device {
|
|||
desc_index: NonZeroU8,
|
||||
language_id: u16,
|
||||
timeout: Duration,
|
||||
) -> Result<String, Error> {
|
||||
let data = self.get_descriptor(
|
||||
) -> impl MaybeFuture<Output = Result<String, Error>> {
|
||||
self.get_descriptor(
|
||||
DESCRIPTOR_TYPE_STRING,
|
||||
desc_index.get(),
|
||||
language_id,
|
||||
timeout,
|
||||
)?;
|
||||
|
||||
decode_string_descriptor(&data)
|
||||
.map_err(|_| Error::new(ErrorKind::InvalidData, "string descriptor data was invalid"))
|
||||
)
|
||||
.map(|r| {
|
||||
let data = r?;
|
||||
decode_string_descriptor(&data).map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidData, "string descriptor data was invalid")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Reset the device, forcing it to re-enumerate.
|
||||
///
|
||||
/// This `Device` will no longer be usable, and you should drop it and call
|
||||
/// [`super::list_devices`] to find and re-open it again.
|
||||
/// [`list_devices`][`super::list_devices`] to find and re-open it again.
|
||||
///
|
||||
/// ### Platform-specific notes
|
||||
/// * Not supported on Windows
|
||||
|
|
@ -267,47 +278,12 @@ impl Device {
|
|||
self.backend.clone().reset()
|
||||
}
|
||||
|
||||
/// Synchronously perform a single **IN (device-to-host)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Platform-specific notes
|
||||
///
|
||||
/// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
|
||||
/// and use the interface handle to submit transfers.
|
||||
/// * On Linux, this takes a device-wide lock, so if you have multiple threads, you
|
||||
/// are better off using the async methods.
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))]
|
||||
pub fn control_in_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &mut [u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.backend.control_in_blocking(control, data, timeout)
|
||||
}
|
||||
|
||||
/// Synchronously perform a single **OUT (host-to-device)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Platform-specific notes
|
||||
///
|
||||
/// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
|
||||
/// and use the interface handle to submit transfers.
|
||||
/// * On Linux, this takes a device-wide lock, so if you have multiple threads, you
|
||||
/// are better off using the async methods.
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))]
|
||||
pub fn control_out_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.backend.control_out_blocking(control, data, timeout)
|
||||
}
|
||||
|
||||
/// Asynchronously submit a single **IN (device-to-host)** transfer on the default **control** endpoint.
|
||||
/// Submit a single **IN (device-to-host)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::time::Duration;
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::{ ControlIn, ControlType, Recipient };
|
||||
/// # use nusb::MaybeFuture;
|
||||
|
|
@ -315,14 +291,14 @@ impl Device {
|
|||
/// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
|
||||
/// # let device = di.open().wait().unwrap();
|
||||
///
|
||||
/// let data: Vec<u8> = block_on(device.control_in(ControlIn {
|
||||
/// let data: Vec<u8> = device.control_in(ControlIn {
|
||||
/// control_type: ControlType::Vendor,
|
||||
/// recipient: Recipient::Device,
|
||||
/// request: 0x30,
|
||||
/// value: 0x0,
|
||||
/// index: 0x0,
|
||||
/// length: 64,
|
||||
/// })).into_result()?;
|
||||
/// }, Duration::from_millis(100)).wait()?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
///
|
||||
|
|
@ -331,10 +307,12 @@ impl Device {
|
|||
/// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
|
||||
/// and use the interface handle to submit transfers.
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))]
|
||||
pub fn control_in(&self, data: ControlIn) -> TransferFuture<ControlIn> {
|
||||
let mut t = self.backend.make_control_transfer();
|
||||
t.submit::<ControlIn>(data);
|
||||
TransferFuture::new(t)
|
||||
pub fn control_in(
|
||||
&self,
|
||||
data: ControlIn,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
|
||||
self.backend.clone().control_in(data, timeout)
|
||||
}
|
||||
|
||||
/// Submit a single **OUT (host-to-device)** transfer on the default **control** endpoint.
|
||||
|
|
@ -342,6 +320,7 @@ impl Device {
|
|||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::time::Duration;
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::{ ControlOut, ControlType, Recipient };
|
||||
/// # use nusb::MaybeFuture;
|
||||
|
|
@ -349,14 +328,14 @@ impl Device {
|
|||
/// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
|
||||
/// # let device = di.open().wait().unwrap();
|
||||
///
|
||||
/// block_on(device.control_out(ControlOut {
|
||||
/// device.control_out(ControlOut {
|
||||
/// control_type: ControlType::Vendor,
|
||||
/// recipient: Recipient::Device,
|
||||
/// request: 0x32,
|
||||
/// value: 0x0,
|
||||
/// index: 0x0,
|
||||
/// data: &[0x01, 0x02, 0x03, 0x04],
|
||||
/// })).into_result()?;
|
||||
/// }, Duration::from_millis(100)).wait()?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
///
|
||||
|
|
@ -365,10 +344,12 @@ impl Device {
|
|||
/// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
|
||||
/// and use the interface handle to submit transfers.
|
||||
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "android"))]
|
||||
pub fn control_out(&self, data: ControlOut) -> TransferFuture<ControlOut> {
|
||||
let mut t = self.backend.make_control_transfer();
|
||||
t.submit::<ControlOut>(data);
|
||||
TransferFuture::new(t)
|
||||
pub fn control_out(
|
||||
&self,
|
||||
data: ControlOut,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<(), TransferError>> {
|
||||
self.backend.clone().control_out(data, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -378,7 +359,7 @@ impl Device {
|
|||
///
|
||||
/// This type is reference-counted with an [`Arc`] internally, and can be cloned cheaply for
|
||||
/// use in multiple places in your program. The interface is released when all clones, and all
|
||||
/// associated [`TransferFuture`]s and [`Queue`]s are dropped.
|
||||
/// associated [`Endpoint`]s are dropped.
|
||||
#[derive(Clone)]
|
||||
pub struct Interface {
|
||||
backend: Arc<platform::Interface>,
|
||||
|
|
@ -388,6 +369,7 @@ impl Interface {
|
|||
pub(crate) fn wrap(backend: Arc<platform::Interface>) -> Self {
|
||||
Interface { backend }
|
||||
}
|
||||
|
||||
/// Select the alternate setting of this interface.
|
||||
///
|
||||
/// An alternate setting is a mode of the interface that makes particular endpoints available
|
||||
|
|
@ -402,51 +384,12 @@ impl Interface {
|
|||
self.backend.get_alt_setting()
|
||||
}
|
||||
|
||||
/// Synchronously perform a single **IN (device-to-host)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Platform-specific notes
|
||||
///
|
||||
/// * On Linux, this takes a device-wide lock, so if you have multiple
|
||||
/// threads, you are better off using the async methods.
|
||||
/// * On Windows, if the `recipient` is `Interface`, the WinUSB driver sends
|
||||
/// the interface number in the least significant byte of `index`,
|
||||
/// overriding any value passed. A warning is logged if the passed `index`
|
||||
/// least significant byte differs from the interface number, and this may
|
||||
/// become an error in the future.
|
||||
pub fn control_in_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &mut [u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.backend.control_in_blocking(control, data, timeout)
|
||||
}
|
||||
|
||||
/// Synchronously perform a single **OUT (host-to-device)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Platform-specific notes
|
||||
///
|
||||
/// * On Linux, this takes a device-wide lock, so if you have multiple
|
||||
/// threads, you are better off using the async methods.
|
||||
/// * On Windows, if the `recipient` is `Interface`, the WinUSB driver sends
|
||||
/// the interface number in the least significant byte of `index`,
|
||||
/// overriding any value passed. A warning is logged if the passed `index`
|
||||
/// least significant byte differs from the interface number, and this may
|
||||
/// become an error in the future.
|
||||
pub fn control_out_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.backend.control_out_blocking(control, data, timeout)
|
||||
}
|
||||
|
||||
/// Submit a single **IN (device-to-host)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::time::Duration;
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::{ ControlIn, ControlType, Recipient };
|
||||
/// # use nusb::MaybeFuture;
|
||||
|
|
@ -455,14 +398,14 @@ impl Interface {
|
|||
/// # let device = di.open().wait().unwrap();
|
||||
/// # let interface = device.claim_interface(0).wait().unwrap();
|
||||
///
|
||||
/// let data: Vec<u8> = block_on(interface.control_in(ControlIn {
|
||||
/// let data: Vec<u8> = interface.control_in(ControlIn {
|
||||
/// control_type: ControlType::Vendor,
|
||||
/// recipient: Recipient::Device,
|
||||
/// request: 0x30,
|
||||
/// value: 0x0,
|
||||
/// index: 0x0,
|
||||
/// length: 64,
|
||||
/// })).into_result()?;
|
||||
/// }, Duration::from_millis(100)).wait()?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
///
|
||||
|
|
@ -472,17 +415,23 @@ impl Interface {
|
|||
/// overriding any value passed. A warning is logged if the passed `index`
|
||||
/// least significant byte differs from the interface number, and this may
|
||||
/// become an error in the future.
|
||||
pub fn control_in(&self, data: ControlIn) -> TransferFuture<ControlIn> {
|
||||
let mut t = self.backend.make_transfer(0, TransferType::Control);
|
||||
t.submit::<ControlIn>(data);
|
||||
TransferFuture::new(t)
|
||||
/// * On Windows, the timeout is currently fixed to 5 seconds and the timeout
|
||||
/// argument is ignored.
|
||||
pub fn control_in(
|
||||
&self,
|
||||
data: ControlIn,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
|
||||
self.backend.clone().control_in(data, timeout)
|
||||
}
|
||||
|
||||
/// Submit a single **OUT (host-to-device)** transfer on the default **control** endpoint.
|
||||
/// Submit a single **OUT (host-to-device)** transfer on the default
|
||||
/// **control** endpoint.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::time::Duration;
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::{ ControlOut, ControlType, Recipient };
|
||||
/// # use nusb::MaybeFuture;
|
||||
|
|
@ -491,14 +440,14 @@ impl Interface {
|
|||
/// # let device = di.open().wait().unwrap();
|
||||
/// # let interface = device.claim_interface(0).wait().unwrap();
|
||||
///
|
||||
/// block_on(interface.control_out(ControlOut {
|
||||
/// interface.control_out(ControlOut {
|
||||
/// control_type: ControlType::Vendor,
|
||||
/// recipient: Recipient::Device,
|
||||
/// request: 0x32,
|
||||
/// value: 0x0,
|
||||
/// index: 0x0,
|
||||
/// data: &[0x01, 0x02, 0x03, 0x04],
|
||||
/// })).into_result()?;
|
||||
/// }, Duration::from_millis(100)).wait()?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
///
|
||||
|
|
@ -508,94 +457,14 @@ impl Interface {
|
|||
/// overriding any value passed. A warning is logged if the passed `index`
|
||||
/// least significant byte differs from the interface number, and this may
|
||||
/// become an error in the future.
|
||||
pub fn control_out(&self, data: ControlOut) -> TransferFuture<ControlOut> {
|
||||
let mut t = self.backend.make_transfer(0, TransferType::Control);
|
||||
t.submit::<ControlOut>(data);
|
||||
TransferFuture::new(t)
|
||||
}
|
||||
|
||||
/// Submit a single **IN (device-to-host)** transfer on the specified **bulk** endpoint.
|
||||
///
|
||||
/// * The requested length must be a multiple of the endpoint's maximum packet size
|
||||
/// * An IN endpoint address must have the top (`0x80`) bit set.
|
||||
pub fn bulk_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture<RequestBuffer> {
|
||||
let mut t = self.backend.make_transfer(endpoint, TransferType::Bulk);
|
||||
t.submit(buf);
|
||||
TransferFuture::new(t)
|
||||
}
|
||||
|
||||
/// Submit a single **OUT (host-to-device)** transfer on the specified **bulk** endpoint.
|
||||
///
|
||||
/// * An OUT endpoint address must have the top (`0x80`) bit clear.
|
||||
pub fn bulk_out(&self, endpoint: u8, buf: Vec<u8>) -> TransferFuture<Vec<u8>> {
|
||||
let mut t = self.backend.make_transfer(endpoint, TransferType::Bulk);
|
||||
t.submit(buf);
|
||||
TransferFuture::new(t)
|
||||
}
|
||||
|
||||
/// Create a queue for managing multiple **IN (device-to-host)** transfers on a **bulk** endpoint.
|
||||
///
|
||||
/// * An IN endpoint address must have the top (`0x80`) bit set.
|
||||
pub fn bulk_in_queue(&self, endpoint: u8) -> Queue<RequestBuffer> {
|
||||
Queue::new(self.backend.clone(), endpoint, TransferType::Bulk)
|
||||
}
|
||||
|
||||
/// Create a queue for managing multiple **OUT (host-to-device)** transfers on a **bulk** endpoint.
|
||||
///
|
||||
/// * An OUT endpoint address must have the top (`0x80`) bit clear.
|
||||
pub fn bulk_out_queue(&self, endpoint: u8) -> Queue<Vec<u8>> {
|
||||
Queue::new(self.backend.clone(), endpoint, TransferType::Bulk)
|
||||
}
|
||||
|
||||
/// Submit a single **IN (device-to-host)** transfer on the specified **interrupt** endpoint.
|
||||
///
|
||||
/// * The requested length must be a multiple of the endpoint's maximum packet size
|
||||
/// * An IN endpoint address must have the top (`0x80`) bit set.
|
||||
pub fn interrupt_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture<RequestBuffer> {
|
||||
let mut t = self
|
||||
.backend
|
||||
.make_transfer(endpoint, TransferType::Interrupt);
|
||||
t.submit(buf);
|
||||
TransferFuture::new(t)
|
||||
}
|
||||
|
||||
/// Submit a single **OUT (host-to-device)** transfer on the specified **interrupt** endpoint.
|
||||
///
|
||||
/// * An OUT endpoint address must have the top (`0x80`) bit clear.
|
||||
pub fn interrupt_out(&self, endpoint: u8, buf: Vec<u8>) -> TransferFuture<Vec<u8>> {
|
||||
let mut t = self
|
||||
.backend
|
||||
.make_transfer(endpoint, TransferType::Interrupt);
|
||||
t.submit(buf);
|
||||
TransferFuture::new(t)
|
||||
}
|
||||
|
||||
/// Create a queue for managing multiple **IN (device-to-host)** transfers on an **interrupt** endpoint.
|
||||
///
|
||||
/// * An IN endpoint address must have the top (`0x80`) bit set.
|
||||
pub fn interrupt_in_queue(&self, endpoint: u8) -> Queue<RequestBuffer> {
|
||||
Queue::new(self.backend.clone(), endpoint, TransferType::Interrupt)
|
||||
}
|
||||
|
||||
/// Create a queue for managing multiple **OUT (device-to-host)** transfers on an **interrupt** endpoint.
|
||||
///
|
||||
/// * An OUT endpoint address must have the top (`0x80`) bit clear.
|
||||
pub fn interrupt_out_queue(&self, endpoint: u8) -> Queue<Vec<u8>> {
|
||||
Queue::new(self.backend.clone(), endpoint, TransferType::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) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
self.backend.clone().clear_halt(endpoint)
|
||||
/// * On Windows, the timeout is currently fixed to 5 seconds and the timeout
|
||||
/// argument is ignored.
|
||||
pub fn control_out(
|
||||
&self,
|
||||
data: ControlOut,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<(), TransferError>> {
|
||||
self.backend.clone().control_out(data, timeout)
|
||||
}
|
||||
|
||||
/// Get the interface number.
|
||||
|
|
@ -626,11 +495,217 @@ impl Interface {
|
|||
self.descriptors()
|
||||
.find(|i| i.alternate_setting() == self.get_alt_setting())
|
||||
}
|
||||
|
||||
/// Open an endpoint.
|
||||
pub fn endpoint<EpType: EndpointType, Dir: EndpointDirection>(
|
||||
&self,
|
||||
address: u8,
|
||||
) -> Result<Endpoint<EpType, Dir>, ClaimEndpointError> {
|
||||
let intf_desc = self.descriptor();
|
||||
let ep_desc =
|
||||
intf_desc.and_then(|desc| desc.endpoints().find(|ep| ep.address() == address));
|
||||
let Some(ep_desc) = ep_desc else {
|
||||
return Err(ClaimEndpointError::InvalidAddress);
|
||||
};
|
||||
|
||||
if ep_desc.transfer_type() != EpType::TYPE || address & Direction::MASK != Dir::DIR as u8 {
|
||||
return Err(ClaimEndpointError::InvalidType);
|
||||
}
|
||||
|
||||
let backend = self.backend.endpoint(ep_desc)?;
|
||||
Ok(Endpoint {
|
||||
backend,
|
||||
ep_type: PhantomData,
|
||||
ep_dir: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Error from [`Interface::endpoint`].
|
||||
#[non_exhaustive]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ClaimEndpointError {
|
||||
/// The specified address does not exist on this interface and alternate setting
|
||||
InvalidAddress,
|
||||
|
||||
/// The type or direction does not match the endpoint descriptor for this address
|
||||
InvalidType,
|
||||
|
||||
/// The endpoint is already claimed
|
||||
Busy,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ClaimEndpointError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ClaimEndpointError::InvalidAddress => write!(f, "invalid endpoint address"),
|
||||
ClaimEndpointError::InvalidType => write!(f, "incorrect endpoint type or direction"),
|
||||
ClaimEndpointError::Busy => write!(f, "endpoint is already claimed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ClaimEndpointError {}
|
||||
|
||||
/// Exclusive access to an endpoint of a USB device.
|
||||
///
|
||||
/// Obtain an `Endpoint` with the [`Interface::endpoint`] method.
|
||||
///
|
||||
/// An `Endpoint` manages a queue of pending transfers. Submitting a transfer is
|
||||
/// a non-blocking operation that adds the operation to the queue. Completed
|
||||
/// transfers can be popped from the queue synchronously or asynchronously.
|
||||
///
|
||||
/// This separation of submission and completion makes the API cancel-safe,
|
||||
/// and makes it easy to submit multiple transfers at once, regardless of
|
||||
/// whether you are using asynchronous or blocking waits.
|
||||
///
|
||||
/// To maximize throughput and minimize latency when streaming data, the host
|
||||
/// controller needs to attempt a transfer in every possible frame. That
|
||||
/// requires always having a transfer request pending with the kernel by
|
||||
/// submitting multiple transfer requests and re-submitting them as they
|
||||
/// complete.
|
||||
///
|
||||
/// When the `Endpoint` is dropped, any pending transfers are cancelled.
|
||||
pub struct Endpoint<EpType, Dir> {
|
||||
backend: platform::Endpoint,
|
||||
ep_type: PhantomData<EpType>,
|
||||
ep_dir: PhantomData<Dir>,
|
||||
}
|
||||
|
||||
/// Methods for all endpoints.
|
||||
impl<EpType: EndpointType, Dir: EndpointDirection> Endpoint<EpType, Dir> {
|
||||
/// Get the endpoint address.
|
||||
pub fn endpoint_address(&self) -> u8 {
|
||||
self.backend.endpoint_address()
|
||||
}
|
||||
|
||||
/// Get the maximum packet size for this endpoint.
|
||||
///
|
||||
/// Transfers can consist of multiple packets, but are split into packets
|
||||
/// of this size on the bus.
|
||||
pub fn max_packet_size(&self) -> usize {
|
||||
self.backend.max_packet_size
|
||||
}
|
||||
|
||||
/// Get the number of transfers that have been submitted with `submit` that
|
||||
/// have not yet been returned from `next_complete`.
|
||||
pub fn pending(&self) -> usize {
|
||||
self.backend.pending()
|
||||
}
|
||||
|
||||
/// Request cancellation of all pending transfers.
|
||||
///
|
||||
/// The transfers are cancelled asynchronously. Once cancelled, they will be
|
||||
/// returned from calls to `next_complete` so you can tell which were
|
||||
/// completed, partially-completed, or cancelled.
|
||||
pub fn cancel_all(&mut self) {
|
||||
self.backend.cancel_all()
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for Bulk and Interrupt endpoints.
|
||||
impl<EpType: BulkOrInterrupt, Dir: EndpointDirection> Endpoint<EpType, Dir> {
|
||||
/// Allocate a buffer for use on this endpoint, zero-copy if possible.
|
||||
///
|
||||
/// A zero-copy buffer allows the kernel to DMA directly to/from this
|
||||
/// buffer for improved performance. However, because it is not allocated
|
||||
/// with the system allocator, it cannot be converted to a [`Vec`] without
|
||||
/// copying.
|
||||
///
|
||||
/// This is currently only supported on Linux, falling back to [`Buffer::new`]
|
||||
/// on other platforms, or if the memory allocation fails.
|
||||
pub fn allocate(&self, len: usize) -> Buffer {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
{
|
||||
if let Ok(b) = self.backend.allocate(len) {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
Buffer::new(len)
|
||||
}
|
||||
|
||||
/// Begin a transfer on the endpoint.
|
||||
///
|
||||
/// Submitted transfers are queued and completed in order. Once the transfer
|
||||
/// completes, it will be returned from
|
||||
/// [`next_complete()`][`Self::next_complete`]. Any error in submitting or
|
||||
/// performing the transfer is deferred until `next_complete`.
|
||||
///
|
||||
/// 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`].
|
||||
pub fn submit(&mut self, buf: Buffer) {
|
||||
self.backend.submit(buf)
|
||||
}
|
||||
|
||||
/// Return a `Future` that waits for the next pending transfer to complete.
|
||||
///
|
||||
/// This future is cancel-safe: it can be cancelled and re-created without
|
||||
/// side effects, enabling its use in `select!{}` or similar.
|
||||
///
|
||||
/// ## Panics
|
||||
/// * if there are no transfers pending (that is, if [`Self::pending()`]
|
||||
/// would return 0).
|
||||
pub fn next_complete(&mut self) -> impl Future<Output = Completion> + Send + Sync + '_ {
|
||||
poll_fn(|cx| self.poll_next_complete(cx))
|
||||
}
|
||||
|
||||
/// Poll for a pending transfer completion.
|
||||
///
|
||||
/// Returns a completed transfer if one is available, or arranges for the
|
||||
/// context's waker to be notified when a transfer completes.
|
||||
///
|
||||
/// ## Panics
|
||||
/// * if there are no transfers pending (that is, if [`Self::pending()`]
|
||||
/// would return 0).
|
||||
pub fn poll_next_complete(&mut self, cx: &mut Context<'_>) -> Poll<Completion> {
|
||||
self.backend.poll_next_complete(cx)
|
||||
}
|
||||
|
||||
/// Wait for a pending transfer completion.
|
||||
///
|
||||
/// Blocks for up to `timeout` waiting for a transfer to complete, or
|
||||
/// returns `None` if the timeout is reached.
|
||||
///
|
||||
/// Note that the transfer is not cancelled after the timeout, and can still
|
||||
/// be returned from a subsequent call.
|
||||
///
|
||||
/// ## Panics
|
||||
/// * if there are no transfers pending (that is, if [`Self::pending()`]
|
||||
/// would return 0).
|
||||
pub fn wait_next_complete(&mut self, timeout: Duration) -> Option<Completion> {
|
||||
self.backend.wait_next_complete(timeout)
|
||||
}
|
||||
|
||||
/// 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`][crate::transfer::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) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
self.backend.clear_halt()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_send_sync() {
|
||||
use crate::transfer::{Bulk, In, Interrupt, Out};
|
||||
|
||||
fn require_send_sync<T: Send + Sync>() {}
|
||||
require_send_sync::<Interface>();
|
||||
require_send_sync::<Device>();
|
||||
require_send_sync::<Endpoint<Bulk, In>>();
|
||||
require_send_sync::<Endpoint<Bulk, Out>>();
|
||||
require_send_sync::<Endpoint<Interrupt, In>>();
|
||||
require_send_sync::<Endpoint<Interrupt, Out>>();
|
||||
}
|
||||
|
|
|
|||
17
src/lib.rs
17
src/lib.rs
|
|
@ -34,10 +34,11 @@
|
|||
//! device. To open an interface, call [`Device::claim_interface`]. Only one
|
||||
//! program (or kernel driver) may claim an interface at a time.
|
||||
//!
|
||||
//! Use the resulting [`Interface`] to transfer data on the device's control,
|
||||
//! bulk or interrupt endpoints. Transfers are async by default, and can be
|
||||
//! awaited as individual [`Future`][`transfer::TransferFuture`]s, or use a
|
||||
//! [`Queue`][`transfer::Queue`] to manage streams of data.
|
||||
//! Use the resulting [`Interface`] to perform control transfers or open
|
||||
//! an [`Endpoint`] to perform bulk or interrupt transfers. Submitting a
|
||||
//! transfer is a non-blocking operation that adds the transfer to an
|
||||
//! internal queue for the endpoint. Completed transfers can be popped
|
||||
//! from the queue synchronously or asynchronously.
|
||||
//!
|
||||
//! *For more details on how USB works, [USB in a
|
||||
//! Nutshell](https://beyondlogic.org/usbnutshell/usb1.shtml) is a good
|
||||
|
|
@ -119,9 +120,9 @@
|
|||
//! This allows for async usage in an async context, or blocking usage in a
|
||||
//! non-async context.
|
||||
//!
|
||||
//! Operations such as [`Device::open`], [`Device::set_configuration`],
|
||||
//! Operations such as [`DeviceInfo::open`], [`Device::set_configuration`],
|
||||
//! [`Device::reset`], [`Device::claim_interface`],
|
||||
//! [`Interface::set_alt_setting`], and [`Interface::clear_halt`] require
|
||||
//! [`Interface::set_alt_setting`], and [`Endpoint::clear_halt`] require
|
||||
//! blocking system calls. To use these in an asynchronous context, `nusb`
|
||||
//! relies on an async runtime to run these operations on an IO thread to avoid
|
||||
//! blocking in async code. Enable the cargo feature `tokio` or `smol` to use
|
||||
|
|
@ -142,7 +143,7 @@ mod enumeration;
|
|||
pub use enumeration::{BusInfo, DeviceId, DeviceInfo, InterfaceInfo, Speed, UsbControllerType};
|
||||
|
||||
mod device;
|
||||
pub use device::{Device, Interface};
|
||||
pub use device::{ClaimEndpointError, Device, Endpoint, Interface};
|
||||
|
||||
pub mod transfer;
|
||||
|
||||
|
|
@ -151,6 +152,8 @@ pub mod hotplug;
|
|||
mod maybe_future;
|
||||
pub use maybe_future::MaybeFuture;
|
||||
|
||||
mod bitset;
|
||||
|
||||
/// OS error returned from operations other than transfers.
|
||||
pub type Error = io::Error;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,43 +1,60 @@
|
|||
use std::io::{ErrorKind, Seek};
|
||||
use std::sync::{Mutex, Weak};
|
||||
use std::{ffi::c_void, time::Duration};
|
||||
use std::{
|
||||
collections::{BTreeMap, VecDeque},
|
||||
ffi::c_void,
|
||||
fs::File,
|
||||
io::Read,
|
||||
io::{ErrorKind, Read, Seek},
|
||||
mem::ManuallyDrop,
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicU8, Ordering},
|
||||
Arc,
|
||||
Arc, Mutex, MutexGuard, Weak,
|
||||
},
|
||||
task::{Context, Poll},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use log::{debug, error, warn};
|
||||
use rustix::event::epoll;
|
||||
use rustix::fd::AsFd;
|
||||
use rustix::{
|
||||
fd::{AsRawFd, FromRawFd, OwnedFd},
|
||||
fs::{Mode, OFlags},
|
||||
event::epoll::EventFlags,
|
||||
fd::{AsFd, AsRawFd, FromRawFd, OwnedFd},
|
||||
fs::{Mode, OFlags, Timespec},
|
||||
io::Errno,
|
||||
time::{timerfd_create, timerfd_settime, Itimerspec, TimerfdFlags, TimerfdTimerFlags},
|
||||
};
|
||||
use slab::Slab;
|
||||
|
||||
use super::{
|
||||
errno_to_transfer_error, events,
|
||||
usbfs::{self, Urb},
|
||||
SysfsPath,
|
||||
SysfsPath, TransferData,
|
||||
};
|
||||
use crate::descriptors::{ConfigurationDescriptor, DeviceDescriptor};
|
||||
use crate::maybe_future::{blocking::Blocking, MaybeFuture};
|
||||
use crate::transfer::{ControlType, Recipient};
|
||||
use crate::{
|
||||
descriptors::{parse_concatenated_config_descriptors, DESCRIPTOR_LEN_DEVICE},
|
||||
bitset::EndpointBitSet,
|
||||
descriptors::{
|
||||
parse_concatenated_config_descriptors, ConfigurationDescriptor, DeviceDescriptor,
|
||||
EndpointDescriptor, TransferType, DESCRIPTOR_LEN_DEVICE,
|
||||
},
|
||||
device::ClaimEndpointError,
|
||||
maybe_future::{blocking::Blocking, MaybeFuture},
|
||||
transfer::{
|
||||
notify_completion, Control, Direction, TransferError, TransferHandle, TransferType,
|
||||
internal::{
|
||||
notify_completion, take_completed_from_queue, Idle, Notify, Pending, TransferFuture,
|
||||
},
|
||||
request_type, Buffer, Completion, ControlIn, ControlOut, ControlType, Direction, Recipient,
|
||||
TransferError,
|
||||
},
|
||||
DeviceInfo, Error, Speed,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct TimeoutEntry {
|
||||
deadline: Instant,
|
||||
urb: *mut Urb,
|
||||
}
|
||||
|
||||
unsafe impl Send for TimeoutEntry {}
|
||||
unsafe impl Sync for TimeoutEntry {}
|
||||
|
||||
static DEVICES: Mutex<Slab<Weak<LinuxDevice>>> = Mutex::new(Slab::new());
|
||||
|
||||
pub(crate) struct LinuxDevice {
|
||||
|
|
@ -49,6 +66,9 @@ pub(crate) struct LinuxDevice {
|
|||
|
||||
sysfs: Option<SysfsPath>,
|
||||
active_config: AtomicU8,
|
||||
|
||||
timerfd: OwnedFd,
|
||||
timeouts: Mutex<BTreeMap<TimeoutEntry, ()>>,
|
||||
}
|
||||
|
||||
impl LinuxDevice {
|
||||
|
|
@ -104,6 +124,12 @@ impl LinuxDevice {
|
|||
Self::get_config(&descriptors, &fd)?
|
||||
};
|
||||
|
||||
let timerfd = timerfd_create(
|
||||
rustix::time::TimerfdClockId::Monotonic,
|
||||
TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK,
|
||||
)
|
||||
.inspect_err(|e| log::error!("Failed to create timerfd: {e}"))?;
|
||||
|
||||
let arc = Arc::new_cyclic(|weak| {
|
||||
let events_id = DEVICES.lock().unwrap().insert(weak.clone());
|
||||
LinuxDevice {
|
||||
|
|
@ -112,6 +138,8 @@ impl LinuxDevice {
|
|||
descriptors,
|
||||
sysfs,
|
||||
active_config: AtomicU8::new(active_config),
|
||||
timerfd,
|
||||
timeouts: Mutex::new(BTreeMap::new()),
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -124,7 +152,13 @@ impl LinuxDevice {
|
|||
events::register_fd(
|
||||
arc.fd.as_fd(),
|
||||
events::Tag::Device(arc.events_id),
|
||||
epoll::EventFlags::OUT,
|
||||
EventFlags::OUT,
|
||||
)?;
|
||||
|
||||
events::register_fd(
|
||||
arc.timerfd.as_fd(),
|
||||
events::Tag::DeviceTimer(arc.events_id),
|
||||
EventFlags::IN,
|
||||
)?;
|
||||
|
||||
Ok(arc)
|
||||
|
|
@ -140,18 +174,29 @@ impl LinuxDevice {
|
|||
fn handle_events(&self) {
|
||||
debug!("Handling events for device {}", self.events_id);
|
||||
match usbfs::reap_urb_ndelay(&self.fd) {
|
||||
Ok(urb_ptr) => {
|
||||
let user_data = {
|
||||
let urb = unsafe { &*urb_ptr };
|
||||
Ok(urb) => {
|
||||
let transfer_data: *mut TransferData = unsafe { &(*urb) }.usercontext.cast();
|
||||
|
||||
{
|
||||
let transfer = unsafe { &*transfer_data };
|
||||
debug_assert!(transfer.urb_ptr() == urb);
|
||||
debug!(
|
||||
"URB {:?} for ep {:x} completed, status={} actual_length={}",
|
||||
urb_ptr, urb.endpoint, urb.status, urb.actual_length
|
||||
transfer.urb_ptr(),
|
||||
transfer.urb().endpoint,
|
||||
transfer.urb().status,
|
||||
transfer.urb().actual_length
|
||||
);
|
||||
urb.usercontext
|
||||
|
||||
if let Some(deadline) = transfer.deadline {
|
||||
let mut timeouts = self.timeouts.lock().unwrap();
|
||||
timeouts.remove(&TimeoutEntry { deadline, urb });
|
||||
self.update_timeouts(timeouts, Instant::now());
|
||||
}
|
||||
};
|
||||
|
||||
// SAFETY: pointer came from submit via kernel an we're now done with it
|
||||
unsafe { notify_completion::<super::TransferData>(user_data) }
|
||||
// SAFETY: pointer came from submit via kernel and we're now done with it
|
||||
unsafe { notify_completion::<super::TransferData>(transfer_data) }
|
||||
}
|
||||
Err(Errno::AGAIN) => {}
|
||||
Err(Errno::NODEV) => {
|
||||
|
|
@ -169,6 +214,71 @@ impl LinuxDevice {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_timer_epoll(id: usize) {
|
||||
let device = DEVICES.lock().unwrap().get(id).and_then(|w| w.upgrade());
|
||||
if let Some(device) = device {
|
||||
device.handle_timeouts();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_timeouts(&self) {
|
||||
debug!("Handling timeouts for device {}", self.events_id);
|
||||
let now = Instant::now();
|
||||
|
||||
rustix::io::read(self.timerfd.as_fd(), &mut [0u8; 8]).ok();
|
||||
|
||||
let mut timeouts = self.timeouts.lock().unwrap();
|
||||
while let Some(entry) = timeouts.first_entry() {
|
||||
if entry.key().deadline > now {
|
||||
break;
|
||||
}
|
||||
|
||||
let urb = entry.remove_entry().0.urb;
|
||||
|
||||
unsafe {
|
||||
match usbfs::discard_urb(&self.fd, urb) {
|
||||
Ok(()) => debug!("Cancelled URB {urb:?} after timeout"),
|
||||
Err(e) => debug!("Failed to cancel timed out URB {urb:?}: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.update_timeouts(timeouts, now);
|
||||
}
|
||||
|
||||
fn update_timeouts(&self, timeouts: MutexGuard<BTreeMap<TimeoutEntry, ()>>, now: Instant) {
|
||||
const TIMESPEC_ZERO: Timespec = Timespec {
|
||||
tv_sec: 0,
|
||||
tv_nsec: 0,
|
||||
};
|
||||
|
||||
let next = if let Some((TimeoutEntry { deadline, .. }, _)) = timeouts.first_key_value() {
|
||||
let duration = deadline
|
||||
.checked_duration_since(now)
|
||||
.unwrap_or(Duration::from_nanos(1));
|
||||
log::debug!("Next timeout in {duration:?}");
|
||||
Timespec {
|
||||
tv_sec: duration.as_secs() as i64,
|
||||
tv_nsec: duration.subsec_nanos() as i64,
|
||||
}
|
||||
} else {
|
||||
TIMESPEC_ZERO
|
||||
};
|
||||
|
||||
timerfd_settime(
|
||||
self.timerfd.as_fd(),
|
||||
TimerfdTimerFlags::empty(),
|
||||
&Itimerspec {
|
||||
it_interval: TIMESPEC_ZERO,
|
||||
it_value: next,
|
||||
},
|
||||
)
|
||||
.inspect_err(|e| {
|
||||
log::error!("Failed to set timerfd: {e}");
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
pub(crate) fn device_descriptor(&self) -> DeviceDescriptor {
|
||||
DeviceDescriptor::new(&self.descriptors).unwrap()
|
||||
}
|
||||
|
|
@ -212,75 +322,28 @@ impl LinuxDevice {
|
|||
})
|
||||
}
|
||||
|
||||
/// SAFETY: `data` must be valid for `len` bytes to read or write, depending on `Direction`
|
||||
unsafe fn control_blocking(
|
||||
pub fn control_in(
|
||||
&self,
|
||||
direction: Direction,
|
||||
control: Control,
|
||||
data: *mut u8,
|
||||
len: usize,
|
||||
data: ControlIn,
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
let r = usbfs::control(
|
||||
&self.fd,
|
||||
usbfs::CtrlTransfer {
|
||||
bRequestType: control.request_type(direction),
|
||||
bRequest: control.request,
|
||||
wValue: control.value,
|
||||
wIndex: control.index,
|
||||
wLength: len.try_into().expect("length must fit in u16"),
|
||||
timeout: timeout
|
||||
.as_millis()
|
||||
.try_into()
|
||||
.expect("timeout must fit in u32 ms"),
|
||||
data: data as *mut c_void,
|
||||
},
|
||||
);
|
||||
|
||||
r.map_err(errno_to_transfer_error)
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
|
||||
let t = TransferData::new_control_in(data);
|
||||
TransferFuture::new(t, |t| self.submit_timeout(t, timeout)).map(|t| {
|
||||
t.status()?;
|
||||
Ok(t.control_in_data().to_owned())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn control_in_blocking(
|
||||
pub fn control_out(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &mut [u8],
|
||||
data: ControlOut,
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
unsafe {
|
||||
self.control_blocking(
|
||||
Direction::In,
|
||||
control,
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
timeout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn control_out_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
unsafe {
|
||||
self.control_blocking(
|
||||
Direction::Out,
|
||||
control,
|
||||
data.as_ptr() as *mut u8,
|
||||
data.len(),
|
||||
timeout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_control_transfer(self: &Arc<Self>) -> TransferHandle<super::TransferData> {
|
||||
TransferHandle::new(super::TransferData::new(
|
||||
self.clone(),
|
||||
None,
|
||||
0,
|
||||
TransferType::Control,
|
||||
))
|
||||
) -> impl MaybeFuture<Output = Result<(), TransferError>> {
|
||||
let t = TransferData::new_control_out(data);
|
||||
TransferFuture::new(t, |t| self.submit_timeout(t, timeout)).map(|t| {
|
||||
t.status()?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn claim_interface(
|
||||
|
|
@ -342,27 +405,59 @@ impl LinuxDevice {
|
|||
usbfs::attach_kernel_driver(&self.fd, interface_number).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn submit_urb(&self, urb: *mut Urb) {
|
||||
let ep = unsafe { (*urb).endpoint };
|
||||
if let Err(e) = usbfs::submit_urb(&self.fd, urb) {
|
||||
// SAFETY: Transfer was not submitted. We still own the transfer
|
||||
// and can write to the URB and complete it in place of the handler.
|
||||
unsafe {
|
||||
let user_data = {
|
||||
let u = &mut *urb;
|
||||
debug!("Failed to submit URB {urb:?} on ep {ep:x}: {e} {u:?}");
|
||||
u.actual_length = 0;
|
||||
u.status = e.raw_os_error();
|
||||
u.usercontext
|
||||
};
|
||||
notify_completion::<super::TransferData>(user_data)
|
||||
pub(crate) fn submit(&self, transfer: Idle<TransferData>) -> Pending<TransferData> {
|
||||
let pending = transfer.pre_submit();
|
||||
let urb = pending.urb_ptr();
|
||||
|
||||
// SAFETY: We got the urb from `Idle<TransferData>`, which always points to
|
||||
// a valid URB with valid buffers, which is not already pending
|
||||
unsafe {
|
||||
let ep = (*urb).endpoint;
|
||||
(*urb).usercontext = pending.as_ptr().cast();
|
||||
if let Err(e) = usbfs::submit_urb(&self.fd, urb) {
|
||||
// SAFETY: Transfer was not submitted. We still own the transfer
|
||||
// and can write to the URB and complete it in place of the handler.
|
||||
let u = &mut *urb;
|
||||
debug!("Failed to submit URB {urb:?} on ep {ep:x}: {e} {u:?}");
|
||||
u.actual_length = 0;
|
||||
u.status = e.raw_os_error();
|
||||
notify_completion::<super::TransferData>(pending.as_ptr().cast());
|
||||
} else {
|
||||
debug!("Submitted URB {urb:?} on ep {ep:x}");
|
||||
}
|
||||
} else {
|
||||
debug!("Submitted URB {urb:?} on ep {ep:x}");
|
||||
}
|
||||
};
|
||||
|
||||
pending
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn cancel_urb(&self, urb: *mut Urb) {
|
||||
fn submit_timeout(
|
||||
&self,
|
||||
mut transfer: Idle<TransferData>,
|
||||
timeout: Duration,
|
||||
) -> Pending<TransferData> {
|
||||
let urb = transfer.urb_ptr();
|
||||
let now = Instant::now();
|
||||
let deadline = now + timeout;
|
||||
transfer.deadline = Some(deadline);
|
||||
|
||||
// Hold the lock across `submit`, so that it can't complete before we
|
||||
// insert the timeout entry.
|
||||
let mut timeouts = self.timeouts.lock().unwrap();
|
||||
|
||||
let r = self.submit(transfer);
|
||||
|
||||
// This can only be false if submit failed, because we hold the timeouts lock
|
||||
// and would block the completion handler.
|
||||
if !r.is_complete() {
|
||||
timeouts.insert(TimeoutEntry { deadline, urb }, ());
|
||||
self.update_timeouts(timeouts, now);
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
pub(crate) fn cancel(&self, transfer: &mut Pending<TransferData>) {
|
||||
let urb = transfer.urb_ptr();
|
||||
unsafe {
|
||||
if let Err(e) = usbfs::discard_urb(&self.fd, urb) {
|
||||
debug!("Failed to cancel URB {urb:?}: {e}");
|
||||
|
|
@ -374,22 +469,13 @@ impl LinuxDevice {
|
|||
const REQUEST_GET_CONFIGURATION: u8 = 0x08;
|
||||
|
||||
let mut dst = [0u8; 1];
|
||||
|
||||
let control = Control {
|
||||
control_type: ControlType::Standard,
|
||||
recipient: Recipient::Device,
|
||||
request: REQUEST_GET_CONFIGURATION,
|
||||
value: 0,
|
||||
index: 0,
|
||||
};
|
||||
|
||||
let r = usbfs::control(
|
||||
&fd,
|
||||
usbfs::CtrlTransfer {
|
||||
bRequestType: control.request_type(Direction::In),
|
||||
bRequest: control.request,
|
||||
wValue: control.value,
|
||||
wIndex: control.index,
|
||||
bRequestType: request_type(Direction::In, ControlType::Standard, Recipient::Device),
|
||||
bRequest: REQUEST_GET_CONFIGURATION,
|
||||
wValue: 0,
|
||||
wIndex: 0,
|
||||
wLength: dst.len() as u16,
|
||||
timeout: Duration::from_millis(50)
|
||||
.as_millis()
|
||||
|
|
@ -460,6 +546,7 @@ impl Drop for LinuxDevice {
|
|||
fn drop(&mut self) {
|
||||
debug!("Closing device {}", self.events_id);
|
||||
events::unregister_fd(self.fd.as_fd());
|
||||
events::unregister_fd(self.timerfd.as_fd());
|
||||
DEVICES.lock().unwrap().remove(self.events_id);
|
||||
}
|
||||
}
|
||||
|
|
@ -473,39 +560,25 @@ pub(crate) struct LinuxInterface {
|
|||
|
||||
#[derive(Default)]
|
||||
struct InterfaceState {
|
||||
endpoints: EndpointBitSet,
|
||||
alt_setting: u8,
|
||||
}
|
||||
|
||||
impl LinuxInterface {
|
||||
pub(crate) fn make_transfer(
|
||||
self: &Arc<Self>,
|
||||
endpoint: u8,
|
||||
ep_type: TransferType,
|
||||
) -> TransferHandle<super::TransferData> {
|
||||
TransferHandle::new(super::TransferData::new(
|
||||
self.device.clone(),
|
||||
Some(self.clone()),
|
||||
endpoint,
|
||||
ep_type,
|
||||
))
|
||||
pub fn control_in(
|
||||
&self,
|
||||
data: ControlIn,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
|
||||
self.device.control_in(data, timeout)
|
||||
}
|
||||
|
||||
pub fn control_in_blocking(
|
||||
pub fn control_out(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &mut [u8],
|
||||
data: ControlOut,
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.device.control_in_blocking(control, data, timeout)
|
||||
}
|
||||
|
||||
pub fn control_out_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.device.control_out_blocking(control, data, timeout)
|
||||
) -> impl MaybeFuture<Output = Result<(), TransferError>> {
|
||||
self.device.control_out(data, timeout)
|
||||
}
|
||||
|
||||
pub fn get_alt_setting(&self) -> u8 {
|
||||
|
|
@ -518,6 +591,13 @@ impl LinuxInterface {
|
|||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
Blocking::new(move || {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
if !state.endpoints.is_empty() {
|
||||
// TODO: Use ErrorKind::ResourceBusy once compatible with MSRV
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"must drop endpoints before changing alt setting",
|
||||
));
|
||||
}
|
||||
debug!(
|
||||
"Set interface {} alt setting to {alt_setting}",
|
||||
self.interface_number
|
||||
|
|
@ -528,13 +608,31 @@ 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}");
|
||||
Ok(usbfs::clear_halt(&self.device.fd, endpoint)?)
|
||||
pub fn endpoint(
|
||||
self: &Arc<Self>,
|
||||
descriptor: EndpointDescriptor,
|
||||
) -> Result<LinuxEndpoint, ClaimEndpointError> {
|
||||
let address = descriptor.address();
|
||||
let ep_type = descriptor.transfer_type();
|
||||
let max_packet_size = descriptor.max_packet_size();
|
||||
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
||||
if state.endpoints.is_set(address) {
|
||||
return Err(ClaimEndpointError::Busy);
|
||||
}
|
||||
state.endpoints.set(address);
|
||||
|
||||
Ok(LinuxEndpoint {
|
||||
inner: Arc::new(EndpointInner {
|
||||
address,
|
||||
ep_type,
|
||||
interface: self.clone(),
|
||||
notify: Notify::new(),
|
||||
}),
|
||||
max_packet_size,
|
||||
pending: VecDeque::new(),
|
||||
idle_transfer: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -556,3 +654,109 @@ impl Drop for LinuxInterface {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LinuxEndpoint {
|
||||
inner: Arc<EndpointInner>,
|
||||
|
||||
pub(crate) max_packet_size: usize,
|
||||
|
||||
/// A queue of pending transfers, expected to complete in order
|
||||
pending: VecDeque<Pending<super::TransferData>>,
|
||||
|
||||
idle_transfer: Option<Idle<TransferData>>,
|
||||
}
|
||||
|
||||
struct EndpointInner {
|
||||
interface: Arc<LinuxInterface>,
|
||||
address: u8,
|
||||
ep_type: TransferType,
|
||||
notify: Notify,
|
||||
}
|
||||
|
||||
impl LinuxEndpoint {
|
||||
pub(crate) fn endpoint_address(&self) -> u8 {
|
||||
self.inner.address
|
||||
}
|
||||
|
||||
pub(crate) fn pending(&self) -> usize {
|
||||
self.pending.len()
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_all(&mut self) {
|
||||
// Cancel transfers in reverse order to ensure subsequent transfers
|
||||
// can't complete out of order while we're going through them.
|
||||
for transfer in self.pending.iter_mut().rev() {
|
||||
self.inner.interface.device.cancel(transfer);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn submit(&mut self, data: Buffer) {
|
||||
let mut transfer = self.idle_transfer.take().unwrap_or_else(|| {
|
||||
Idle::new(
|
||||
self.inner.clone(),
|
||||
super::TransferData::new(self.inner.address, self.inner.ep_type),
|
||||
)
|
||||
});
|
||||
transfer.set_buffer(data);
|
||||
self.pending
|
||||
.push_back(self.inner.interface.device.submit(transfer));
|
||||
}
|
||||
|
||||
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) {
|
||||
let completion = transfer.take_completion();
|
||||
self.idle_transfer = Some(transfer);
|
||||
Poll::Ready(completion)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wait_next_complete(&mut self, timeout: Duration) -> Option<Completion> {
|
||||
self.inner.notify.wait_timeout(timeout, || {
|
||||
take_completed_from_queue(&mut self.pending).map(|mut transfer| {
|
||||
let completion = transfer.take_completion();
|
||||
self.idle_transfer = Some(transfer);
|
||||
completion
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clear_halt(&self) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
let inner = self.inner.clone();
|
||||
Blocking::new(move || {
|
||||
let endpoint = inner.address;
|
||||
debug!("Clear halt, endpoint {endpoint:02x}");
|
||||
Ok(usbfs::clear_halt(&inner.interface.device.fd, endpoint)?)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn allocate(&self, len: usize) -> Result<Buffer, Errno> {
|
||||
Buffer::mmap(&self.inner.interface.device.fd, len).inspect_err(|e| {
|
||||
warn!(
|
||||
"Failed to allocate zero-copy buffer of length {len} for endpoint {}: {e}",
|
||||
self.inner.address
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LinuxEndpoint {
|
||||
fn drop(&mut self) {
|
||||
self.cancel_all();
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Notify> for EndpointInner {
|
||||
fn as_ref(&self) -> &Notify {
|
||||
&self.notify
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EndpointInner {
|
||||
fn drop(&mut self) {
|
||||
let mut state = self.interface.state.lock().unwrap();
|
||||
state.endpoints.clear(self.address);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,16 +35,19 @@ static EPOLL_FD: OnceCell<OwnedFd> = OnceCell::new();
|
|||
|
||||
pub(crate) enum Tag {
|
||||
Device(usize),
|
||||
DeviceTimer(usize),
|
||||
Waker(usize),
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
const DEVICE: u64 = 1;
|
||||
const DEVICE_TIMER: u64 = 2;
|
||||
const WAKER: u64 = 3;
|
||||
|
||||
fn as_event_data(&self) -> EventData {
|
||||
let (tag, id) = match *self {
|
||||
Tag::Device(id) => (Self::DEVICE, id),
|
||||
Tag::DeviceTimer(id) => (Self::DEVICE_TIMER, id),
|
||||
Tag::Waker(id) => (Self::WAKER, id),
|
||||
};
|
||||
EventData::new_u64((id as u64) << 3 | tag)
|
||||
|
|
@ -53,8 +56,9 @@ impl Tag {
|
|||
fn from_event_data(data: EventData) -> Self {
|
||||
let id = (data.u64() >> 3) as usize;
|
||||
let tag = data.u64() & 0b111;
|
||||
match (tag, id as usize) {
|
||||
match (tag, id) {
|
||||
(Self::DEVICE, id) => Tag::Device(id),
|
||||
(Self::DEVICE_TIMER, id) => Tag::DeviceTimer(id),
|
||||
(Self::WAKER, id) => Tag::Waker(id),
|
||||
_ => panic!("Invalid event data"),
|
||||
}
|
||||
|
|
@ -98,6 +102,7 @@ fn event_loop() {
|
|||
for event in events {
|
||||
match Tag::from_event_data(event.data) {
|
||||
Tag::Device(id) => Device::handle_usb_epoll(id),
|
||||
Tag::DeviceTimer(id) => Device::handle_timer_epoll(id),
|
||||
Tag::Waker(id) => {
|
||||
if let Some(waker) = WAKERS.lock().unwrap().get(id) {
|
||||
waker.wake();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ pub use enumeration::{list_buses, list_devices, SysfsPath};
|
|||
|
||||
mod device;
|
||||
pub(crate) use device::LinuxDevice as Device;
|
||||
pub(crate) use device::LinuxEndpoint as Endpoint;
|
||||
pub(crate) use device::LinuxInterface as Interface;
|
||||
|
||||
mod hotplug;
|
||||
|
|
@ -30,6 +31,6 @@ fn errno_to_transfer_error(e: Errno) -> TransferError {
|
|||
Errno::PROTO | Errno::ILSEQ | Errno::OVERFLOW | Errno::COMM | Errno::TIME => {
|
||||
TransferError::Fault
|
||||
}
|
||||
_ => TransferError::Unknown,
|
||||
_ => TransferError::Unknown(e.raw_os_error()),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
use std::{
|
||||
ffi::c_void,
|
||||
mem::{self, ManuallyDrop},
|
||||
ptr::null_mut,
|
||||
sync::Arc,
|
||||
ptr::{addr_of_mut, null_mut},
|
||||
slice,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use rustix::io::Errno;
|
||||
|
||||
use crate::transfer::{
|
||||
Completion, ControlIn, ControlOut, PlatformSubmit, PlatformTransfer, RequestBuffer,
|
||||
ResponseBuffer, TransferError, TransferType, SETUP_PACKET_SIZE,
|
||||
use crate::{
|
||||
descriptors::TransferType,
|
||||
transfer::{
|
||||
internal::Pending, Allocator, Buffer, Completion, ControlIn, ControlOut, Direction,
|
||||
TransferError, SETUP_PACKET_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
|
@ -24,27 +27,20 @@ use super::{
|
|||
///
|
||||
/// This logically contains a `Vec` with urb.buffer and capacity.
|
||||
/// It also owns the `urb` allocation itself, which is stored out-of-line
|
||||
/// to avoid violating noalias when submitting the transfer while holding
|
||||
/// `&mut TransferData`.
|
||||
/// to enable isochronous transfers to allocate the variable-length
|
||||
/// `iso_packet_desc` array.
|
||||
pub struct TransferData {
|
||||
urb: *mut Urb,
|
||||
capacity: usize,
|
||||
device: Arc<super::Device>,
|
||||
|
||||
/// Not directly used, exists just to keep the interface from being released
|
||||
/// while active.
|
||||
_interface: Option<Arc<super::Interface>>,
|
||||
capacity: u32,
|
||||
allocator: Allocator,
|
||||
pub(crate) deadline: Option<Instant>,
|
||||
}
|
||||
|
||||
unsafe impl Send for TransferData {}
|
||||
unsafe impl Sync for TransferData {}
|
||||
|
||||
impl TransferData {
|
||||
pub(super) fn new(
|
||||
device: Arc<super::Device>,
|
||||
interface: Option<Arc<super::Interface>>,
|
||||
endpoint: u8,
|
||||
ep_type: TransferType,
|
||||
) -> TransferData {
|
||||
pub(super) fn new(endpoint: u8, ep_type: TransferType) -> TransferData {
|
||||
let ep_type = match ep_type {
|
||||
TransferType::Control => USBDEVFS_URB_TYPE_CONTROL,
|
||||
TransferType::Interrupt => USBDEVFS_URB_TYPE_INTERRUPT,
|
||||
|
|
@ -52,13 +48,15 @@ impl TransferData {
|
|||
TransferType::Isochronous => USBDEVFS_URB_TYPE_ISO,
|
||||
};
|
||||
|
||||
let mut empty = ManuallyDrop::new(Vec::new());
|
||||
|
||||
TransferData {
|
||||
urb: Box::into_raw(Box::new(Urb {
|
||||
ep_type,
|
||||
endpoint,
|
||||
status: 0,
|
||||
flags: 0,
|
||||
buffer: null_mut(),
|
||||
buffer: empty.as_mut_ptr(),
|
||||
buffer_length: 0,
|
||||
actual_length: 0,
|
||||
start_frame: 0,
|
||||
|
|
@ -68,155 +66,123 @@ impl TransferData {
|
|||
usercontext: null_mut(),
|
||||
})),
|
||||
capacity: 0,
|
||||
device,
|
||||
_interface: interface,
|
||||
allocator: Allocator::Default,
|
||||
deadline: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn urb_mut(&mut self) -> &mut Urb {
|
||||
// SAFETY: if we have `&mut`, the transfer is not pending
|
||||
pub(super) fn new_control_out(data: ControlOut) -> TransferData {
|
||||
let mut t = TransferData::new(0x00, TransferType::Control);
|
||||
let mut buffer = Buffer::new(SETUP_PACKET_SIZE.checked_add(data.data.len()).unwrap());
|
||||
buffer.extend_from_slice(&data.setup_packet());
|
||||
buffer.extend_from_slice(&data.data);
|
||||
t.set_buffer(buffer);
|
||||
t
|
||||
}
|
||||
|
||||
pub(super) fn new_control_in(data: ControlIn) -> TransferData {
|
||||
let mut t = TransferData::new(0x80, TransferType::Control);
|
||||
let mut buffer = Buffer::new(SETUP_PACKET_SIZE.checked_add(data.length as usize).unwrap());
|
||||
buffer.extend_from_slice(&data.setup_packet());
|
||||
t.set_buffer(buffer);
|
||||
t
|
||||
}
|
||||
|
||||
pub fn set_buffer(&mut self, buf: Buffer) {
|
||||
debug_assert!(self.capacity == 0);
|
||||
let buf = ManuallyDrop::new(buf);
|
||||
self.capacity = buf.capacity;
|
||||
self.urb_mut().buffer = buf.ptr;
|
||||
self.urb_mut().actual_length = 0;
|
||||
self.urb_mut().buffer_length = match Direction::from_address(self.urb().endpoint) {
|
||||
Direction::Out => buf.len as i32,
|
||||
Direction::In => buf.requested_len as i32,
|
||||
};
|
||||
self.allocator = buf.allocator;
|
||||
}
|
||||
|
||||
pub fn take_completion(&mut self) -> Completion {
|
||||
let status = self.status();
|
||||
let requested_len = (&mut *self).urb().buffer_length as u32;
|
||||
let actual_len = self.urb().actual_length as usize;
|
||||
let len = match Direction::from_address((&mut *self).urb().endpoint) {
|
||||
Direction::Out => (&mut *self).urb().buffer_length as u32,
|
||||
Direction::In => (&mut *self).urb().actual_length as u32,
|
||||
};
|
||||
|
||||
let mut empty = ManuallyDrop::new(Vec::new());
|
||||
let ptr = mem::replace(&mut (&mut *self).urb_mut().buffer, empty.as_mut_ptr());
|
||||
let capacity = mem::replace(&mut (&mut *self).capacity, 0);
|
||||
(&mut *self).urb_mut().buffer_length = 0;
|
||||
(&mut *self).urb_mut().actual_length = 0;
|
||||
let allocator = mem::replace(&mut (&mut *self).allocator, Allocator::Default);
|
||||
|
||||
Completion {
|
||||
status,
|
||||
actual_len,
|
||||
buffer: Buffer {
|
||||
ptr,
|
||||
len,
|
||||
requested_len,
|
||||
capacity,
|
||||
allocator,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn urb(&self) -> &Urb {
|
||||
unsafe { &*self.urb }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn urb_mut(&mut self) -> &mut Urb {
|
||||
unsafe { &mut *self.urb }
|
||||
}
|
||||
|
||||
fn fill(&mut self, v: Vec<u8>, len: usize, user_data: *mut c_void) {
|
||||
let mut v = ManuallyDrop::new(v);
|
||||
let urb = self.urb_mut();
|
||||
urb.buffer = v.as_mut_ptr();
|
||||
urb.buffer_length = len.try_into().expect("buffer size should fit in i32");
|
||||
urb.usercontext = user_data;
|
||||
urb.actual_length = 0;
|
||||
self.capacity = v.capacity();
|
||||
#[inline]
|
||||
pub(super) fn urb_ptr(&self) -> *mut Urb {
|
||||
self.urb
|
||||
}
|
||||
|
||||
/// SAFETY: requires that the transfer has completed and `length` bytes are initialized
|
||||
unsafe fn take_buf(&mut self, length: usize) -> Vec<u8> {
|
||||
let urb = self.urb_mut();
|
||||
assert!(!urb.buffer.is_null());
|
||||
let ptr = mem::replace(&mut urb.buffer, null_mut());
|
||||
let capacity = mem::replace(&mut self.capacity, 0);
|
||||
assert!(length <= capacity);
|
||||
Vec::from_raw_parts(ptr, length, capacity)
|
||||
#[inline]
|
||||
pub fn status(&self) -> Result<(), TransferError> {
|
||||
if self.urb().status == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// It's sometimes positive, sometimes negative, but rustix panics if negative.
|
||||
Err(errno_to_transfer_error(Errno::from_raw_os_error(
|
||||
self.urb().status.abs(),
|
||||
)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn control_in_data(&self) -> &[u8] {
|
||||
debug_assert!(self.urb().endpoint == 0x80);
|
||||
let urb = self.urb();
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
urb.buffer.add(SETUP_PACKET_SIZE),
|
||||
urb.actual_length as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Pending<TransferData> {
|
||||
pub fn urb_ptr(&self) -> *mut Urb {
|
||||
// Get urb pointer without dereferencing as `TransferData`, because
|
||||
// it may be mutably aliased.
|
||||
unsafe { *addr_of_mut!((*self.as_ptr()).urb) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TransferData {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.urb_mut().buffer.is_null() {
|
||||
drop(Vec::from_raw_parts(self.urb_mut().buffer, 0, self.capacity));
|
||||
}
|
||||
drop(self.take_completion());
|
||||
drop(Box::from_raw(self.urb));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformTransfer for TransferData {
|
||||
fn cancel(&self) {
|
||||
unsafe {
|
||||
self.device.cancel_urb(self.urb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<Vec<u8>> for TransferData {
|
||||
unsafe fn submit(&mut self, data: Vec<u8>, user_data: *mut c_void) {
|
||||
let ep = self.urb_mut().endpoint;
|
||||
assert!(ep & 0x80 == 0);
|
||||
let len = data.len();
|
||||
self.fill(data, len, user_data);
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
unsafe { self.device.submit_urb(self.urb) }
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<ResponseBuffer> {
|
||||
let status = urb_status(self.urb_mut());
|
||||
let len = self.urb_mut().actual_length as usize;
|
||||
|
||||
// SAFETY: self is completed (precondition)
|
||||
let data = ResponseBuffer::from_vec(self.take_buf(0), len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<RequestBuffer> for TransferData {
|
||||
unsafe fn submit(&mut self, data: RequestBuffer, user_data: *mut c_void) {
|
||||
let ep = self.urb_mut().endpoint;
|
||||
let ty = self.urb_mut().ep_type;
|
||||
assert!(ep & 0x80 == 0x80);
|
||||
assert!(ty == USBDEVFS_URB_TYPE_BULK || ty == USBDEVFS_URB_TYPE_INTERRUPT);
|
||||
|
||||
let (data, len) = data.into_vec();
|
||||
self.fill(data, len, user_data);
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
unsafe { self.device.submit_urb(self.urb) }
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<Vec<u8>> {
|
||||
let status = urb_status(self.urb_mut());
|
||||
let len = self.urb_mut().actual_length as usize;
|
||||
|
||||
// SAFETY: self is completed (precondition) and `actual_length` bytes were initialized.
|
||||
let data = unsafe { self.take_buf(len) };
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<ControlIn> for TransferData {
|
||||
unsafe fn submit(&mut self, data: ControlIn, user_data: *mut c_void) {
|
||||
let buf_len = SETUP_PACKET_SIZE + data.length as usize;
|
||||
let mut buf = Vec::with_capacity(buf_len);
|
||||
buf.extend_from_slice(&data.setup_packet());
|
||||
self.fill(buf, buf_len, user_data);
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
unsafe { self.device.submit_urb(self.urb) }
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<Vec<u8>> {
|
||||
let status = urb_status(self.urb_mut());
|
||||
let len = self.urb_mut().actual_length as usize;
|
||||
|
||||
// SAFETY: transfer is completed (precondition) and `actual_length`
|
||||
// bytes were initialized with setup buf in front
|
||||
let mut data = unsafe { self.take_buf(SETUP_PACKET_SIZE + len) };
|
||||
data.splice(0..SETUP_PACKET_SIZE, []);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<ControlOut<'_>> for TransferData {
|
||||
unsafe fn submit(&mut self, data: ControlOut, user_data: *mut c_void) {
|
||||
let buf_len = SETUP_PACKET_SIZE + data.data.len();
|
||||
let mut buf = Vec::with_capacity(buf_len);
|
||||
buf.extend_from_slice(
|
||||
&data
|
||||
.setup_packet()
|
||||
.expect("data length should fit in setup packet's u16"),
|
||||
);
|
||||
buf.extend_from_slice(data.data);
|
||||
self.fill(buf, buf_len, user_data);
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
unsafe { self.device.submit_urb(self.urb) }
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<ResponseBuffer> {
|
||||
let status = urb_status(self.urb_mut());
|
||||
let len = self.urb_mut().actual_length as usize;
|
||||
let data = ResponseBuffer::from_vec(self.take_buf(0), len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
fn urb_status(urb: &Urb) -> Result<(), TransferError> {
|
||||
if urb.status == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// It's sometimes positive, sometimes negative, but rustix panics if negative.
|
||||
Err(errno_to_transfer_error(Errno::from_raw_os_error(
|
||||
urb.status.abs(),
|
||||
)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,30 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
collections::VecDeque,
|
||||
ffi::c_void,
|
||||
io::ErrorKind,
|
||||
mem::ManuallyDrop,
|
||||
sync::{
|
||||
atomic::{AtomicU8, AtomicUsize, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use io_kit_sys::ret::{kIOReturnSuccess, IOReturn};
|
||||
use log::{debug, error};
|
||||
|
||||
use crate::{
|
||||
descriptors::{ConfigurationDescriptor, DeviceDescriptor},
|
||||
bitset::EndpointBitSet,
|
||||
descriptors::{ConfigurationDescriptor, DeviceDescriptor, EndpointDescriptor},
|
||||
device::ClaimEndpointError,
|
||||
maybe_future::blocking::Blocking,
|
||||
transfer::{Control, Direction, TransferError, TransferHandle, TransferType},
|
||||
transfer::{
|
||||
internal::{
|
||||
notify_completion, take_completed_from_queue, Idle, Notify, Pending, TransferFuture,
|
||||
},
|
||||
Buffer, Completion, ControlIn, ControlOut, Direction, TransferError,
|
||||
},
|
||||
DeviceInfo, Error, MaybeFuture, Speed,
|
||||
};
|
||||
|
||||
|
|
@ -23,8 +33,8 @@ use super::{
|
|||
events::{add_event_source, EventRegistration},
|
||||
iokit::{call_iokit_function, check_iokit_return},
|
||||
iokit_c::IOUSBDevRequestTO,
|
||||
iokit_usb::{EndpointInfo, IoKitDevice, IoKitInterface},
|
||||
status_to_transfer_result,
|
||||
iokit_usb::{IoKitDevice, IoKitInterface},
|
||||
TransferData,
|
||||
};
|
||||
|
||||
pub(crate) struct MacDevice {
|
||||
|
|
@ -168,71 +178,6 @@ impl MacDevice {
|
|||
})
|
||||
}
|
||||
|
||||
/// SAFETY: `data` must be valid for `len` bytes to read or write, depending on `Direction`
|
||||
unsafe fn control_blocking(
|
||||
&self,
|
||||
direction: Direction,
|
||||
control: Control,
|
||||
data: *mut u8,
|
||||
len: usize,
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
let timeout_ms = timeout.as_millis().min(u32::MAX as u128) as u32;
|
||||
let mut req = IOUSBDevRequestTO {
|
||||
bmRequestType: control.request_type(direction),
|
||||
bRequest: control.request,
|
||||
wValue: control.value,
|
||||
wIndex: control.index,
|
||||
wLength: len.try_into().expect("length must fit in u16"),
|
||||
pData: data.cast::<c_void>(),
|
||||
wLenDone: 0,
|
||||
noDataTimeout: timeout_ms,
|
||||
completionTimeout: timeout_ms,
|
||||
};
|
||||
|
||||
let r = unsafe { call_iokit_function!(self.device.raw, DeviceRequestTO(&mut req)) };
|
||||
|
||||
status_to_transfer_result(r).map(|()| req.wLenDone as usize)
|
||||
}
|
||||
|
||||
pub fn control_in_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &mut [u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
unsafe {
|
||||
self.control_blocking(
|
||||
Direction::In,
|
||||
control,
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
timeout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn control_out_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
unsafe {
|
||||
self.control_blocking(
|
||||
Direction::Out,
|
||||
control,
|
||||
data.as_ptr() as *mut u8,
|
||||
data.len(),
|
||||
timeout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_control_transfer(self: &Arc<Self>) -> TransferHandle<super::TransferData> {
|
||||
TransferHandle::new(super::TransferData::new_control(self.clone()))
|
||||
}
|
||||
|
||||
pub(crate) fn claim_interface(
|
||||
self: Arc<Self>,
|
||||
interface_number: u8,
|
||||
|
|
@ -257,17 +202,12 @@ impl MacDevice {
|
|||
let _event_registration = add_event_source(interface.create_async_event_source()?);
|
||||
|
||||
interface.open()?;
|
||||
|
||||
let endpoints = interface.endpoints()?;
|
||||
debug!("Found endpoints: {endpoints:?}");
|
||||
|
||||
self.claimed_interfaces.fetch_add(1, Ordering::Acquire);
|
||||
|
||||
Ok(Arc::new(MacInterface {
|
||||
device: self.clone(),
|
||||
interface_number,
|
||||
interface,
|
||||
endpoints: Mutex::new(endpoints),
|
||||
state: Mutex::new(InterfaceState::default()),
|
||||
_event_registration,
|
||||
}))
|
||||
|
|
@ -280,6 +220,97 @@ impl MacDevice {
|
|||
) -> impl MaybeFuture<Output = Result<Arc<MacInterface>, Error>> {
|
||||
self.claim_interface(interface)
|
||||
}
|
||||
|
||||
pub fn control_in(
|
||||
self: &Arc<Self>,
|
||||
data: ControlIn,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
|
||||
let timeout = timeout.as_millis().try_into().expect("timeout too long");
|
||||
let mut v = ManuallyDrop::new(Vec::with_capacity(data.length as usize));
|
||||
let t = unsafe {
|
||||
TransferData::from_raw(v.as_mut_ptr(), data.length as u32, v.capacity() as u32)
|
||||
};
|
||||
|
||||
let req = IOUSBDevRequestTO {
|
||||
bmRequestType: data.request_type(),
|
||||
bRequest: data.request,
|
||||
wValue: data.value,
|
||||
wIndex: data.index,
|
||||
wLength: data.length,
|
||||
pData: t.buf as *mut c_void,
|
||||
wLenDone: 0,
|
||||
completionTimeout: timeout,
|
||||
noDataTimeout: timeout,
|
||||
};
|
||||
|
||||
TransferFuture::new(t, |t| self.submit_control(Direction::In, t, req)).map(|t| {
|
||||
t.status()?;
|
||||
let t = ManuallyDrop::new(t);
|
||||
Ok(unsafe { Vec::from_raw_parts(t.buf, t.actual_len as usize, t.capacity as usize) })
|
||||
})
|
||||
}
|
||||
|
||||
pub fn control_out(
|
||||
self: &Arc<Self>,
|
||||
data: ControlOut,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<(), TransferError>> {
|
||||
let timeout = timeout.as_millis().try_into().expect("timeout too long");
|
||||
let mut v = ManuallyDrop::new(data.data.to_vec());
|
||||
let t =
|
||||
unsafe { TransferData::from_raw(v.as_mut_ptr(), v.len() as u32, v.capacity() as u32) };
|
||||
|
||||
let req = IOUSBDevRequestTO {
|
||||
bmRequestType: data.request_type(),
|
||||
bRequest: data.request,
|
||||
wValue: data.value,
|
||||
wIndex: data.index,
|
||||
wLength: u16::try_from(data.data.len()).expect("request too long"),
|
||||
pData: t.buf as *mut c_void,
|
||||
wLenDone: 0,
|
||||
completionTimeout: timeout,
|
||||
noDataTimeout: timeout,
|
||||
};
|
||||
|
||||
TransferFuture::new(t, |t| self.submit_control(Direction::Out, t, req)).map(|t| {
|
||||
t.status()?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn submit_control(
|
||||
&self,
|
||||
dir: Direction,
|
||||
mut t: Idle<TransferData>,
|
||||
mut req: IOUSBDevRequestTO,
|
||||
) -> Pending<TransferData> {
|
||||
t.actual_len = 0;
|
||||
assert!(req.pData == t.buf.cast());
|
||||
|
||||
let t = t.pre_submit();
|
||||
let ptr = t.as_ptr();
|
||||
|
||||
let res = unsafe {
|
||||
call_iokit_function!(
|
||||
self.device.raw,
|
||||
DeviceRequestAsyncTO(&mut req, transfer_callback, ptr as *mut c_void)
|
||||
)
|
||||
};
|
||||
|
||||
if res == kIOReturnSuccess {
|
||||
debug!("Submitted control {dir:?} {ptr:?}");
|
||||
} else {
|
||||
error!("Failed to submit control {dir:?} {ptr:?}: {res:x}");
|
||||
unsafe {
|
||||
// Complete the transfer in the place of the callback
|
||||
(*ptr).status = res;
|
||||
notify_completion::<super::TransferData>(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MacDevice {
|
||||
|
|
@ -298,71 +329,31 @@ pub(crate) struct MacInterface {
|
|||
_event_registration: EventRegistration,
|
||||
pub(crate) interface: IoKitInterface,
|
||||
pub(crate) device: Arc<MacDevice>,
|
||||
/// Map from address to a structure that contains the `pipe_ref` used by iokit
|
||||
pub(crate) endpoints: Mutex<BTreeMap<u8, EndpointInfo>>,
|
||||
state: Mutex<InterfaceState>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct InterfaceState {
|
||||
alt_setting: u8,
|
||||
endpoints_used: EndpointBitSet,
|
||||
}
|
||||
|
||||
impl MacInterface {
|
||||
pub(crate) fn make_transfer(
|
||||
self: &Arc<Self>,
|
||||
endpoint: u8,
|
||||
ep_type: TransferType,
|
||||
) -> TransferHandle<super::TransferData> {
|
||||
if ep_type == TransferType::Control {
|
||||
assert!(endpoint == 0);
|
||||
TransferHandle::new(super::TransferData::new_control(self.device.clone()))
|
||||
} else {
|
||||
let endpoints = self.endpoints.lock().unwrap();
|
||||
|
||||
// This function can't fail, so if the endpoint is not found, use an invalid
|
||||
// pipe_ref that will fail when submitting the transfer.
|
||||
let pipe_ref = endpoints.get(&endpoint).map(|e| e.pipe_ref).unwrap_or(0);
|
||||
|
||||
TransferHandle::new(super::TransferData::new(
|
||||
self.device.clone(),
|
||||
self.clone(),
|
||||
endpoint,
|
||||
pipe_ref,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn control_in_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &mut [u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.device.control_in_blocking(control, data, timeout)
|
||||
}
|
||||
|
||||
pub fn control_out_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
self.device.control_out_blocking(control, data, timeout)
|
||||
}
|
||||
|
||||
pub fn set_alt_setting(
|
||||
self: Arc<Self>,
|
||||
alt_setting: u8,
|
||||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
Blocking::new(move || {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
debug!(
|
||||
"Set interface {} alt setting to {alt_setting}",
|
||||
self.interface_number
|
||||
);
|
||||
|
||||
let mut endpoints = self.endpoints.lock().unwrap();
|
||||
if !state.endpoints_used.is_empty() {
|
||||
// TODO: Use ErrorKind::ResourceBusy once compatible with MSRV
|
||||
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"must drop endpoints before changing alt setting",
|
||||
));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
check_iokit_return(call_iokit_function!(
|
||||
|
|
@ -371,8 +362,11 @@ impl MacInterface {
|
|||
))?;
|
||||
}
|
||||
|
||||
*endpoints = self.interface.endpoints()?;
|
||||
debug!("Found endpoints: {endpoints:?}");
|
||||
debug!(
|
||||
"Set interface {} alt setting to {alt_setting}",
|
||||
self.interface_number
|
||||
);
|
||||
|
||||
state.alt_setting = alt_setting;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -383,27 +377,51 @@ impl MacInterface {
|
|||
self.state.lock().unwrap().alt_setting
|
||||
}
|
||||
|
||||
pub fn clear_halt(
|
||||
self: Arc<Self>,
|
||||
endpoint: u8,
|
||||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
Blocking::new(move || {
|
||||
debug!("Clear halt, endpoint {endpoint:02x}");
|
||||
pub fn control_in(
|
||||
self: &Arc<Self>,
|
||||
data: ControlIn,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, TransferError>> {
|
||||
self.device.control_in(data, timeout)
|
||||
}
|
||||
|
||||
let pipe_ref = {
|
||||
let endpoints = self.endpoints.lock().unwrap();
|
||||
let ep = endpoints
|
||||
.get(&endpoint)
|
||||
.ok_or_else(|| Error::new(ErrorKind::NotFound, "Endpoint not found"))?;
|
||||
ep.pipe_ref
|
||||
};
|
||||
pub fn control_out(
|
||||
self: &Arc<Self>,
|
||||
data: ControlOut,
|
||||
timeout: Duration,
|
||||
) -> impl MaybeFuture<Output = Result<(), TransferError>> {
|
||||
self.device.control_out(data, timeout)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
check_iokit_return(call_iokit_function!(
|
||||
self.interface.raw,
|
||||
ClearPipeStallBothEnds(pipe_ref)
|
||||
))
|
||||
}
|
||||
pub fn endpoint(
|
||||
self: &Arc<Self>,
|
||||
descriptor: EndpointDescriptor,
|
||||
) -> Result<MacEndpoint, ClaimEndpointError> {
|
||||
let address = descriptor.address();
|
||||
let max_packet_size = descriptor.max_packet_size();
|
||||
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
||||
let Some(pipe_ref) = self.interface.find_pipe_ref(address) else {
|
||||
debug!("Endpoint {address:02X} not found in iokit");
|
||||
return Err(ClaimEndpointError::InvalidAddress);
|
||||
};
|
||||
|
||||
if state.endpoints_used.is_set(address) {
|
||||
return Err(ClaimEndpointError::Busy);
|
||||
}
|
||||
state.endpoints_used.set(address);
|
||||
|
||||
Ok(MacEndpoint {
|
||||
inner: Arc::new(EndpointInner {
|
||||
pipe_ref,
|
||||
address,
|
||||
interface: self.clone(),
|
||||
notify: Notify::new(),
|
||||
}),
|
||||
max_packet_size,
|
||||
pending: VecDeque::new(),
|
||||
idle_transfer: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -418,3 +436,175 @@ impl Drop for MacInterface {
|
|||
.fetch_sub(1, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MacEndpoint {
|
||||
inner: Arc<EndpointInner>,
|
||||
pub(crate) max_packet_size: usize,
|
||||
|
||||
/// A queue of pending transfers, expected to complete in order
|
||||
pending: VecDeque<Pending<TransferData>>,
|
||||
|
||||
idle_transfer: Option<Idle<TransferData>>,
|
||||
}
|
||||
|
||||
struct EndpointInner {
|
||||
interface: Arc<MacInterface>,
|
||||
pipe_ref: u8,
|
||||
address: u8,
|
||||
notify: Notify,
|
||||
}
|
||||
|
||||
impl MacEndpoint {
|
||||
pub(crate) fn endpoint_address(&self) -> u8 {
|
||||
self.inner.address
|
||||
}
|
||||
|
||||
pub(crate) fn pending(&self) -> usize {
|
||||
self.pending.len()
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_all(&mut self) {
|
||||
let r = unsafe {
|
||||
call_iokit_function!(
|
||||
self.inner.interface.interface.raw,
|
||||
AbortPipe(self.inner.pipe_ref)
|
||||
)
|
||||
};
|
||||
debug!(
|
||||
"Cancelled all transfers on endpoint {ep:02x}. status={r:x}",
|
||||
ep = self.inner.address
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn submit(&mut self, buffer: Buffer) {
|
||||
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 {
|
||||
Direction::Out => buffer.len,
|
||||
Direction::In => buffer.requested_len,
|
||||
};
|
||||
transfer.requested_len = req_len;
|
||||
|
||||
let transfer = transfer.pre_submit();
|
||||
let ptr = transfer.as_ptr();
|
||||
|
||||
let res = unsafe {
|
||||
match dir {
|
||||
Direction::Out => call_iokit_function!(
|
||||
self.inner.interface.interface.raw,
|
||||
WritePipeAsync(
|
||||
pipe_ref,
|
||||
buffer.ptr as *mut c_void,
|
||||
buffer.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,
|
||||
transfer_callback,
|
||||
ptr as *mut c_void
|
||||
)
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
if res == kIOReturnSuccess {
|
||||
debug!(
|
||||
"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}");
|
||||
unsafe {
|
||||
// Complete the transfer in the place of the callback
|
||||
(*ptr).status = res;
|
||||
notify_completion::<super::TransferData>(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
self.pending.push_back(transfer);
|
||||
}
|
||||
|
||||
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) {
|
||||
let dir = Direction::from_address(self.inner.address);
|
||||
let completion = unsafe { transfer.take_completion(dir) };
|
||||
self.idle_transfer = Some(transfer);
|
||||
Poll::Ready(completion)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wait_next_complete(&mut self, timeout: Duration) -> Option<Completion> {
|
||||
self.inner.notify.wait_timeout(timeout, || {
|
||||
take_completed_from_queue(&mut self.pending).map(|mut transfer| {
|
||||
let dir = Direction::from_address(self.inner.address);
|
||||
let completion = unsafe { transfer.take_completion(dir) };
|
||||
self.idle_transfer = Some(transfer);
|
||||
completion
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clear_halt(&mut self) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
let inner = self.inner.clone();
|
||||
Blocking::new(move || {
|
||||
debug!("Clear halt, endpoint {:02x}", inner.address);
|
||||
|
||||
unsafe {
|
||||
check_iokit_return(call_iokit_function!(
|
||||
inner.interface.interface.raw,
|
||||
ClearPipeStallBothEnds(inner.pipe_ref)
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MacEndpoint {
|
||||
fn drop(&mut self) {
|
||||
self.cancel_all();
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Notify> for EndpointInner {
|
||||
fn as_ref(&self) -> &Notify {
|
||||
&self.notify
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EndpointInner {
|
||||
fn drop(&mut self) {
|
||||
let mut state = self.interface.state.lock().unwrap();
|
||||
state.endpoints_used.clear(self.address);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn transfer_callback(refcon: *mut c_void, result: IOReturn, len: *mut c_void) {
|
||||
let len = len as u32;
|
||||
let transfer: *mut TransferData = refcon.cast();
|
||||
debug!("Completion for transfer {transfer:?}, status={result:x}, len={len}");
|
||||
|
||||
unsafe {
|
||||
(*transfer).actual_len = len;
|
||||
(*transfer).status = result;
|
||||
notify_completion::<TransferData>(transfer)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
//! Based on Kate Temkin's [usrs](https://github.com/ktemkin/usrs)
|
||||
//! licensed under MIT OR Apache-2.0.
|
||||
|
||||
use std::{collections::BTreeMap, io::ErrorKind, ptr, slice, time::Duration};
|
||||
use std::{io::ErrorKind, ptr, slice, time::Duration};
|
||||
|
||||
use core_foundation::{base::TCFType, runloop::CFRunLoopSource};
|
||||
use core_foundation_sys::runloop::CFRunLoopSourceRef;
|
||||
|
|
@ -191,30 +191,6 @@ impl Drop for IoKitDevice {
|
|||
unsafe impl Send for IoKitDevice {}
|
||||
unsafe impl Sync for IoKitDevice {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct EndpointInfo {
|
||||
pub(crate) pipe_ref: u8,
|
||||
pub(crate) direction: u8,
|
||||
pub(crate) number: u8,
|
||||
pub(crate) transfer_type: u8,
|
||||
pub(crate) max_packet_size: u16,
|
||||
pub(crate) interval: u8,
|
||||
pub(crate) max_burst: u8,
|
||||
pub(crate) mult: u8,
|
||||
pub(crate) bytes_per_interval: u16,
|
||||
}
|
||||
|
||||
impl EndpointInfo {
|
||||
pub(crate) fn address(&self) -> u8 {
|
||||
if self.direction == 0 {
|
||||
self.number
|
||||
} else {
|
||||
self.number | 0x80
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around an IOKit UsbInterface
|
||||
pub(crate) struct IoKitInterface {
|
||||
pub(crate) raw: *mut *mut iokit::UsbInterface,
|
||||
|
|
@ -287,11 +263,10 @@ impl IoKitInterface {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn endpoints(&self) -> Result<BTreeMap<u8, EndpointInfo>, Error> {
|
||||
pub(crate) fn find_pipe_ref(&self, endpoint_addr: u8) -> Option<u8> {
|
||||
unsafe {
|
||||
let mut endpoints = BTreeMap::new();
|
||||
let mut count = 0;
|
||||
check_iokit_return(call_iokit_function!(self.raw, GetNumEndpoints(&mut count)))?;
|
||||
check_iokit_return(call_iokit_function!(self.raw, GetNumEndpoints(&mut count))).ok()?;
|
||||
|
||||
// Pipe references are 1-indexed
|
||||
for pipe_ref in 1..=count {
|
||||
|
|
@ -300,40 +275,26 @@ impl IoKitInterface {
|
|||
let mut transfer_type: u8 = 0;
|
||||
let mut max_packet_size: u16 = 0;
|
||||
let mut interval: u8 = 0;
|
||||
let mut max_burst: u8 = 0;
|
||||
let mut mult: u8 = 0;
|
||||
let mut bytes_per_interval: u16 = 0;
|
||||
|
||||
check_iokit_return(call_iokit_function!(
|
||||
let Ok(()) = check_iokit_return(call_iokit_function!(
|
||||
self.raw,
|
||||
GetPipePropertiesV2(
|
||||
GetPipeProperties(
|
||||
pipe_ref,
|
||||
&mut direction,
|
||||
&mut number,
|
||||
&mut transfer_type,
|
||||
&mut max_packet_size,
|
||||
&mut interval,
|
||||
&mut max_burst,
|
||||
&mut mult,
|
||||
&mut bytes_per_interval
|
||||
&mut interval
|
||||
)
|
||||
))?;
|
||||
|
||||
let endpoint = EndpointInfo {
|
||||
pipe_ref,
|
||||
direction,
|
||||
number,
|
||||
transfer_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
max_burst,
|
||||
mult,
|
||||
bytes_per_interval,
|
||||
)) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
endpoints.insert(endpoint.address(), endpoint);
|
||||
if number | (((direction != 0) as u8) << 7) == endpoint_addr {
|
||||
return Some(pipe_ref);
|
||||
}
|
||||
}
|
||||
Ok(endpoints)
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use crate::transfer::TransferError;
|
||||
|
||||
mod transfer;
|
||||
use io_kit_sys::ret::IOReturn;
|
||||
pub(crate) use transfer::TransferData;
|
||||
|
|
@ -8,13 +10,12 @@ pub use enumeration::{list_buses, list_devices};
|
|||
|
||||
mod device;
|
||||
pub(crate) use device::MacDevice as Device;
|
||||
pub(crate) use device::MacEndpoint as Endpoint;
|
||||
pub(crate) use device::MacInterface as Interface;
|
||||
|
||||
mod hotplug;
|
||||
pub(crate) use hotplug::MacHotplugWatch as HotplugWatch;
|
||||
|
||||
use crate::transfer::TransferError;
|
||||
|
||||
mod iokit;
|
||||
mod iokit_c;
|
||||
mod iokit_usb;
|
||||
|
|
@ -30,6 +31,6 @@ fn status_to_transfer_result(status: IOReturn) -> Result<(), TransferError> {
|
|||
io_kit_sys::ret::kIOReturnNoDevice => Err(TransferError::Disconnected),
|
||||
io_kit_sys::ret::kIOReturnAborted => Err(TransferError::Cancelled),
|
||||
iokit_c::kIOUSBPipeStalled => Err(TransferError::Stall),
|
||||
_ => Err(TransferError::Unknown),
|
||||
_ => Err(TransferError::Unknown(status as i32)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,316 +1,74 @@
|
|||
use std::{
|
||||
ffi::c_void,
|
||||
mem::{self, ManuallyDrop},
|
||||
ptr::null_mut,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
|
||||
use io_kit_sys::ret::{kIOReturnSuccess, IOReturn};
|
||||
use log::{error, info};
|
||||
|
||||
use crate::{
|
||||
platform::macos_iokit::iokit_c::IOUSBDevRequest,
|
||||
transfer::{
|
||||
notify_completion, Completion, ControlIn, ControlOut, PlatformSubmit, PlatformTransfer,
|
||||
RequestBuffer, ResponseBuffer, TransferError,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{iokit::call_iokit_function, status_to_transfer_result};
|
||||
|
||||
extern "C" fn transfer_callback(refcon: *mut c_void, result: IOReturn, len: *mut c_void) {
|
||||
info!(
|
||||
"Completion callback for transfer {refcon:?}, status={result:x}, len={len}",
|
||||
len = len as usize
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let callback_data = {
|
||||
let inner = &mut *(refcon as *mut TransferDataInner);
|
||||
inner.actual_len = len as usize;
|
||||
inner.status = result;
|
||||
inner.callback_data
|
||||
};
|
||||
notify_completion::<super::TransferData>(callback_data)
|
||||
}
|
||||
}
|
||||
use crate::transfer::{Allocator, Buffer, Completion, Direction, TransferError};
|
||||
|
||||
pub struct TransferData {
|
||||
endpoint_addr: u8,
|
||||
pipe_ref: u8,
|
||||
buf: *mut u8,
|
||||
capacity: usize,
|
||||
inner: *mut TransferDataInner,
|
||||
device: Arc<super::Device>,
|
||||
interface: Option<Arc<super::Interface>>,
|
||||
pub(super) buf: *mut u8,
|
||||
pub(super) capacity: u32,
|
||||
pub(super) requested_len: u32,
|
||||
pub(super) actual_len: u32,
|
||||
pub(super) status: IOReturn,
|
||||
}
|
||||
|
||||
impl Drop for TransferData {
|
||||
fn drop(&mut self) {
|
||||
if !self.buf.is_null() {
|
||||
unsafe { drop(Vec::from_raw_parts(self.buf, 0, self.capacity)) }
|
||||
}
|
||||
unsafe { drop(Box::from_raw(self.inner)) }
|
||||
unsafe { drop(Vec::from_raw_parts(self.buf, 0, self.capacity as usize)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Bring the data accessed on the transfer callback out-of-line
|
||||
/// so that we can have a reference to it while the callback may
|
||||
/// write to other fields concurrently. This could be included
|
||||
/// in `TransferData`` with the proposed [`UnsafePinned`](https://github.com/rust-lang/rfcs/pull/3467)
|
||||
pub struct TransferDataInner {
|
||||
actual_len: usize,
|
||||
callback_data: *mut c_void,
|
||||
status: IOReturn,
|
||||
}
|
||||
|
||||
impl TransferData {
|
||||
pub(super) fn new(
|
||||
device: Arc<super::Device>,
|
||||
interface: Arc<super::Interface>,
|
||||
endpoint_addr: u8,
|
||||
pipe_ref: u8,
|
||||
) -> TransferData {
|
||||
pub(super) fn new() -> TransferData {
|
||||
let mut empty = ManuallyDrop::new(Vec::with_capacity(0));
|
||||
unsafe { Self::from_raw(empty.as_mut_ptr(), 0, 0) }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn from_raw(buf: *mut u8, requested_len: u32, capacity: u32) -> TransferData {
|
||||
TransferData {
|
||||
endpoint_addr,
|
||||
pipe_ref,
|
||||
buf: null_mut(),
|
||||
capacity: 0,
|
||||
inner: Box::into_raw(Box::new(TransferDataInner {
|
||||
actual_len: 0,
|
||||
callback_data: null_mut(),
|
||||
status: kIOReturnSuccess,
|
||||
})),
|
||||
device,
|
||||
interface: Some(interface),
|
||||
buf,
|
||||
capacity,
|
||||
requested_len,
|
||||
actual_len: 0,
|
||||
status: kIOReturnSuccess,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new_control(device: Arc<super::Device>) -> TransferData {
|
||||
TransferData {
|
||||
endpoint_addr: 0,
|
||||
pipe_ref: 0,
|
||||
buf: null_mut(),
|
||||
capacity: 0,
|
||||
inner: Box::into_raw(Box::new(TransferDataInner {
|
||||
actual_len: 0,
|
||||
callback_data: null_mut(),
|
||||
status: kIOReturnSuccess,
|
||||
})),
|
||||
device,
|
||||
interface: None,
|
||||
}
|
||||
#[inline]
|
||||
pub fn status(&self) -> Result<(), TransferError> {
|
||||
super::status_to_transfer_result(self.status)
|
||||
}
|
||||
|
||||
/// SAFETY: Requires that the transfer is not active
|
||||
unsafe fn fill(&mut self, buf: Vec<u8>, callback_data: *mut c_void) {
|
||||
let mut buf = ManuallyDrop::new(buf);
|
||||
self.buf = buf.as_mut_ptr();
|
||||
self.capacity = buf.capacity();
|
||||
/// # Safety
|
||||
/// The transfer must have been completed to initialize the buffer. The direction must be correct.
|
||||
pub unsafe fn take_completion(&mut self, direction: Direction) -> Completion {
|
||||
let status = self.status();
|
||||
|
||||
let inner = &mut *self.inner;
|
||||
inner.actual_len = 0;
|
||||
inner.status = kIOReturnSuccess;
|
||||
inner.callback_data = callback_data;
|
||||
}
|
||||
|
||||
/// SAFETY: requires that the transfer has completed and `length` bytes are initialized
|
||||
unsafe fn take_buf(&mut self, length: usize) -> Vec<u8> {
|
||||
assert!(!self.buf.is_null());
|
||||
let ptr = mem::replace(&mut self.buf, null_mut());
|
||||
let mut empty = ManuallyDrop::new(Vec::new());
|
||||
let ptr = mem::replace(&mut self.buf, empty.as_mut_ptr());
|
||||
let capacity = mem::replace(&mut self.capacity, 0);
|
||||
assert!(length <= capacity);
|
||||
Vec::from_raw_parts(ptr, length, capacity)
|
||||
}
|
||||
let len = match direction {
|
||||
Direction::Out => self.requested_len,
|
||||
Direction::In => self.actual_len,
|
||||
};
|
||||
let requested_len = mem::replace(&mut self.requested_len, 0);
|
||||
let actual_len = mem::replace(&mut self.actual_len, 0) as usize;
|
||||
|
||||
/// SAFETY: requires that the transfer is not active, but is fully prepared (as it is when submitting the transfer fails)
|
||||
unsafe fn check_submit_result(&mut self, res: IOReturn) {
|
||||
if res != kIOReturnSuccess {
|
||||
error!(
|
||||
"Failed to submit transfer on endpoint {ep}: {res:x}",
|
||||
ep = self.endpoint_addr
|
||||
);
|
||||
let callback_data = {
|
||||
let inner = &mut *self.inner;
|
||||
inner.status = res;
|
||||
inner.callback_data
|
||||
};
|
||||
let buffer = Buffer {
|
||||
ptr,
|
||||
len,
|
||||
requested_len,
|
||||
capacity,
|
||||
allocator: Allocator::Default,
|
||||
};
|
||||
|
||||
// Complete the transfer in the place of the callback
|
||||
notify_completion::<super::TransferData>(callback_data)
|
||||
Completion {
|
||||
status,
|
||||
actual_len,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
/// SAFETY: requires that the transfer is in a completed state
|
||||
unsafe fn take_status(&mut self) -> (Result<(), TransferError>, usize) {
|
||||
let inner = unsafe { &*self.inner };
|
||||
|
||||
(status_to_transfer_result(inner.status), inner.actual_len)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for TransferData {}
|
||||
|
||||
impl PlatformTransfer for TransferData {
|
||||
fn cancel(&self) {
|
||||
if let Some(intf) = self.interface.as_ref() {
|
||||
let r = unsafe { call_iokit_function!(intf.interface.raw, AbortPipe(self.pipe_ref)) };
|
||||
info!(
|
||||
"Cancelled all transfers on endpoint {ep:02x}. status={r:x}",
|
||||
ep = self.endpoint_addr
|
||||
);
|
||||
} else {
|
||||
assert!(self.pipe_ref == 0);
|
||||
let r =
|
||||
unsafe { call_iokit_function!(self.device.device.raw, USBDeviceAbortPipeZero()) };
|
||||
info!("Cancelled all transfers on control pipe. status={r:x}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<Vec<u8>> for TransferData {
|
||||
unsafe fn submit(&mut self, data: Vec<u8>, callback_data: *mut std::ffi::c_void) {
|
||||
assert!(self.endpoint_addr & 0x80 == 0);
|
||||
let len = data.len();
|
||||
self.fill(data, callback_data);
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
let res = call_iokit_function!(
|
||||
self.interface.as_ref().unwrap().interface.raw,
|
||||
WritePipeAsync(
|
||||
self.pipe_ref,
|
||||
self.buf as *mut c_void,
|
||||
u32::try_from(len).expect("request too large"),
|
||||
transfer_callback,
|
||||
self.inner as *mut c_void
|
||||
)
|
||||
);
|
||||
info!(
|
||||
"Submitted OUT transfer {inner:?} on endpoint {ep:02x}",
|
||||
inner = self.inner,
|
||||
ep = self.endpoint_addr
|
||||
);
|
||||
self.check_submit_result(res);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> crate::transfer::Completion<ResponseBuffer> {
|
||||
let (status, actual_len) = self.take_status();
|
||||
|
||||
// SAFETY: self is completed (precondition) and `actual_length` bytes were initialized.
|
||||
let data = ResponseBuffer::from_vec(unsafe { self.take_buf(0) }, actual_len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<RequestBuffer> for TransferData {
|
||||
unsafe fn submit(&mut self, data: RequestBuffer, callback_data: *mut std::ffi::c_void) {
|
||||
assert!(self.endpoint_addr & 0x80 == 0x80);
|
||||
|
||||
let (data, len) = data.into_vec();
|
||||
self.fill(data, callback_data);
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
let res = call_iokit_function!(
|
||||
self.interface.as_ref().unwrap().interface.raw,
|
||||
ReadPipeAsync(
|
||||
self.pipe_ref,
|
||||
self.buf as *mut c_void,
|
||||
u32::try_from(len).expect("request too large"),
|
||||
transfer_callback,
|
||||
self.inner as *mut c_void
|
||||
)
|
||||
);
|
||||
info!(
|
||||
"Submitted IN transfer {inner:?} on endpoint {ep:02x}",
|
||||
inner = self.inner,
|
||||
ep = self.endpoint_addr
|
||||
);
|
||||
|
||||
self.check_submit_result(res);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> crate::transfer::Completion<Vec<u8>> {
|
||||
let (status, actual_len) = self.take_status();
|
||||
|
||||
// SAFETY: self is completed (precondition) and `actual_length` bytes were initialized.
|
||||
let data = unsafe { self.take_buf(actual_len) };
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<ControlIn> for TransferData {
|
||||
unsafe fn submit(&mut self, data: ControlIn, callback_data: *mut std::ffi::c_void) {
|
||||
assert!(self.pipe_ref == 0);
|
||||
|
||||
let buf = Vec::with_capacity(data.length as usize);
|
||||
self.fill(buf, callback_data);
|
||||
|
||||
let mut req = IOUSBDevRequest {
|
||||
bmRequestType: data.request_type(),
|
||||
bRequest: data.request,
|
||||
wValue: data.value,
|
||||
wIndex: data.index,
|
||||
wLength: data.length,
|
||||
pData: self.buf as *mut c_void,
|
||||
wLenDone: 0,
|
||||
};
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
let res = call_iokit_function!(
|
||||
self.device.device.raw,
|
||||
DeviceRequestAsync(&mut req, transfer_callback, self.inner as *mut c_void)
|
||||
);
|
||||
info!(
|
||||
"Submitted Control IN transfer {inner:?}",
|
||||
inner = self.inner
|
||||
);
|
||||
self.check_submit_result(res);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> crate::transfer::Completion<Vec<u8>> {
|
||||
let (status, actual_len) = self.take_status();
|
||||
|
||||
// SAFETY: self is completed (precondition) and `actual_length` bytes were initialized.
|
||||
let data = unsafe { self.take_buf(actual_len) };
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<ControlOut<'_>> for TransferData {
|
||||
unsafe fn submit(&mut self, data: ControlOut<'_>, callback_data: *mut std::ffi::c_void) {
|
||||
assert!(self.pipe_ref == 0);
|
||||
|
||||
let buf = data.data.to_vec();
|
||||
let len = buf.len();
|
||||
self.fill(buf, callback_data);
|
||||
|
||||
let mut req = IOUSBDevRequest {
|
||||
bmRequestType: data.request_type(),
|
||||
bRequest: data.request,
|
||||
wValue: data.value,
|
||||
wIndex: data.index,
|
||||
wLength: u16::try_from(len).expect("request too long"),
|
||||
pData: self.buf as *mut c_void,
|
||||
wLenDone: 0,
|
||||
};
|
||||
|
||||
// SAFETY: we just properly filled the buffer and it is not already pending
|
||||
let res = call_iokit_function!(
|
||||
self.device.device.raw,
|
||||
DeviceRequestAsync(&mut req, transfer_callback, self.inner as *mut c_void)
|
||||
);
|
||||
info!(
|
||||
"Submitted Control OUT transfer {inner:?}",
|
||||
inner = self.inner
|
||||
);
|
||||
self.check_submit_result(res);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> crate::transfer::Completion<ResponseBuffer> {
|
||||
let (status, actual_len) = self.take_status();
|
||||
|
||||
// SAFETY: self is completed (precondition) and `actual_length` bytes were initialized.
|
||||
let data = ResponseBuffer::from_vec(unsafe { self.take_buf(0) }, actual_len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
unsafe impl Sync for TransferData {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use std::{
|
||||
collections::{btree_map::Entry, BTreeMap},
|
||||
collections::{btree_map::Entry, BTreeMap, VecDeque},
|
||||
ffi::c_void,
|
||||
io::{self, ErrorKind},
|
||||
mem::{size_of_val, transmute},
|
||||
|
|
@ -7,28 +7,37 @@ use std::{
|
|||
io::{AsRawHandle, RawHandle},
|
||||
prelude::OwnedHandle,
|
||||
},
|
||||
ptr,
|
||||
ptr::{self, null_mut},
|
||||
sync::{Arc, Mutex},
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use log::{debug, error, info, warn};
|
||||
use log::{debug, error, warn};
|
||||
use windows_sys::Win32::{
|
||||
Devices::Usb::{
|
||||
WinUsb_ControlTransfer, WinUsb_Free, WinUsb_GetAssociatedInterface, WinUsb_Initialize,
|
||||
WinUsb_ResetPipe, WinUsb_SetCurrentAlternateSetting, WinUsb_SetPipePolicy,
|
||||
PIPE_TRANSFER_TIMEOUT, WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET,
|
||||
self, WinUsb_ControlTransfer, WinUsb_Free, WinUsb_GetAssociatedInterface,
|
||||
WinUsb_Initialize, WinUsb_ReadPipe, WinUsb_ResetPipe, WinUsb_SetCurrentAlternateSetting,
|
||||
WinUsb_SetPipePolicy, WinUsb_WritePipe, WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET,
|
||||
},
|
||||
Foundation::{GetLastError, FALSE, TRUE},
|
||||
Foundation::{GetLastError, ERROR_IO_PENDING, ERROR_NOT_FOUND, FALSE, HANDLE, TRUE},
|
||||
System::IO::{CancelIoEx, OVERLAPPED},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
bitset::EndpointBitSet,
|
||||
descriptors::{
|
||||
ConfigurationDescriptor, DeviceDescriptor, DESCRIPTOR_LEN_DEVICE,
|
||||
ConfigurationDescriptor, DeviceDescriptor, EndpointDescriptor, DESCRIPTOR_LEN_DEVICE,
|
||||
DESCRIPTOR_TYPE_CONFIGURATION,
|
||||
},
|
||||
device::ClaimEndpointError,
|
||||
maybe_future::{blocking::Blocking, Ready},
|
||||
transfer::{Control, Direction, Recipient, TransferError, TransferHandle, TransferType},
|
||||
transfer::{
|
||||
internal::{
|
||||
notify_completion, take_completed_from_queue, Idle, Notify, Pending, TransferFuture,
|
||||
},
|
||||
Buffer, Completion, ControlIn, ControlOut, Direction, Recipient, TransferError,
|
||||
},
|
||||
DeviceInfo, Error, MaybeFuture, Speed,
|
||||
};
|
||||
|
||||
|
|
@ -37,6 +46,7 @@ use super::{
|
|||
find_usbccgp_child, get_driver_name, get_usbccgp_winusb_device_path, get_winusb_device_path,
|
||||
},
|
||||
hub::HubPort,
|
||||
transfer::TransferData,
|
||||
util::{create_file, raw_handle, WCStr},
|
||||
DevInst,
|
||||
};
|
||||
|
|
@ -127,12 +137,18 @@ impl WindowsDevice {
|
|||
}
|
||||
|
||||
pub(crate) fn get_descriptor(
|
||||
&self,
|
||||
self: Arc<Self>,
|
||||
desc_type: u8,
|
||||
desc_index: u8,
|
||||
language_id: u16,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
HubPort::by_child_devinst(self.devinst)?.get_descriptor(desc_type, desc_index, language_id)
|
||||
) -> impl MaybeFuture<Output = Result<Vec<u8>, Error>> {
|
||||
Blocking::new(move || {
|
||||
HubPort::by_child_devinst(self.devinst)?.get_descriptor(
|
||||
desc_type,
|
||||
desc_index,
|
||||
language_id,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn reset(&self) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
|
|
@ -342,6 +358,7 @@ pub(crate) struct WindowsInterface {
|
|||
#[derive(Default)]
|
||||
struct InterfaceState {
|
||||
alt_setting: u8,
|
||||
endpoints: EndpointBitSet,
|
||||
}
|
||||
|
||||
unsafe impl Send for WindowsInterface {}
|
||||
|
|
@ -384,123 +401,61 @@ impl Drop for WindowsInterface {
|
|||
}
|
||||
|
||||
impl WindowsInterface {
|
||||
pub(crate) fn make_transfer(
|
||||
pub fn control_in(
|
||||
self: &Arc<Self>,
|
||||
endpoint: u8,
|
||||
ep_type: TransferType,
|
||||
) -> TransferHandle<super::TransferData> {
|
||||
TransferHandle::new(super::TransferData::new(self.clone(), endpoint, ep_type))
|
||||
}
|
||||
|
||||
/// SAFETY: `data` must be valid for `len` bytes to read or write, depending on `Direction`
|
||||
unsafe fn control_blocking(
|
||||
&self,
|
||||
direction: Direction,
|
||||
control: Control,
|
||||
data: *mut u8,
|
||||
len: usize,
|
||||
data: ControlIn,
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
info!("Blocking control {direction:?}, {len} bytes");
|
||||
|
||||
if control.recipient == Recipient::Interface && control.index as u8 != self.interface_number
|
||||
{
|
||||
) -> 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 timeout_ms = timeout.as_millis().min(u32::MAX as u128) as u32;
|
||||
let r = WinUsb_SetPipePolicy(
|
||||
self.winusb_handle,
|
||||
0,
|
||||
PIPE_TRANSFER_TIMEOUT,
|
||||
size_of_val(&timeout_ms) as u32,
|
||||
&timeout_ms as *const u32 as *const c_void,
|
||||
);
|
||||
|
||||
if r != TRUE {
|
||||
error!(
|
||||
"WinUsb_SetPipePolicy PIPE_TRANSFER_TIMEOUT failed: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
}
|
||||
let mut t = TransferData::new(0x80);
|
||||
t.set_buffer(Buffer::new(data.length as usize));
|
||||
|
||||
let pkt = WINUSB_SETUP_PACKET {
|
||||
RequestType: control.request_type(direction),
|
||||
Request: control.request,
|
||||
Value: control.value,
|
||||
Index: control.index,
|
||||
Length: len.try_into().expect("request size too large"),
|
||||
RequestType: data.request_type(),
|
||||
Request: data.request,
|
||||
Value: data.value,
|
||||
Index: data.index,
|
||||
Length: data.length,
|
||||
};
|
||||
|
||||
let mut actual_len = 0;
|
||||
let intf = self.clone();
|
||||
|
||||
let r = WinUsb_ControlTransfer(
|
||||
self.winusb_handle,
|
||||
pkt,
|
||||
data,
|
||||
len.try_into().expect("request size too large"),
|
||||
&mut actual_len,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
|
||||
if r == TRUE {
|
||||
Ok(actual_len as usize)
|
||||
} else {
|
||||
error!(
|
||||
"WinUsb_ControlTransfer failed: {}",
|
||||
io::Error::last_os_error()
|
||||
);
|
||||
Err(super::transfer::map_error(GetLastError()))
|
||||
}
|
||||
TransferFuture::new(t, |t| self.submit_control(t, pkt)).map(move |mut t| {
|
||||
let c = t.take_completion(&intf);
|
||||
c.status?;
|
||||
Ok(c.buffer.into_vec())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn control_in_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &mut [u8],
|
||||
pub fn control_out(
|
||||
self: &Arc<Self>,
|
||||
data: ControlOut,
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
unsafe {
|
||||
self.control_blocking(
|
||||
Direction::In,
|
||||
control,
|
||||
data.as_mut_ptr(),
|
||||
data.len(),
|
||||
timeout,
|
||||
)
|
||||
) -> 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`");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn control_out_blocking(
|
||||
&self,
|
||||
control: Control,
|
||||
data: &[u8],
|
||||
timeout: Duration,
|
||||
) -> Result<usize, TransferError> {
|
||||
// When passed a pointer to read-only memory (e.g. a constant slice),
|
||||
// WinUSB fails with "Invalid access to memory location. (os error 998)".
|
||||
// I assume the kernel is checking the pointer for write access
|
||||
// regardless of the transfer direction. Copy the data to the stack to ensure
|
||||
// we give it a pointer to writable memory.
|
||||
let mut buf = [0; 4096];
|
||||
let Some(buf) = buf.get_mut(..data.len()) else {
|
||||
error!(
|
||||
"Control transfer length {} exceeds limit of 4096",
|
||||
data.len()
|
||||
);
|
||||
return Err(TransferError::Unknown);
|
||||
let mut t = TransferData::new(0x00);
|
||||
t.set_buffer(Buffer::from(data.data.to_vec()));
|
||||
|
||||
let pkt = WINUSB_SETUP_PACKET {
|
||||
RequestType: data.request_type(),
|
||||
Request: data.request,
|
||||
Value: data.value,
|
||||
Index: data.index,
|
||||
Length: data.data.len().try_into().expect("transfer too large"),
|
||||
};
|
||||
buf.copy_from_slice(data);
|
||||
|
||||
unsafe {
|
||||
self.control_blocking(
|
||||
Direction::Out,
|
||||
control,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len(),
|
||||
timeout,
|
||||
)
|
||||
}
|
||||
let intf = self.clone();
|
||||
|
||||
TransferFuture::new(t, |t| self.submit_control(t, pkt)).map(move |mut t| {
|
||||
let c = t.take_completion(&intf);
|
||||
c.status
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_alt_setting(
|
||||
|
|
@ -509,6 +464,13 @@ impl WindowsInterface {
|
|||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
Blocking::new(move || unsafe {
|
||||
let mut state = self.state.lock().unwrap();
|
||||
if !state.endpoints.is_empty() {
|
||||
// TODO: Use ErrorKind::ResourceBusy once compatible with MSRV
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
"must drop endpoints before changing alt setting",
|
||||
));
|
||||
}
|
||||
let r = WinUsb_SetCurrentAlternateSetting(self.winusb_handle, alt_setting.into());
|
||||
if r == TRUE {
|
||||
debug!(
|
||||
|
|
@ -527,14 +489,224 @@ impl WindowsInterface {
|
|||
self.state.lock().unwrap().alt_setting
|
||||
}
|
||||
|
||||
pub fn clear_halt(
|
||||
self: Arc<Self>,
|
||||
endpoint: u8,
|
||||
) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
pub fn endpoint(
|
||||
self: &Arc<Self>,
|
||||
descriptor: EndpointDescriptor,
|
||||
) -> Result<WindowsEndpoint, ClaimEndpointError> {
|
||||
let address = descriptor.address();
|
||||
let max_packet_size = descriptor.max_packet_size();
|
||||
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
||||
if state.endpoints.is_set(address) {
|
||||
return Err(ClaimEndpointError::Busy);
|
||||
}
|
||||
state.endpoints.set(address);
|
||||
|
||||
if Direction::from_address(address) == Direction::In {
|
||||
unsafe {
|
||||
let enable: u8 = 1;
|
||||
let r = WinUsb_SetPipePolicy(
|
||||
self.winusb_handle,
|
||||
address,
|
||||
Usb::RAW_IO,
|
||||
size_of_val(&enable) as u32,
|
||||
&enable as *const _ as *const c_void,
|
||||
);
|
||||
if r != TRUE {
|
||||
let err = GetLastError();
|
||||
warn!("Failed to enable RAW_IO on endpoint {address:02X}: error {err:x}",);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(WindowsEndpoint {
|
||||
inner: Arc::new(EndpointInner {
|
||||
address,
|
||||
interface: self.clone(),
|
||||
notify: Notify::new(),
|
||||
}),
|
||||
max_packet_size,
|
||||
pending: VecDeque::new(),
|
||||
idle_transfer: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn submit(&self, mut t: Idle<TransferData>) -> Pending<TransferData> {
|
||||
let endpoint = t.endpoint;
|
||||
let dir = Direction::from_address(endpoint);
|
||||
let len = t.request_len;
|
||||
let buf = t.buf;
|
||||
t.overlapped.InternalHigh = 0;
|
||||
|
||||
let t = t.pre_submit();
|
||||
let ptr = t.as_ptr();
|
||||
|
||||
debug!("Submit transfer {ptr:?} on endpoint {endpoint:02X} for {len} bytes {dir:?}");
|
||||
|
||||
let r = unsafe {
|
||||
match dir {
|
||||
Direction::Out => WinUsb_WritePipe(
|
||||
self.winusb_handle,
|
||||
endpoint,
|
||||
buf,
|
||||
len.try_into().expect("transfer size should fit in u32"),
|
||||
null_mut(),
|
||||
ptr as *mut OVERLAPPED,
|
||||
),
|
||||
Direction::In => WinUsb_ReadPipe(
|
||||
self.winusb_handle,
|
||||
endpoint,
|
||||
buf,
|
||||
len.try_into().expect("transfer size should fit in u32"),
|
||||
null_mut(),
|
||||
ptr as *mut OVERLAPPED,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
self.post_submit(r, t)
|
||||
}
|
||||
|
||||
fn submit_control(
|
||||
&self,
|
||||
mut t: Idle<TransferData>,
|
||||
pkt: WINUSB_SETUP_PACKET,
|
||||
) -> Pending<TransferData> {
|
||||
let endpoint = t.endpoint;
|
||||
let dir = Direction::from_address(endpoint);
|
||||
let len = t.request_len;
|
||||
let buf = t.buf;
|
||||
t.overlapped.InternalHigh = 0;
|
||||
|
||||
let t = t.pre_submit();
|
||||
let ptr = t.as_ptr();
|
||||
|
||||
debug!("Submit control {dir:?} transfer {ptr:?} for {len} bytes");
|
||||
|
||||
let r = unsafe {
|
||||
WinUsb_ControlTransfer(
|
||||
self.winusb_handle,
|
||||
pkt,
|
||||
buf,
|
||||
len,
|
||||
null_mut(),
|
||||
ptr as *mut OVERLAPPED,
|
||||
)
|
||||
};
|
||||
|
||||
self.post_submit(r, t)
|
||||
}
|
||||
|
||||
fn post_submit(&self, r: i32, t: Pending<TransferData>) -> Pending<TransferData> {
|
||||
if r == TRUE {
|
||||
error!("Transfer submit completed synchronously")
|
||||
}
|
||||
|
||||
let err = unsafe { GetLastError() };
|
||||
|
||||
if err != ERROR_IO_PENDING {
|
||||
error!("submit failed: {}", io::Error::from_raw_os_error(err as _));
|
||||
|
||||
// Safety: Transfer was not submitted, so we still own it
|
||||
// and must complete it in place of the event thread.
|
||||
unsafe {
|
||||
(&mut *t.as_ptr()).overlapped.Internal = err as _;
|
||||
notify_completion::<TransferData>(t.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
t
|
||||
}
|
||||
|
||||
fn cancel(&self, t: &mut Pending<TransferData>) {
|
||||
debug!("Cancelling transfer {:?}", t.as_ptr());
|
||||
unsafe {
|
||||
let r = CancelIoEx(self.handle as HANDLE, t.as_ptr() as *mut OVERLAPPED);
|
||||
if r == 0 {
|
||||
let err = GetLastError();
|
||||
if err != ERROR_NOT_FOUND {
|
||||
error!(
|
||||
"CancelIoEx failed: {}",
|
||||
io::Error::from_raw_os_error(err as i32)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WindowsEndpoint {
|
||||
inner: Arc<EndpointInner>,
|
||||
|
||||
pub(crate) max_packet_size: usize,
|
||||
|
||||
/// A queue of pending transfers, expected to complete in order
|
||||
pending: VecDeque<Pending<TransferData>>,
|
||||
|
||||
idle_transfer: Option<Idle<TransferData>>,
|
||||
}
|
||||
|
||||
struct EndpointInner {
|
||||
interface: Arc<WindowsInterface>,
|
||||
address: u8,
|
||||
notify: Notify,
|
||||
}
|
||||
|
||||
impl WindowsEndpoint {
|
||||
pub(crate) fn endpoint_address(&self) -> u8 {
|
||||
self.inner.address
|
||||
}
|
||||
|
||||
pub(crate) fn pending(&self) -> usize {
|
||||
self.pending.len()
|
||||
}
|
||||
|
||||
pub(crate) fn cancel_all(&mut self) {
|
||||
// Cancel transfers in reverse order to ensure subsequent transfers
|
||||
// can't complete out of order while we're going through them.
|
||||
for transfer in self.pending.iter_mut().rev() {
|
||||
self.inner.interface.cancel(transfer);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn submit(&mut self, buffer: Buffer) {
|
||||
let mut t = self.idle_transfer.take().unwrap_or_else(|| {
|
||||
Idle::new(self.inner.clone(), TransferData::new(self.inner.address))
|
||||
});
|
||||
t.set_buffer(buffer);
|
||||
let t = self.inner.interface.submit(t);
|
||||
self.pending.push_back(t);
|
||||
}
|
||||
|
||||
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) {
|
||||
let completion = transfer.take_completion(&self.inner.interface);
|
||||
self.idle_transfer = Some(transfer);
|
||||
Poll::Ready(completion)
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wait_next_complete(&mut self, timeout: Duration) -> Option<Completion> {
|
||||
self.inner.notify.wait_timeout(timeout, || {
|
||||
take_completed_from_queue(&mut self.pending).map(|mut transfer| {
|
||||
let completion = transfer.take_completion(&self.inner.interface);
|
||||
self.idle_transfer = Some(transfer);
|
||||
completion
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clear_halt(&mut self) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
let inner = self.inner.clone();
|
||||
Blocking::new(move || {
|
||||
let endpoint = inner.address;
|
||||
debug!("Clear halt, endpoint {endpoint:02x}");
|
||||
unsafe {
|
||||
let r = WinUsb_ResetPipe(self.winusb_handle, endpoint);
|
||||
let r = WinUsb_ResetPipe(inner.interface.winusb_handle, endpoint);
|
||||
if r == TRUE {
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
@ -544,3 +716,22 @@ impl WindowsInterface {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WindowsEndpoint {
|
||||
fn drop(&mut self) {
|
||||
self.cancel_all();
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Notify> for EndpointInner {
|
||||
fn as_ref(&self) -> &Notify {
|
||||
&self.notify
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EndpointInner {
|
||||
fn drop(&mut self) {
|
||||
let mut state = self.interface.state.lock().unwrap();
|
||||
state.endpoints.clear(self.address);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ mod events;
|
|||
|
||||
mod device;
|
||||
pub(crate) use device::WindowsDevice as Device;
|
||||
pub(crate) use device::WindowsEndpoint as Endpoint;
|
||||
pub(crate) use device::WindowsInterface as Interface;
|
||||
|
||||
mod transfer;
|
||||
pub(crate) use transfer::TransferData;
|
||||
|
||||
mod cfgmgr32;
|
||||
mod hub;
|
||||
|
|
|
|||
|
|
@ -1,347 +1,124 @@
|
|||
use std::{
|
||||
ffi::c_void,
|
||||
io,
|
||||
mem::{self, ManuallyDrop},
|
||||
ptr::{addr_of_mut, null_mut},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
|
||||
use log::{debug, error, warn};
|
||||
use log::debug;
|
||||
use windows_sys::Win32::{
|
||||
Devices::Usb::{
|
||||
WinUsb_ControlTransfer, WinUsb_GetOverlappedResult, WinUsb_ReadPipe, WinUsb_WritePipe,
|
||||
WINUSB_SETUP_PACKET,
|
||||
},
|
||||
Foundation::{
|
||||
GetLastError, ERROR_DEVICE_NOT_CONNECTED, ERROR_FILE_NOT_FOUND, ERROR_GEN_FAILURE,
|
||||
ERROR_IO_PENDING, ERROR_NOT_FOUND, ERROR_NO_SUCH_DEVICE, ERROR_OPERATION_ABORTED,
|
||||
ERROR_REQUEST_ABORTED, ERROR_SEM_TIMEOUT, ERROR_TIMEOUT, FALSE, HANDLE, TRUE, WIN32_ERROR,
|
||||
ERROR_NO_SUCH_DEVICE, ERROR_OPERATION_ABORTED, ERROR_REQUEST_ABORTED, ERROR_SEM_TIMEOUT,
|
||||
ERROR_SUCCESS, ERROR_TIMEOUT,
|
||||
},
|
||||
System::IO::{CancelIoEx, OVERLAPPED},
|
||||
System::IO::{GetOverlappedResult, OVERLAPPED},
|
||||
};
|
||||
|
||||
use crate::transfer::{
|
||||
notify_completion, Completion, ControlIn, ControlOut, PlatformSubmit, PlatformTransfer,
|
||||
Recipient, RequestBuffer, ResponseBuffer, TransferError, TransferType,
|
||||
};
|
||||
use crate::transfer::{internal::notify_completion, Buffer, Completion, Direction, TransferError};
|
||||
|
||||
use super::Interface;
|
||||
|
||||
#[repr(C)]
|
||||
pub(crate) struct EventNotify {
|
||||
// first member of repr(C) struct; can cast pointer between types
|
||||
overlapped: OVERLAPPED,
|
||||
ptr: *mut c_void,
|
||||
}
|
||||
|
||||
pub struct TransferData {
|
||||
interface: Arc<super::Interface>,
|
||||
event: *mut EventNotify,
|
||||
buf: *mut u8,
|
||||
capacity: usize,
|
||||
endpoint: u8,
|
||||
ep_type: TransferType,
|
||||
submit_error: Option<WIN32_ERROR>,
|
||||
// first member of repr(C) struct; can cast pointer between types
|
||||
// overlapped.Internal contains the status
|
||||
// overlapped.InternalHigh contains the number of bytes transferred
|
||||
pub(crate) overlapped: OVERLAPPED,
|
||||
pub(crate) buf: *mut u8,
|
||||
pub(crate) capacity: u32,
|
||||
pub(crate) request_len: u32,
|
||||
pub(crate) endpoint: u8,
|
||||
}
|
||||
|
||||
unsafe impl Send for TransferData {}
|
||||
unsafe impl Sync for TransferData {}
|
||||
|
||||
impl TransferData {
|
||||
pub(crate) fn new(
|
||||
interface: std::sync::Arc<super::Interface>,
|
||||
endpoint: u8,
|
||||
ep_type: TransferType,
|
||||
) -> TransferData {
|
||||
pub(crate) fn new(endpoint: u8) -> TransferData {
|
||||
let mut empty = ManuallyDrop::new(Vec::with_capacity(0));
|
||||
|
||||
TransferData {
|
||||
interface,
|
||||
event: Box::into_raw(Box::new(unsafe { mem::zeroed() })),
|
||||
buf: null_mut(),
|
||||
overlapped: unsafe { mem::zeroed() },
|
||||
buf: empty.as_mut_ptr(),
|
||||
capacity: 0,
|
||||
request_len: 0,
|
||||
endpoint,
|
||||
ep_type,
|
||||
submit_error: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// SAFETY: requires that the transfer has completed and `length` bytes are initialized
|
||||
unsafe fn take_buf(&mut self, length: usize) -> Vec<u8> {
|
||||
let v = Vec::from_raw_parts(self.buf, length, self.capacity);
|
||||
self.buf = null_mut();
|
||||
self.capacity = 0;
|
||||
v
|
||||
#[inline]
|
||||
pub fn actual_len(&self) -> usize {
|
||||
self.overlapped.InternalHigh
|
||||
}
|
||||
|
||||
/// SAFETY: user_data must be the callback pointer passed to `submit`
|
||||
unsafe fn post_submit(&mut self, r: i32, func: &str, user_data: *mut c_void) {
|
||||
if r == TRUE {
|
||||
error!("{func} completed synchronously")
|
||||
}
|
||||
|
||||
let err = GetLastError();
|
||||
|
||||
if err != ERROR_IO_PENDING {
|
||||
self.submit_error = Some(err);
|
||||
error!("{func} failed: {}", io::Error::from_raw_os_error(err as _));
|
||||
|
||||
// Safety: Transfer was not submitted, so we still own it
|
||||
// and must complete it in place of the event thread.
|
||||
notify_completion::<TransferData>(user_data);
|
||||
} else {
|
||||
self.submit_error = None;
|
||||
}
|
||||
pub fn set_buffer(&mut self, buf: Buffer) {
|
||||
debug_assert!(self.capacity == 0);
|
||||
let buf = ManuallyDrop::new(buf);
|
||||
self.capacity = buf.capacity;
|
||||
self.buf = buf.ptr;
|
||||
self.overlapped.InternalHigh = 0;
|
||||
self.request_len = match Direction::from_address(self.endpoint) {
|
||||
Direction::Out => buf.len,
|
||||
Direction::In => buf.requested_len,
|
||||
};
|
||||
}
|
||||
|
||||
/// SAFETY: transfer must be completed
|
||||
unsafe fn get_status(&mut self) -> (usize, Result<(), TransferError>) {
|
||||
if let Some(err) = self.submit_error {
|
||||
debug!(
|
||||
"Transfer {:?} on endpoint {:02x} failed on submit: {}",
|
||||
self.event, self.endpoint, err
|
||||
);
|
||||
return (0, Err(map_error(err)));
|
||||
}
|
||||
pub fn take_completion(&mut self, intf: &Interface) -> Completion {
|
||||
let mut actual_len: u32 = 0;
|
||||
|
||||
let mut actual_len = 0;
|
||||
let r = WinUsb_GetOverlappedResult(
|
||||
self.interface.winusb_handle,
|
||||
self.event as *mut OVERLAPPED,
|
||||
&mut actual_len,
|
||||
FALSE,
|
||||
);
|
||||
unsafe { GetOverlappedResult(intf.handle, &self.overlapped, &mut actual_len, 0) };
|
||||
|
||||
let status = if r != 0 {
|
||||
debug!(
|
||||
"Transfer {:?} on endpoint {:02x} complete: {} bytes transferred",
|
||||
self.event, self.endpoint, actual_len
|
||||
);
|
||||
Ok(())
|
||||
} else {
|
||||
let err = GetLastError();
|
||||
debug!(
|
||||
"Transfer {:?} on endpoint {:02x} failed: {}, {} bytes transferred",
|
||||
self.event, self.endpoint, err, actual_len
|
||||
);
|
||||
Err(map_error(err))
|
||||
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)
|
||||
}
|
||||
e => Err(TransferError::Unknown(e as i32)),
|
||||
};
|
||||
|
||||
(actual_len as usize, status)
|
||||
let mut empty = ManuallyDrop::new(Vec::new());
|
||||
let ptr = mem::replace(&mut self.buf, empty.as_mut_ptr());
|
||||
let capacity = mem::replace(&mut self.capacity, 0);
|
||||
let len = match Direction::from_address(self.endpoint) {
|
||||
Direction::Out => self.request_len as u32,
|
||||
Direction::In => actual_len,
|
||||
};
|
||||
let requested_len = mem::replace(&mut self.request_len, 0);
|
||||
self.overlapped.InternalHigh = 0;
|
||||
|
||||
Completion {
|
||||
status,
|
||||
actual_len: actual_len as usize,
|
||||
buffer: Buffer {
|
||||
ptr,
|
||||
len,
|
||||
requested_len,
|
||||
capacity,
|
||||
allocator: crate::transfer::Allocator::Default,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TransferData {
|
||||
fn drop(&mut self) {
|
||||
if !self.buf.is_null() {
|
||||
unsafe { drop(Vec::from_raw_parts(self.buf, 0, self.capacity)) }
|
||||
}
|
||||
unsafe { drop(Box::from_raw(self.event)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformTransfer for TransferData {
|
||||
fn cancel(&self) {
|
||||
debug!("Cancelling transfer {:?}", self.event);
|
||||
unsafe {
|
||||
let r = CancelIoEx(
|
||||
self.interface.handle as HANDLE,
|
||||
self.event as *mut OVERLAPPED,
|
||||
);
|
||||
if r == 0 {
|
||||
let err = GetLastError();
|
||||
if err != ERROR_NOT_FOUND {
|
||||
error!(
|
||||
"CancelIoEx failed: {}",
|
||||
io::Error::from_raw_os_error(err as i32)
|
||||
);
|
||||
}
|
||||
}
|
||||
drop(Vec::from_raw_parts(self.buf, 0, self.capacity as usize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<Vec<u8>> for TransferData {
|
||||
unsafe fn submit(&mut self, data: Vec<u8>, user_data: *mut c_void) {
|
||||
addr_of_mut!((*self.event).ptr).write(user_data);
|
||||
|
||||
let mut data = ManuallyDrop::new(data);
|
||||
self.buf = data.as_mut_ptr();
|
||||
self.capacity = data.capacity();
|
||||
let len = data.len();
|
||||
|
||||
debug!(
|
||||
"Submit transfer {:?} on endpoint {:02X} for {} bytes OUT",
|
||||
self.event, self.endpoint, len
|
||||
);
|
||||
|
||||
let r = WinUsb_WritePipe(
|
||||
self.interface.winusb_handle,
|
||||
self.endpoint,
|
||||
self.buf,
|
||||
len.try_into().expect("transfer size should fit in u32"),
|
||||
null_mut(),
|
||||
self.event as *mut OVERLAPPED,
|
||||
);
|
||||
self.post_submit(r, "WinUsb_WritePipe", user_data);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<ResponseBuffer> {
|
||||
let (actual_len, status) = self.get_status();
|
||||
let data = ResponseBuffer::from_vec(self.take_buf(0), actual_len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<RequestBuffer> for TransferData {
|
||||
unsafe fn submit(&mut self, data: RequestBuffer, user_data: *mut c_void) {
|
||||
addr_of_mut!((*self.event).ptr).write(user_data);
|
||||
|
||||
let (buf, request_len) = data.into_vec();
|
||||
let mut buf = ManuallyDrop::new(buf);
|
||||
self.buf = buf.as_mut_ptr();
|
||||
self.capacity = buf.capacity();
|
||||
|
||||
debug!(
|
||||
"Submit transfer {:?} on endpoint {:02X} for {} bytes IN",
|
||||
self.event, self.endpoint, request_len
|
||||
);
|
||||
|
||||
let r = WinUsb_ReadPipe(
|
||||
self.interface.winusb_handle,
|
||||
self.endpoint,
|
||||
self.buf,
|
||||
request_len
|
||||
.try_into()
|
||||
.expect("transfer size should fit in u32"),
|
||||
null_mut(),
|
||||
self.event as *mut OVERLAPPED,
|
||||
);
|
||||
self.post_submit(r, "WinUsb_ReadPipe", user_data);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<Vec<u8>> {
|
||||
let (actual_len, status) = self.get_status();
|
||||
let data = self.take_buf(actual_len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<ControlIn> for TransferData {
|
||||
unsafe fn submit(&mut self, data: ControlIn, user_data: *mut c_void) {
|
||||
assert_eq!(self.endpoint, 0);
|
||||
assert_eq!(self.ep_type, TransferType::Control);
|
||||
|
||||
if data.recipient == Recipient::Interface
|
||||
&& data.index as u8 != self.interface.interface_number
|
||||
{
|
||||
warn!("WinUSB sends interface number instead of passed `index` when performing a control transfer with `Recipient::Interface`");
|
||||
}
|
||||
|
||||
addr_of_mut!((*self.event).ptr).write(user_data);
|
||||
|
||||
let mut buf = ManuallyDrop::new(Vec::with_capacity(data.length as usize));
|
||||
self.buf = buf.as_mut_ptr();
|
||||
self.capacity = buf.capacity();
|
||||
|
||||
debug!(
|
||||
"Submit transfer {:?} on endpoint {:02X} for {} bytes ControlIN",
|
||||
self.event, self.endpoint, data.length
|
||||
);
|
||||
|
||||
let pkt = WINUSB_SETUP_PACKET {
|
||||
RequestType: data.request_type(),
|
||||
Request: data.request,
|
||||
Value: data.value,
|
||||
Index: data.index,
|
||||
Length: data.length,
|
||||
};
|
||||
|
||||
let r = WinUsb_ControlTransfer(
|
||||
self.interface.winusb_handle,
|
||||
pkt,
|
||||
self.buf,
|
||||
data.length as u32,
|
||||
null_mut(),
|
||||
self.event as *mut OVERLAPPED,
|
||||
);
|
||||
|
||||
self.post_submit(r, "WinUsb_ControlTransfer", user_data);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<Vec<u8>> {
|
||||
let (actual_len, status) = self.get_status();
|
||||
let data = self.take_buf(actual_len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformSubmit<ControlOut<'_>> for TransferData {
|
||||
unsafe fn submit(&mut self, data: ControlOut, user_data: *mut c_void) {
|
||||
assert_eq!(self.endpoint, 0);
|
||||
assert_eq!(self.ep_type, TransferType::Control);
|
||||
|
||||
if data.recipient == Recipient::Interface
|
||||
&& data.index as u8 != self.interface.interface_number
|
||||
{
|
||||
warn!("WinUSB sends interface number instead of passed `index` when performing a control transfer with `Recipient::Interface`");
|
||||
}
|
||||
|
||||
addr_of_mut!((*self.event).ptr).write(user_data);
|
||||
|
||||
let mut buf = ManuallyDrop::new(data.data.to_vec());
|
||||
self.buf = buf.as_mut_ptr();
|
||||
self.capacity = buf.capacity();
|
||||
let len: u16 = buf
|
||||
.len()
|
||||
.try_into()
|
||||
.expect("transfer size should fit in u16");
|
||||
|
||||
debug!(
|
||||
"Submit transfer {:?} on endpoint {:02X} for {} bytes ControlOUT",
|
||||
self.event, self.endpoint, len
|
||||
);
|
||||
|
||||
let pkt = WINUSB_SETUP_PACKET {
|
||||
RequestType: data.request_type(),
|
||||
Request: data.request,
|
||||
Value: data.value,
|
||||
Index: data.index,
|
||||
Length: len as u16,
|
||||
};
|
||||
|
||||
let r = WinUsb_ControlTransfer(
|
||||
self.interface.winusb_handle,
|
||||
pkt,
|
||||
self.buf,
|
||||
len as u32,
|
||||
null_mut(),
|
||||
self.event as *mut OVERLAPPED,
|
||||
);
|
||||
|
||||
self.post_submit(r, "WinUsb_ControlTransfer", user_data);
|
||||
}
|
||||
|
||||
unsafe fn take_completed(&mut self) -> Completion<ResponseBuffer> {
|
||||
let (actual_len, status) = self.get_status();
|
||||
let data = ResponseBuffer::from_vec(self.take_buf(0), actual_len);
|
||||
Completion { data, status }
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_event(completion: *mut OVERLAPPED) {
|
||||
let completion = completion as *mut EventNotify;
|
||||
debug!("Handling completion for transfer {completion:?}");
|
||||
unsafe {
|
||||
let p = addr_of_mut!((*completion).ptr).read();
|
||||
notify_completion::<TransferData>(p)
|
||||
}
|
||||
}
|
||||
let t = completion as *mut TransferData;
|
||||
{
|
||||
let transfer = unsafe { &mut *t };
|
||||
|
||||
pub(crate) fn map_error(err: WIN32_ERROR) -> TransferError {
|
||||
match err {
|
||||
ERROR_GEN_FAILURE => TransferError::Stall,
|
||||
ERROR_REQUEST_ABORTED | ERROR_TIMEOUT | ERROR_SEM_TIMEOUT | ERROR_OPERATION_ABORTED => {
|
||||
TransferError::Cancelled
|
||||
}
|
||||
ERROR_FILE_NOT_FOUND | ERROR_DEVICE_NOT_CONNECTED | ERROR_NO_SUCH_DEVICE => {
|
||||
TransferError::Disconnected
|
||||
}
|
||||
_ => TransferError::Unknown,
|
||||
debug!(
|
||||
"Transfer {t:?} on endpoint {:02x} complete: status {}, {} bytes",
|
||||
transfer.endpoint,
|
||||
transfer.overlapped.Internal,
|
||||
transfer.actual_len(),
|
||||
);
|
||||
}
|
||||
unsafe { notify_completion::<TransferData>(t) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,128 +1,273 @@
|
|||
use std::fmt::Debug;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
mem::{ManuallyDrop, MaybeUninit},
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use super::TransferRequest;
|
||||
|
||||
/// A buffer for requesting an IN transfer.
|
||||
///
|
||||
/// A `RequestBuffer` is passed when submitting an `IN` transfer to define the
|
||||
/// requested length and provide a buffer to receive data into. The buffer is
|
||||
/// returned in the [`Completion`][`crate::transfer::Completion`] as a `Vec<u8>`
|
||||
/// with the data read from the endpoint. The `Vec`'s allocation can turned back
|
||||
/// into a `RequestBuffer` to re-use it for another transfer.
|
||||
///
|
||||
/// You can think of a `RequestBuffer` as a `Vec` with uninitialized contents.
|
||||
pub struct RequestBuffer {
|
||||
pub(crate) buf: *mut u8,
|
||||
pub(crate) capacity: usize,
|
||||
pub(crate) requested: usize,
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum Allocator {
|
||||
Default,
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
Mmap,
|
||||
}
|
||||
|
||||
impl RequestBuffer {
|
||||
/// Create a `RequestBuffer` of the specified size.
|
||||
pub fn new(len: usize) -> RequestBuffer {
|
||||
let mut v = ManuallyDrop::new(Vec::with_capacity(len));
|
||||
RequestBuffer {
|
||||
buf: v.as_mut_ptr(),
|
||||
capacity: v.capacity(),
|
||||
requested: len,
|
||||
/// Buffer for bulk and interrupt transfers.
|
||||
///
|
||||
/// The fixed-capacity buffer can be backed either by the system allocator or a
|
||||
/// platform-specific way of allocating memory for zero-copy transfers.
|
||||
///
|
||||
/// * For OUT transfers, fill the buffer with data prior to submitting it.
|
||||
/// The `len` is the number of initialized bytes which will be sent when
|
||||
/// submitted. `requested_len` is not used.
|
||||
///
|
||||
/// * For IN transfers, the `requested_len` specifies the number of bytes
|
||||
/// requested from the device. It must be a multiple of the endpoint's maximum
|
||||
/// packet size. The `len` and contents are ignored when the transfer is
|
||||
/// submitted. When the transfer is completed, the `len` is set to the number
|
||||
/// of bytes actually received. The `requested_len` is unmodified, so the same
|
||||
/// buffer can be submitted again to perform another transfer of the same
|
||||
/// length.
|
||||
pub struct Buffer {
|
||||
/// Data pointer
|
||||
pub(crate) ptr: *mut u8,
|
||||
|
||||
/// Initialized bytes
|
||||
pub(crate) len: u32,
|
||||
|
||||
/// Requested length for IN transfer
|
||||
pub(crate) requested_len: u32,
|
||||
|
||||
/// Allocated memory at `ptr`
|
||||
pub(crate) capacity: u32,
|
||||
|
||||
/// Whether the system allocator or a special allocator was used
|
||||
pub(crate) allocator: Allocator,
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
/// Allocate a new bufffer with the default allocator.
|
||||
///
|
||||
/// This buffer will not support [zero-copy
|
||||
/// transfers][`crate::Endpoint::allocate`], but can be cheaply converted to
|
||||
/// a `Vec<u8>`.
|
||||
///
|
||||
/// The passed size will be used as the `requested_len`, and the `capacity`
|
||||
/// will be at least that large.
|
||||
///
|
||||
/// ### Panics
|
||||
/// * If the requested length is greater than `u32::MAX`.
|
||||
#[inline]
|
||||
pub fn new(requested_len: usize) -> Self {
|
||||
let len_u32 = requested_len.try_into().expect("length overflow");
|
||||
let mut vec = ManuallyDrop::new(Vec::with_capacity(requested_len));
|
||||
Buffer {
|
||||
ptr: vec.as_mut_ptr(),
|
||||
len: 0,
|
||||
requested_len: len_u32,
|
||||
capacity: vec.capacity().try_into().expect("capacity overflow"),
|
||||
allocator: Allocator::Default,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_vec(self) -> (Vec<u8>, usize) {
|
||||
let s = ManuallyDrop::new(self);
|
||||
let v = unsafe { Vec::from_raw_parts(s.buf, 0, s.capacity) };
|
||||
(v, s.requested)
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub(crate) fn mmap(
|
||||
fd: &std::os::unix::prelude::OwnedFd,
|
||||
len: usize,
|
||||
) -> Result<Buffer, rustix::io::Errno> {
|
||||
let len_u32 = len.try_into().expect("length overflow");
|
||||
|
||||
let ptr = unsafe {
|
||||
rustix::mm::mmap(
|
||||
std::ptr::null_mut(),
|
||||
len,
|
||||
rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
|
||||
rustix::mm::MapFlags::SHARED,
|
||||
fd,
|
||||
0,
|
||||
)
|
||||
}?;
|
||||
|
||||
Ok(Buffer {
|
||||
ptr: ptr as *mut u8,
|
||||
len: 0,
|
||||
requested_len: len_u32,
|
||||
capacity: len_u32,
|
||||
allocator: Allocator::Mmap,
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a `RequestBuffer` by re-using the allocation of a `Vec`.
|
||||
pub fn reuse(v: Vec<u8>, len: usize) -> RequestBuffer {
|
||||
let mut v = ManuallyDrop::new(v);
|
||||
v.clear();
|
||||
v.reserve_exact(len);
|
||||
RequestBuffer {
|
||||
buf: v.as_mut_ptr(),
|
||||
capacity: v.capacity(),
|
||||
requested: len,
|
||||
/// Get the number of initialized bytes in the buffer.
|
||||
///
|
||||
/// For OUT transfers, this is the amount of data written to the buffer which will be sent when the buffer is submitted.
|
||||
/// For IN transfers, this is the amount of data received from the device. This length is updated when the transfer is returned.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len as usize
|
||||
}
|
||||
|
||||
/// Requested length for IN transfer or actual length for OUT transfer.
|
||||
#[inline]
|
||||
pub fn requested_len(&self) -> usize {
|
||||
self.requested_len as usize
|
||||
}
|
||||
|
||||
/// Set the requested length for an IN transfer.
|
||||
///
|
||||
/// ### Panics
|
||||
/// * If the requested length is greater than the capacity.
|
||||
#[inline]
|
||||
pub fn set_requested_len(&mut self, len: usize) {
|
||||
assert!(len <= self.capacity as usize, "length exceeds capacity");
|
||||
self.requested_len = len.try_into().expect("requested_len overflow");
|
||||
}
|
||||
|
||||
/// Number of allocated bytes.
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity as usize
|
||||
}
|
||||
|
||||
/// Get the number of bytes that can be written to the buffer.
|
||||
///
|
||||
/// This is a convenience method for `capacity() - len()`.
|
||||
#[inline]
|
||||
pub fn remaining_capacity(&self) -> usize {
|
||||
self.capacity() - self.len()
|
||||
}
|
||||
|
||||
/// Clear the buffer.
|
||||
///
|
||||
/// This sets `len` to 0, but does not change the `capacity` or `requested_len`.
|
||||
/// This is useful for reusing the buffer for a new transfer.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
/// Extend the buffer by initializing `len` bytes to `value`, and get a
|
||||
/// mutable slice to the newly initialized bytes.
|
||||
///
|
||||
/// # Panics
|
||||
/// * If the resulting length exceeds the buffer's capacity.
|
||||
pub fn extend_fill(&mut self, len: usize, value: u8) -> &mut [u8] {
|
||||
assert!(len <= self.remaining_capacity(), "length exceeds capacity");
|
||||
unsafe {
|
||||
std::ptr::write_bytes(self.ptr.add(self.len()), value, len);
|
||||
}
|
||||
self.len += len as u32;
|
||||
unsafe { std::slice::from_raw_parts_mut(self.ptr.add(self.len() - len), len) }
|
||||
}
|
||||
|
||||
/// Append a slice of bytes to the buffer.
|
||||
///
|
||||
/// # Panics
|
||||
/// * If the resulting length exceeds the buffer's capacity.
|
||||
pub fn extend_from_slice(&mut self, slice: &[u8]) {
|
||||
assert!(
|
||||
slice.len() <= self.remaining_capacity(),
|
||||
"length exceeds capacity"
|
||||
);
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(slice.as_ptr(), self.ptr.add(self.len()), slice.len());
|
||||
}
|
||||
self.len += slice.len() as u32;
|
||||
}
|
||||
|
||||
/// Returns whether the buffer is specially-allocated for zero-copy IO.
|
||||
pub fn is_zero_copy(&self) -> bool {
|
||||
!matches!(self.allocator, Allocator::Default)
|
||||
}
|
||||
|
||||
/// Convert the buffer into a `Vec<u8>`.
|
||||
///
|
||||
/// This is zero-cost if the buffer was allocated with the default allocator
|
||||
/// (if [`is_zero_copy()`][Self::is_zero_copy] returns false), otherwise it will copy the data
|
||||
/// into a new `Vec<u8>`.
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
match self.allocator {
|
||||
Allocator::Default => {
|
||||
let buf = ManuallyDrop::new(self);
|
||||
unsafe { Vec::from_raw_parts(buf.ptr, buf.len as usize, buf.capacity as usize) }
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => self[..].to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for RequestBuffer {}
|
||||
unsafe impl Sync for RequestBuffer {}
|
||||
unsafe impl Send for Buffer {}
|
||||
unsafe impl Sync for Buffer {}
|
||||
|
||||
impl Drop for RequestBuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe { drop(Vec::from_raw_parts(self.buf, 0, self.capacity)) }
|
||||
/// A `Vec<u8>` can be converted to a `Buffer` cheaply.
|
||||
///
|
||||
/// The Vec's `len` will be used for both the `len` and `requested_len`.
|
||||
impl From<Vec<u8>> for Buffer {
|
||||
fn from(vec: Vec<u8>) -> Self {
|
||||
let mut vec = ManuallyDrop::new(vec);
|
||||
Buffer {
|
||||
ptr: vec.as_mut_ptr(),
|
||||
len: vec.len().try_into().expect("len overflow"),
|
||||
requested_len: vec.len().try_into().expect("len overflow"),
|
||||
capacity: vec.capacity().try_into().expect("capacity overflow"),
|
||||
allocator: Allocator::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RequestBuffer {
|
||||
/// A `Vec<MaybeUninit<u8>>` can be converted to a `Buffer` cheaply.
|
||||
///
|
||||
/// The Vec's `len` will be used for the `requested_len`, and the `len` will be 0.
|
||||
impl From<Vec<MaybeUninit<u8>>> for Buffer {
|
||||
fn from(vec: Vec<MaybeUninit<u8>>) -> Self {
|
||||
let mut vec = ManuallyDrop::new(vec);
|
||||
Buffer {
|
||||
ptr: vec.as_mut_ptr().cast(),
|
||||
len: 0,
|
||||
requested_len: vec.len().try_into().expect("len overflow"),
|
||||
capacity: vec.capacity().try_into().expect("capacity overflow"),
|
||||
allocator: Allocator::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Buffer {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe { std::slice::from_raw_parts(self.ptr, self.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Buffer {
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len as usize) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Buffer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RequestBuffer")
|
||||
.field("requested", &self.requested)
|
||||
.finish_non_exhaustive()
|
||||
f.debug_struct("Buffer")
|
||||
.field("len", &self.len)
|
||||
.field("requested_len", &self.requested_len)
|
||||
.field("data", &format_args!("{:02x?}", &self[..]))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl TransferRequest for RequestBuffer {
|
||||
type Response = Vec<u8>;
|
||||
}
|
||||
|
||||
/// Returned buffer and actual length for a completed OUT transfer.
|
||||
///
|
||||
/// When an `OUT` transfer completes, a `ResponseBuffer` is returned in the
|
||||
/// `Completion`. The [`actual_length`][`ResponseBuffer::actual_length`] tells
|
||||
/// you how many bytes were successfully sent, which may be useful in the case
|
||||
/// of a partially-completed transfer.
|
||||
///
|
||||
/// The `ResponseBuffer` can be turned into an empty `Vec` to re-use the allocation
|
||||
/// for another transfer, or dropped to free the memory.
|
||||
pub struct ResponseBuffer {
|
||||
pub(crate) buf: *mut u8,
|
||||
pub(crate) capacity: usize,
|
||||
pub(crate) transferred: usize,
|
||||
}
|
||||
|
||||
impl ResponseBuffer {
|
||||
pub(crate) fn from_vec(v: Vec<u8>, transferred: usize) -> ResponseBuffer {
|
||||
let mut v = ManuallyDrop::new(v);
|
||||
ResponseBuffer {
|
||||
buf: v.as_mut_ptr(),
|
||||
capacity: v.capacity(),
|
||||
transferred,
|
||||
impl Drop for Buffer {
|
||||
fn drop(&mut self) {
|
||||
match self.allocator {
|
||||
Allocator::Default => unsafe {
|
||||
drop(Vec::from_raw_parts(
|
||||
self.ptr,
|
||||
self.len as usize,
|
||||
self.capacity as usize,
|
||||
));
|
||||
},
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
Allocator::Mmap => unsafe {
|
||||
rustix::mm::munmap(self.ptr as *mut _, self.capacity as usize).unwrap();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of bytes successfully transferred.
|
||||
pub fn actual_length(&self) -> usize {
|
||||
self.transferred
|
||||
}
|
||||
|
||||
/// Extract the buffer as an empty `Vec` to re-use in another transfer.
|
||||
pub fn reuse(self) -> Vec<u8> {
|
||||
let s = ManuallyDrop::new(self);
|
||||
unsafe { Vec::from_raw_parts(s.buf, 0, s.capacity) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ResponseBuffer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ResponseBuffer")
|
||||
.field("transferred", &self.transferred)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for ResponseBuffer {}
|
||||
unsafe impl Sync for ResponseBuffer {}
|
||||
|
||||
impl Drop for ResponseBuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe { drop(Vec::from_raw_parts(self.buf, 0, self.capacity)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl TransferRequest for Vec<u8> {
|
||||
type Response = ResponseBuffer;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use super::{ResponseBuffer, TransferRequest};
|
||||
|
||||
/// Transfer direction
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[repr(u8)]
|
||||
|
|
@ -52,38 +50,8 @@ pub enum Recipient {
|
|||
Other = 3,
|
||||
}
|
||||
|
||||
/// SETUP packet without direction or buffers
|
||||
pub struct Control {
|
||||
/// Request type used for the `bmRequestType` field sent in the SETUP packet.
|
||||
#[doc(alias = "bmRequestType")]
|
||||
pub control_type: ControlType,
|
||||
|
||||
/// Recipient used for the `bmRequestType` field sent in the SETUP packet.
|
||||
#[doc(alias = "bmRequestType")]
|
||||
pub recipient: Recipient,
|
||||
|
||||
/// `bRequest` field sent in the SETUP packet.
|
||||
#[doc(alias = "bRequest")]
|
||||
pub request: u8,
|
||||
|
||||
/// `wValue` field sent in the SETUP packet.
|
||||
#[doc(alias = "wValue")]
|
||||
pub value: u16,
|
||||
|
||||
/// `wIndex` field sent in the SETUP packet.
|
||||
///
|
||||
/// For [`Recipient::Interface`] this is the interface number. For [`Recipient::Endpoint`] this is the endpoint number.
|
||||
#[doc(alias = "wIndex")]
|
||||
pub index: u16,
|
||||
}
|
||||
|
||||
impl Control {
|
||||
pub(crate) fn request_type(&self, direction: Direction) -> u8 {
|
||||
request_type(direction, self.control_type, self.recipient)
|
||||
}
|
||||
}
|
||||
|
||||
/// SETUP packet and associated data to make an **OUT** request on a control endpoint.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ControlOut<'a> {
|
||||
/// Request type used for the `bmRequestType` field sent in the SETUP packet.
|
||||
#[doc(alias = "bmRequestType")]
|
||||
|
|
@ -114,16 +82,16 @@ pub struct ControlOut<'a> {
|
|||
|
||||
impl<'a> ControlOut<'a> {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn setup_packet(&self) -> Result<[u8; SETUP_PACKET_SIZE], ()> {
|
||||
Ok(pack_setup(
|
||||
pub(crate) fn setup_packet(&self) -> [u8; SETUP_PACKET_SIZE] {
|
||||
pack_setup(
|
||||
Direction::Out,
|
||||
self.control_type,
|
||||
self.recipient,
|
||||
self.request,
|
||||
self.value,
|
||||
self.index,
|
||||
self.data.len().try_into().map_err(|_| ())?,
|
||||
))
|
||||
self.data.len().try_into().expect("length must fit in u16"),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
|
|
@ -132,11 +100,8 @@ impl<'a> ControlOut<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl TransferRequest for ControlOut<'_> {
|
||||
type Response = ResponseBuffer;
|
||||
}
|
||||
|
||||
/// SETUP packet to make an **IN** request on a control endpoint.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ControlIn {
|
||||
/// Request type used for the `bmRequestType` field sent in the SETUP packet.
|
||||
#[doc(alias = "bmRequestType")]
|
||||
|
|
@ -210,10 +175,6 @@ fn pack_setup(
|
|||
]
|
||||
}
|
||||
|
||||
impl TransferRequest for ControlIn {
|
||||
type Response = Vec<u8>;
|
||||
}
|
||||
|
||||
pub(crate) fn request_type(
|
||||
direction: Direction,
|
||||
control_type: ControlType,
|
||||
|
|
|
|||
|
|
@ -1,173 +1,206 @@
|
|||
use std::{
|
||||
cell::UnsafeCell,
|
||||
ffi::c_void,
|
||||
ptr::NonNull,
|
||||
collections::VecDeque,
|
||||
future::Future,
|
||||
mem::ManuallyDrop,
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
ptr::{addr_of_mut, NonNull},
|
||||
sync::{
|
||||
atomic::{AtomicU8, Ordering},
|
||||
Arc,
|
||||
Arc, Mutex,
|
||||
},
|
||||
task::{Context, Poll},
|
||||
task::{Context, Poll, Waker},
|
||||
thread::{self, Thread},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use atomic_waker::AtomicWaker;
|
||||
use crate::MaybeFuture;
|
||||
|
||||
use super::Completion;
|
||||
|
||||
pub trait PlatformTransfer: Send {
|
||||
/// Request cancellation of a transfer that may or may not currently be
|
||||
/// pending.
|
||||
fn cancel(&self);
|
||||
pub struct Notify {
|
||||
state: Mutex<NotifyState>,
|
||||
}
|
||||
|
||||
pub trait TransferRequest {
|
||||
type Response;
|
||||
pub enum NotifyState {
|
||||
None,
|
||||
Waker(Waker),
|
||||
Thread(Thread),
|
||||
}
|
||||
|
||||
pub trait PlatformSubmit<D: TransferRequest>: PlatformTransfer {
|
||||
/// Fill the transfer with the data from `data` and submit it to the kernel.
|
||||
/// Arrange for `notify_completion(transfer)` to be called once the transfer
|
||||
/// has completed.
|
||||
///
|
||||
/// SAFETY(caller): transfer is in an idle state
|
||||
unsafe fn submit(&mut self, data: D, transfer: *mut c_void);
|
||||
|
||||
/// SAFETY(caller): `transfer` is in a completed state
|
||||
unsafe fn take_completed(&mut self) -> Completion<D::Response>;
|
||||
impl AsRef<Notify> for Notify {
|
||||
fn as_ref(&self) -> &Notify {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
struct TransferInner<P: PlatformTransfer> {
|
||||
impl Notify {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: Mutex::new(NotifyState::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe(&self, cx: &mut Context) {
|
||||
*self.state.lock().unwrap() = NotifyState::Waker(cx.waker().clone());
|
||||
}
|
||||
|
||||
pub fn wait<T>(&self, mut check: impl FnMut() -> Option<T>) -> T {
|
||||
*self.state.lock().unwrap() = NotifyState::Thread(thread::current());
|
||||
loop {
|
||||
if let Some(result) = check() {
|
||||
return result;
|
||||
}
|
||||
thread::park();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_timeout<T>(
|
||||
&self,
|
||||
timeout: Duration,
|
||||
mut check: impl FnMut() -> Option<T>,
|
||||
) -> Option<T> {
|
||||
*self.state.lock().unwrap() = NotifyState::Thread(thread::current());
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if let Some(result) = check() {
|
||||
return Some(result);
|
||||
}
|
||||
let remaining = timeout.checked_sub(start.elapsed())?;
|
||||
thread::park_timeout(remaining);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notify(&self) {
|
||||
match &mut *self.state.lock().unwrap() {
|
||||
NotifyState::None => {}
|
||||
NotifyState::Waker(waker) => waker.wake_by_ref(),
|
||||
NotifyState::Thread(thread) => thread.unpark(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct TransferInner<P> {
|
||||
/// Platform-specific data.
|
||||
///
|
||||
/// In an `UnsafeCell` because we provide `&mut` when the
|
||||
/// state guarantees us exclusive access
|
||||
platform_data: UnsafeCell<P>,
|
||||
platform_data: P,
|
||||
|
||||
/// One of the `STATE_*` constants below, used to synchronize
|
||||
/// the state.
|
||||
state: AtomicU8,
|
||||
|
||||
/// Waker that is notified when transfer completes.
|
||||
waker: Arc<AtomicWaker>,
|
||||
/// Object notified when transfer completes.
|
||||
notify: Arc<dyn AsRef<Notify> + Send + Sync>,
|
||||
}
|
||||
|
||||
/// Handle to a transfer.
|
||||
///
|
||||
/// Cancels the transfer and arranges for memory to be freed
|
||||
/// when dropped.
|
||||
pub(crate) struct TransferHandle<P: PlatformTransfer> {
|
||||
ptr: NonNull<TransferInner<P>>,
|
||||
}
|
||||
|
||||
unsafe impl<P: PlatformTransfer> Send for TransferHandle<P> {}
|
||||
unsafe impl<P: PlatformTransfer> Sync for TransferHandle<P> {}
|
||||
|
||||
/// The transfer has not been submitted. The buffer is not valid.
|
||||
/// Either the transfer has not yet been submitted, or it has been completed.
|
||||
/// The inner data may be accessed mutably.
|
||||
const STATE_IDLE: u8 = 0;
|
||||
|
||||
/// The transfer has been or is about to be submitted to the kernel and
|
||||
/// completion has not yet been handled. The buffer points to valid memory but
|
||||
/// cannot necessarily be accessed by userspace. There is a future or queue
|
||||
/// waiting for it completion.
|
||||
/// completion has not yet been handled. The buffer cannot necessarily be
|
||||
/// accessed by userspace. There is a future or queue waiting for its completion.
|
||||
const STATE_PENDING: u8 = 1;
|
||||
|
||||
/// Like PENDING, but there is no one waiting for completion. The completion
|
||||
/// handler will drop the buffer and transfer.
|
||||
const STATE_ABANDONED: u8 = 2;
|
||||
|
||||
/// The transfer completion has been handled on the event loop thread. The
|
||||
/// buffer is valid and may be accessed by the `TransferHandle`.
|
||||
const STATE_COMPLETED: u8 = 3;
|
||||
/// Handle to a transfer that is known to be idle.
|
||||
pub(crate) struct Idle<P>(Box<TransferInner<P>>);
|
||||
|
||||
impl<P: PlatformTransfer> TransferHandle<P> {
|
||||
impl<P> Idle<P> {
|
||||
/// Create a new transfer and get a handle.
|
||||
pub(crate) fn new(inner: P) -> TransferHandle<P> {
|
||||
let b = Box::new(TransferInner {
|
||||
platform_data: UnsafeCell::new(inner),
|
||||
pub(crate) fn new(notify: Arc<dyn AsRef<Notify> + Send + Sync>, inner: P) -> Idle<P> {
|
||||
Idle(Box::new(TransferInner {
|
||||
platform_data: inner,
|
||||
state: AtomicU8::new(STATE_IDLE),
|
||||
waker: Arc::new(AtomicWaker::new()),
|
||||
});
|
||||
|
||||
TransferHandle {
|
||||
ptr: Box::leak(b).into(),
|
||||
}
|
||||
notify,
|
||||
}))
|
||||
}
|
||||
|
||||
fn inner(&self) -> &TransferInner<P> {
|
||||
// SAFETY: while `TransferHandle` is alive, its `TransferInner` is alive
|
||||
// (it may be shared by `notify_completion` on the event thread, so can't be &mut)
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
|
||||
fn platform_data(&self) -> &P {
|
||||
// SAFETY: while `TransferHandle` is alive, the only mutable access to `platform_data`
|
||||
// is via this `TransferHandle`.
|
||||
unsafe { &*self.inner().platform_data.get() }
|
||||
}
|
||||
|
||||
pub(crate) fn submit<D>(&mut self, data: D)
|
||||
where
|
||||
D: TransferRequest,
|
||||
P: PlatformSubmit<D>,
|
||||
{
|
||||
let inner = self.inner();
|
||||
|
||||
/// Mark the transfer as pending. The caller must submit the transfer to the kernel
|
||||
/// and arrange for `notify_completion` to be called on the returned value.
|
||||
pub(crate) fn pre_submit(self) -> Pending<P> {
|
||||
// It's the syscall that submits the transfer that actually performs the
|
||||
// release ordering.
|
||||
let prev = self.inner().state.swap(STATE_PENDING, Ordering::Relaxed);
|
||||
let prev = self.0.state.swap(STATE_PENDING, Ordering::Relaxed);
|
||||
assert_eq!(prev, STATE_IDLE, "Transfer should be idle when submitted");
|
||||
|
||||
// SAFETY: while `TransferHandle` is alive, the only mutable access to `platform_data`
|
||||
// is via this `TransferHandle`. Verified that it is idle.
|
||||
unsafe {
|
||||
let p = &mut *inner.platform_data.get();
|
||||
p.submit(data, self.ptr.as_ptr() as *mut c_void);
|
||||
Pending {
|
||||
ptr: unsafe { NonNull::new_unchecked(Box::into_raw(self.0)) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn cancel(&mut self) {
|
||||
self.platform_data().cancel();
|
||||
impl<P> Deref for Idle<P> {
|
||||
type Target = P;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0.platform_data
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> DerefMut for Idle<P> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0.platform_data
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a transfer that may be pending.
|
||||
pub(crate) struct Pending<P> {
|
||||
ptr: NonNull<TransferInner<P>>,
|
||||
}
|
||||
|
||||
unsafe impl<P: Send> Send for Pending<P> {}
|
||||
unsafe impl<P: Sync> Sync for Pending<P> {}
|
||||
|
||||
impl<P> Pending<P> {
|
||||
pub fn as_ptr(&self) -> *mut P {
|
||||
// first member of repr(C) struct
|
||||
self.ptr.as_ptr().cast()
|
||||
}
|
||||
|
||||
fn poll_completion_generic(&mut self, cx: &Context) -> Poll<&mut P> {
|
||||
let inner = self.inner();
|
||||
inner.waker.register(cx.waker());
|
||||
match inner.state.load(Ordering::Acquire) {
|
||||
STATE_PENDING => Poll::Pending,
|
||||
STATE_COMPLETED => {
|
||||
// Relaxed because this doesn't synchronize with anything,
|
||||
// just marks that we no longer need to drop the buffer
|
||||
inner.state.store(STATE_IDLE, Ordering::Relaxed);
|
||||
fn state(&self) -> &AtomicU8 {
|
||||
// Get state without dereferencing as `TransferInner`, because
|
||||
// its `platform_data` may be mutably aliased.
|
||||
unsafe { &*(addr_of_mut!((*self.ptr.as_ptr()).state)) }
|
||||
}
|
||||
|
||||
// SAFETY: while `TransferHandle` is alive, the only mutable access to `platform_data`
|
||||
// is via this `TransferHandle`.
|
||||
Poll::Ready(unsafe { &mut *inner.platform_data.get() })
|
||||
}
|
||||
pub fn is_complete(&self) -> bool {
|
||||
match self.state().load(Ordering::Acquire) {
|
||||
STATE_PENDING => false,
|
||||
STATE_IDLE => true,
|
||||
s => panic!("Polling transfer in unexpected state {s}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_completion<D: TransferRequest>(
|
||||
&mut self,
|
||||
cx: &Context,
|
||||
) -> Poll<Completion<D::Response>>
|
||||
where
|
||||
D: TransferRequest,
|
||||
P: PlatformSubmit<D>,
|
||||
{
|
||||
// SAFETY: `poll_completion_generic` checks that it is completed
|
||||
self.poll_completion_generic(cx)
|
||||
.map(|u| unsafe { u.take_completed() })
|
||||
/// SAFETY: is_complete must have returned `true`
|
||||
pub unsafe fn into_idle(self) -> Idle<P> {
|
||||
debug_assert!(self.is_complete());
|
||||
let transfer = ManuallyDrop::new(self);
|
||||
Idle(Box::from_raw(transfer.ptr.as_ptr()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: PlatformTransfer> Drop for TransferHandle<P> {
|
||||
pub fn take_completed_from_queue<P>(queue: &mut VecDeque<Pending<P>>) -> Option<Idle<P>> {
|
||||
if queue.front().expect("no transfer pending").is_complete() {
|
||||
Some(unsafe { queue.pop_front().unwrap().into_idle() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn take_completed_from_option<P>(option: &mut Option<Pending<P>>) -> Option<Idle<P>> {
|
||||
// TODO: use Option::take_if once supported by MSRV
|
||||
if option.as_mut().map_or(false, |next| next.is_complete()) {
|
||||
option.take().map(|t| unsafe { t.into_idle() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Drop for Pending<P> {
|
||||
fn drop(&mut self) {
|
||||
match self.inner().state.swap(STATE_ABANDONED, Ordering::Acquire) {
|
||||
STATE_PENDING => {
|
||||
self.cancel();
|
||||
/* handler responsible for dropping */
|
||||
}
|
||||
STATE_IDLE | STATE_COMPLETED => {
|
||||
match self.state().swap(STATE_ABANDONED, Ordering::Acquire) {
|
||||
STATE_PENDING => { /* handler responsible for dropping */ }
|
||||
STATE_IDLE => {
|
||||
// SAFETY: state means there is no concurrent access
|
||||
unsafe { drop(Box::from_raw(self.ptr.as_ptr())) }
|
||||
}
|
||||
|
|
@ -180,12 +213,12 @@ impl<P: PlatformTransfer> Drop for TransferHandle<P> {
|
|||
///
|
||||
/// SAFETY: `transfer` must be a pointer previously passed to `submit`, and
|
||||
/// the caller / kernel must no longer dereference it or its buffer.
|
||||
pub(crate) unsafe fn notify_completion<P: PlatformTransfer>(transfer: *mut c_void) {
|
||||
pub(crate) unsafe fn notify_completion<P>(transfer: *mut P) {
|
||||
unsafe {
|
||||
let transfer = transfer as *mut TransferInner<P>;
|
||||
let waker = (*transfer).waker.clone();
|
||||
match (*transfer).state.swap(STATE_COMPLETED, Ordering::Release) {
|
||||
STATE_PENDING => waker.wake(),
|
||||
let notify = (*transfer).notify.clone();
|
||||
match (*transfer).state.swap(STATE_IDLE, Ordering::Release) {
|
||||
STATE_PENDING => (*notify).as_ref().notify(),
|
||||
STATE_ABANDONED => {
|
||||
drop(Box::from_raw(transfer));
|
||||
}
|
||||
|
|
@ -193,3 +226,38 @@ pub(crate) unsafe fn notify_completion<P: PlatformTransfer>(transfer: *mut c_voi
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TransferFuture<D> {
|
||||
transfer: Option<Pending<D>>,
|
||||
notify: Arc<Notify>,
|
||||
}
|
||||
|
||||
impl<D> TransferFuture<D> {
|
||||
pub(crate) fn new(transfer: D, submit: impl FnOnce(Idle<D>) -> Pending<D>) -> Self {
|
||||
let notify = Arc::new(Notify::new());
|
||||
let transfer = submit(Idle::new(notify.clone(), transfer));
|
||||
Self {
|
||||
transfer: Some(transfer),
|
||||
notify,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> Future for TransferFuture<D> {
|
||||
type Output = Idle<D>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
self.notify.subscribe(cx);
|
||||
take_completed_from_option(&mut self.transfer).map_or(Poll::Pending, Poll::Ready)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D> MaybeFuture for TransferFuture<D>
|
||||
where
|
||||
D: Send,
|
||||
{
|
||||
fn wait(mut self) -> Self::Output {
|
||||
self.notify
|
||||
.wait(|| take_completed_from_option(&mut self.transfer))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,62 +1,34 @@
|
|||
//! Transfer-related types.
|
||||
//!
|
||||
//! Use the methods on an [`Interface`][`super::Interface`] to make individual
|
||||
//! transfers or obtain a [`Queue`] to manage multiple transfers.
|
||||
//! Use the methods on an [`Interface`][`super::Interface`] and
|
||||
//! [`Endpoint`][`super::Endpoint`] to perform transfers.
|
||||
|
||||
use std::{
|
||||
fmt::Display,
|
||||
future::Future,
|
||||
io,
|
||||
marker::PhantomData,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::platform;
|
||||
|
||||
mod queue;
|
||||
pub use queue::Queue;
|
||||
|
||||
mod buffer;
|
||||
pub use buffer::{RequestBuffer, ResponseBuffer};
|
||||
use std::{fmt::Display, io};
|
||||
|
||||
mod control;
|
||||
#[allow(unused)]
|
||||
pub(crate) use control::SETUP_PACKET_SIZE;
|
||||
pub use control::{Control, ControlIn, ControlOut, ControlType, Direction, Recipient};
|
||||
pub(crate) use control::{request_type, SETUP_PACKET_SIZE};
|
||||
pub use control::{ControlIn, ControlOut, ControlType, Direction, Recipient};
|
||||
|
||||
mod internal;
|
||||
pub(crate) use internal::{
|
||||
notify_completion, PlatformSubmit, PlatformTransfer, TransferHandle, TransferRequest,
|
||||
};
|
||||
mod buffer;
|
||||
pub(crate) use buffer::Allocator;
|
||||
pub use buffer::Buffer;
|
||||
|
||||
/// Endpoint type.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum TransferType {
|
||||
/// Control endpoint.
|
||||
Control = 0,
|
||||
pub(crate) mod internal;
|
||||
|
||||
/// Isochronous endpoint.
|
||||
Isochronous = 1,
|
||||
|
||||
/// Bulk endpoint.
|
||||
Bulk = 2,
|
||||
|
||||
/// Interrupt endpoint.
|
||||
Interrupt = 3,
|
||||
}
|
||||
use crate::descriptors::TransferType;
|
||||
|
||||
/// Transfer error.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum TransferError {
|
||||
/// Transfer was cancelled.
|
||||
/// Transfer was cancelled or timed out.
|
||||
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
|
||||
/// [`Interface::clear_halt`][crate::Endpoint::clear_halt]. For control
|
||||
/// requests, the stall is automatically cleared when another request is
|
||||
/// submitted.
|
||||
Stall,
|
||||
|
|
@ -68,17 +40,24 @@ pub enum TransferError {
|
|||
Fault,
|
||||
|
||||
/// Unknown or OS-specific error.
|
||||
Unknown,
|
||||
///
|
||||
/// It won't be considered a breaking change to map unhandled errors from
|
||||
/// `Unknown` to one of the above variants. If you are matching on the
|
||||
/// OS-specific code because an error is not correctly mapped, please open
|
||||
/// an issue or pull request.
|
||||
Unknown(i32),
|
||||
}
|
||||
|
||||
impl Display for TransferError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TransferError::Cancelled => write!(f, "transfer was cancelled"),
|
||||
TransferError::Stall => write!(f, "endpoint STALL condition"),
|
||||
TransferError::Stall => write!(f, "endpoint stalled"),
|
||||
TransferError::Disconnected => write!(f, "device disconnected"),
|
||||
TransferError::Fault => write!(f, "hardware fault or protocol violation"),
|
||||
TransferError::Unknown => write!(f, "unknown error"),
|
||||
TransferError::Unknown(e) => {
|
||||
write!(f, "unknown error ({})", io::Error::from_raw_os_error(*e))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -92,87 +71,82 @@ impl From<TransferError> for io::Error {
|
|||
TransferError::Stall => io::Error::new(io::ErrorKind::ConnectionReset, value),
|
||||
TransferError::Disconnected => io::Error::new(io::ErrorKind::ConnectionAborted, value),
|
||||
TransferError::Fault => io::Error::new(io::ErrorKind::Other, value),
|
||||
TransferError::Unknown => io::Error::new(io::ErrorKind::Other, value),
|
||||
TransferError::Unknown(_) => io::Error::new(io::ErrorKind::Other, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Status and data returned on transfer completion.
|
||||
///
|
||||
/// A transfer can return partial data even in the case of failure or
|
||||
/// cancellation, thus this is a struct containing both `data` and `status`
|
||||
/// rather than a `Result`. Use [`into_result`][`Completion::into_result`] to
|
||||
/// ignore a partial transfer and get a `Result`.
|
||||
#[derive(Debug, Clone)]
|
||||
#[must_use]
|
||||
pub struct Completion<T> {
|
||||
/// Returned data or buffer to re-use.
|
||||
pub data: T,
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// Indicates successful completion or error.
|
||||
/// Type-level endpoint direction
|
||||
pub trait EndpointDirection: private::Sealed + Send + Sync {
|
||||
/// Runtime direction value
|
||||
const DIR: Direction;
|
||||
}
|
||||
|
||||
/// Type-level endpoint direction: device-to-host
|
||||
pub enum In {}
|
||||
impl private::Sealed for In {}
|
||||
impl EndpointDirection for In {
|
||||
const DIR: Direction = Direction::In;
|
||||
}
|
||||
|
||||
/// Type-level endpoint direction: host-to-device
|
||||
pub enum Out {}
|
||||
impl private::Sealed for Out {}
|
||||
impl EndpointDirection for Out {
|
||||
const DIR: Direction = Direction::Out;
|
||||
}
|
||||
|
||||
/// Type-level endpoint direction
|
||||
pub trait EndpointType: private::Sealed + Send + Sync {
|
||||
/// Runtime direction value
|
||||
const TYPE: TransferType;
|
||||
}
|
||||
|
||||
/// EndpointType for Bulk and interrupt endpoints.
|
||||
pub trait BulkOrInterrupt: EndpointType {}
|
||||
|
||||
/// Type-level endpoint type: Bulk
|
||||
pub enum Bulk {}
|
||||
impl private::Sealed for Bulk {}
|
||||
impl EndpointType for Bulk {
|
||||
const TYPE: TransferType = TransferType::Bulk;
|
||||
}
|
||||
impl BulkOrInterrupt for Bulk {}
|
||||
|
||||
/// Type-level endpoint type: Interrupt
|
||||
pub enum Interrupt {}
|
||||
impl private::Sealed for Interrupt {}
|
||||
impl EndpointType for Interrupt {
|
||||
const TYPE: TransferType = TransferType::Interrupt;
|
||||
}
|
||||
impl BulkOrInterrupt for Interrupt {}
|
||||
|
||||
/// A completed transfer returned from [`Endpoint::next_complete`][`crate::Endpoint::next_complete`].
|
||||
///
|
||||
/// A transfer can partially complete even in the case of failure or
|
||||
/// cancellation, thus the [`actual_len`][`Self::actual_len`] may be nonzero
|
||||
/// even if the [`status`][`Self::status`] is an error.
|
||||
#[derive(Debug)]
|
||||
pub struct Completion {
|
||||
/// The transfer buffer.
|
||||
pub buffer: Buffer,
|
||||
|
||||
/// The number of bytes transferred.
|
||||
pub actual_len: usize,
|
||||
|
||||
/// Status of the transfer.
|
||||
pub status: Result<(), TransferError>,
|
||||
}
|
||||
|
||||
impl<T> Completion<T> {
|
||||
impl Completion {
|
||||
/// Ignore any partial completion, turning `self` into a `Result` containing
|
||||
/// either the completed buffer for a successful transfer or a
|
||||
/// `TransferError`.
|
||||
pub fn into_result(self) -> Result<T, TransferError> {
|
||||
self.status.map(|()| self.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Completion<Vec<u8>>> for Vec<u8> {
|
||||
type Error = TransferError;
|
||||
|
||||
fn try_from(c: Completion<Vec<u8>>) -> Result<Self, Self::Error> {
|
||||
c.into_result()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Completion<ResponseBuffer>> for ResponseBuffer {
|
||||
type Error = TransferError;
|
||||
|
||||
fn try_from(c: Completion<ResponseBuffer>) -> Result<Self, Self::Error> {
|
||||
c.into_result()
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Future`] used to await the completion of a transfer.
|
||||
///
|
||||
/// Use the methods on [`Interface`][super::Interface] to
|
||||
/// submit an individual transfer and obtain a `TransferFuture`.
|
||||
///
|
||||
/// The transfer is cancelled on drop. The buffer and
|
||||
/// any partially-completed data are destroyed. This means
|
||||
/// that `TransferFuture` is not [cancel-safe] and cannot be used
|
||||
/// in `select!{}`, When racing a `TransferFuture` with a timeout
|
||||
/// you cannot tell whether data may have been partially transferred on timeout.
|
||||
/// Use the [`Queue`] interface if these matter for your application.
|
||||
///
|
||||
/// [cancel-safe]: https://docs.rs/tokio/latest/tokio/macro.select.html#cancellation-safety
|
||||
pub struct TransferFuture<D: TransferRequest> {
|
||||
transfer: TransferHandle<platform::TransferData>,
|
||||
ty: PhantomData<D::Response>,
|
||||
}
|
||||
|
||||
impl<D: TransferRequest> TransferFuture<D> {
|
||||
pub(crate) fn new(transfer: TransferHandle<platform::TransferData>) -> TransferFuture<D> {
|
||||
TransferFuture {
|
||||
transfer,
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: TransferRequest> Future for TransferFuture<D>
|
||||
where
|
||||
platform::TransferData: PlatformSubmit<D>,
|
||||
D::Response: Unpin,
|
||||
{
|
||||
type Output = Completion<D::Response>;
|
||||
|
||||
fn poll(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.as_mut().transfer.poll_completion::<D>(cx)
|
||||
pub fn into_result(self) -> Result<Buffer, TransferError> {
|
||||
self.status.map(|()| self.buffer)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,237 +0,0 @@
|
|||
use std::{
|
||||
collections::VecDeque,
|
||||
future::{poll_fn, Future},
|
||||
marker::PhantomData,
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::{platform, Error, MaybeFuture};
|
||||
|
||||
use super::{Completion, PlatformSubmit, TransferHandle, TransferRequest, TransferType};
|
||||
|
||||
/// Manages a stream of transfers on an endpoint.
|
||||
///
|
||||
/// A `Queue` optimizes a common pattern when streaming data to or from a USB
|
||||
/// endpoint: To maximize throughput and minimize latency, the host controller
|
||||
/// needs to attempt a transfer in every possible frame. That requires always
|
||||
/// having a transfer request pending with the kernel by submitting multiple
|
||||
/// transfer requests and re-submitting them as they complete.
|
||||
///
|
||||
/// Use the methods on [`Interface`][`crate::Interface`] to obtain a `Queue`.
|
||||
///
|
||||
/// When the `Queue` is dropped, all pending transfers are cancelled.
|
||||
///
|
||||
/// ### Why use a `Queue` instead of submitting multiple transfers individually with the methods on [`Interface`][`crate::Interface`]?
|
||||
///
|
||||
/// * Individual transfers give you individual `Future`s, which you then have
|
||||
/// to keep track of and poll using something like `FuturesUnordered`.
|
||||
/// * A `Queue` provides better cancellation semantics than `Future`'s
|
||||
/// cancel-on-drop.
|
||||
/// * After dropping a [`TransferFuture`][super::TransferFuture], you lose
|
||||
/// the ability to get the status of the cancelled transfer and see if it
|
||||
/// may have been partially or fully completed.
|
||||
/// * When cancelling multiple transfers, it's important to do so in reverse
|
||||
/// order so that subsequent pending transfers can't end up executing.
|
||||
/// When managing a collection of `TransferFuture`s it's tricky to
|
||||
/// guarantee drop order, while `Queue` always cancels its contained
|
||||
/// transfers in reverse order.
|
||||
/// * The `TransferFuture` methods on `Interface` are not [cancel-safe],
|
||||
/// meaning they cannot be used in `select!{}` or similar patterns,
|
||||
/// because dropping the Future has side effects and can lose data. The
|
||||
/// Future returned from [`Queue::next_complete`] is cancel-safe because
|
||||
/// it merely waits for completion, while the `Queue` owns the pending
|
||||
/// transfers.
|
||||
/// * A queue caches the internal transfer data structures of the last
|
||||
/// completed transfer, meaning that if you re-use the data buffer there is
|
||||
/// no memory allocation involved in continued streaming.
|
||||
///
|
||||
/// [cancel-safe]: https://docs.rs/tokio/latest/tokio/macro.select.html#cancellation-safety
|
||||
/// ### Example (read from an endpoint)
|
||||
///
|
||||
/// ```no_run
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::RequestBuffer;
|
||||
/// # use nusb::MaybeFuture;
|
||||
/// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
|
||||
/// # let device = di.open().wait().unwrap();
|
||||
/// # let interface = device.claim_interface(0).wait().unwrap();
|
||||
/// # fn handle_data(_: &[u8]) {}
|
||||
/// let mut queue = interface.bulk_in_queue(0x81);
|
||||
///
|
||||
/// let n_transfers = 8;
|
||||
/// let transfer_size = 256;
|
||||
///
|
||||
/// while queue.pending() < n_transfers {
|
||||
/// queue.submit(RequestBuffer::new(transfer_size));
|
||||
/// }
|
||||
///
|
||||
/// loop {
|
||||
/// let completion = block_on(queue.next_complete());
|
||||
/// handle_data(&completion.data); // your function
|
||||
///
|
||||
/// if completion.status.is_err() {
|
||||
/// break;
|
||||
/// }
|
||||
///
|
||||
/// queue.submit(RequestBuffer::reuse(completion.data, transfer_size))
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example (write to an endpoint)
|
||||
/// ```no_run
|
||||
/// use std::mem;
|
||||
/// use futures_lite::future::block_on;
|
||||
/// # use nusb::MaybeFuture;
|
||||
/// # let di = nusb::list_devices().wait().unwrap().next().unwrap();
|
||||
/// # let device = di.open().wait().unwrap();
|
||||
/// # let interface = device.claim_interface(0).wait().unwrap();
|
||||
/// # fn fill_data(_: &mut Vec<u8>) {}
|
||||
/// # fn data_confirmed_sent(_: usize) {}
|
||||
/// let mut queue = interface.bulk_out_queue(0x02);
|
||||
///
|
||||
/// let n_transfers = 8;
|
||||
///
|
||||
/// let mut next_buf = Vec::new();
|
||||
///
|
||||
/// loop {
|
||||
/// while queue.pending() < n_transfers {
|
||||
/// let mut buf = mem::replace(&mut next_buf, Vec::new());
|
||||
/// fill_data(&mut buf); // your function
|
||||
/// queue.submit(buf);
|
||||
/// }
|
||||
///
|
||||
/// let completion = block_on(queue.next_complete());
|
||||
/// data_confirmed_sent(completion.data.actual_length()); // your function
|
||||
/// next_buf = completion.data.reuse();
|
||||
|
||||
/// if completion.status.is_err() {
|
||||
/// break;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Queue<R: TransferRequest> {
|
||||
interface: Arc<platform::Interface>,
|
||||
endpoint: u8,
|
||||
endpoint_type: TransferType,
|
||||
|
||||
/// A queue of pending transfers, expected to complete in order
|
||||
pending: VecDeque<TransferHandle<platform::TransferData>>,
|
||||
|
||||
/// An idle transfer that recently completed for re-use.
|
||||
cached: Option<TransferHandle<platform::TransferData>>,
|
||||
|
||||
bufs: PhantomData<R>,
|
||||
}
|
||||
|
||||
impl<R> Queue<R>
|
||||
where
|
||||
R: TransferRequest + Send + Sync,
|
||||
platform::TransferData: PlatformSubmit<R>,
|
||||
{
|
||||
pub(crate) fn new(
|
||||
interface: Arc<platform::Interface>,
|
||||
endpoint: u8,
|
||||
endpoint_type: TransferType,
|
||||
) -> Queue<R> {
|
||||
Queue {
|
||||
interface,
|
||||
endpoint,
|
||||
endpoint_type,
|
||||
pending: VecDeque::new(),
|
||||
cached: None,
|
||||
bufs: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Submit a new transfer on the endpoint.
|
||||
///
|
||||
/// For an `IN` endpoint, pass a [`RequestBuffer`][`super::RequestBuffer`].\
|
||||
/// For an `OUT` endpoint, pass a [`Vec<u8>`].
|
||||
pub fn submit(&mut self, data: R) {
|
||||
let mut transfer = self.cached.take().unwrap_or_else(|| {
|
||||
self.interface
|
||||
.make_transfer(self.endpoint, self.endpoint_type)
|
||||
});
|
||||
transfer.submit(data);
|
||||
self.pending.push_back(transfer);
|
||||
}
|
||||
|
||||
/// Return a `Future` that waits for the next pending transfer to complete, and yields its
|
||||
/// buffer and status.
|
||||
///
|
||||
/// For an `IN` endpoint, the completion contains a [`Vec<u8>`].\
|
||||
/// For an `OUT` endpoint, the completion contains a [`ResponseBuffer`][`super::ResponseBuffer`].
|
||||
///
|
||||
/// This future is cancel-safe: it can be cancelled and re-created without
|
||||
/// side effects, enabling its use in `select!{}` or similar.
|
||||
///
|
||||
/// Panics if there are no transfers pending.
|
||||
pub fn next_complete<'a>(
|
||||
&'a mut self,
|
||||
) -> impl Future<Output = Completion<R::Response>> + Unpin + Send + Sync + 'a {
|
||||
poll_fn(|cx| self.poll_next(cx))
|
||||
}
|
||||
|
||||
/// Get the next pending transfer if one has completed, or register the
|
||||
/// current task for wakeup when the next transfer completes.
|
||||
///
|
||||
/// For an `IN` endpoint, the completion contains a [`Vec<u8>`].\
|
||||
/// For an `OUT` endpoint, the completion contains a
|
||||
/// [`ResponseBuffer`][`super::ResponseBuffer`].
|
||||
///
|
||||
/// Panics if there are no transfers pending.
|
||||
pub fn poll_next(&mut self, cx: &mut Context) -> Poll<Completion<R::Response>> {
|
||||
let res = self
|
||||
.pending
|
||||
.front_mut()
|
||||
.expect("queue should have pending transfers when calling next_complete")
|
||||
.poll_completion::<R>(cx);
|
||||
if res.is_ready() {
|
||||
self.cached = self.pending.pop_front();
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Get the number of transfers that have been submitted with `submit` that
|
||||
/// have not yet been returned from `next_complete`.
|
||||
pub fn pending(&self) -> usize {
|
||||
self.pending.len()
|
||||
}
|
||||
|
||||
/// Request cancellation of all pending transfers.
|
||||
///
|
||||
/// The transfers will still be returned from subsequent calls to
|
||||
/// `next_complete` so you can tell which were completed,
|
||||
/// partially-completed, or cancelled.
|
||||
pub fn cancel_all(&mut self) {
|
||||
// Cancel transfers in reverse order to ensure subsequent transfers
|
||||
// can't complete out of order while we're going through them.
|
||||
for transfer in self.pending.iter_mut().rev() {
|
||||
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`][crate::transfer::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) -> impl MaybeFuture<Output = Result<(), Error>> {
|
||||
self.interface.clone().clear_halt(self.endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: TransferRequest> Drop for Queue<R> {
|
||||
fn drop(&mut self) {
|
||||
// Cancel transfers in reverse order to ensure subsequent transfers
|
||||
// can't complete out of order while we're going through them.
|
||||
self.pending.drain(..).rev().for_each(drop)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue