Strongly-typed IN vs OUT buffer types

This commit is contained in:
Kevin Mehall 2023-10-05 22:27:03 -06:00
parent a6baa5957e
commit 81cb48daa5
10 changed files with 221 additions and 84 deletions

View file

@ -1,5 +1,5 @@
use futures_lite::future::block_on;
use nusb::TransferStatus;
use nusb::transfer::{RequestBuffer, TransferStatus};
fn main() {
env_logger::init();
@ -13,11 +13,11 @@ fn main() {
let device = di.open().unwrap();
let interface = device.claim_interface(0).unwrap();
let mut queue = interface.bulk_queue(0x81);
let mut queue = interface.bulk_in_queue(0x81);
loop {
while queue.pending() < 8 {
queue.submit(Vec::with_capacity(256));
queue.submit(RequestBuffer::new(256));
}
let result = block_on(queue.next_complete());
println!("{result:?}");

View file

@ -1,5 +1,5 @@
use futures_lite::future::block_on;
use nusb::{ControlIn, ControlOut, ControlType, Recipient};
use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
fn main() {
env_logger::init();
@ -13,7 +13,7 @@ fn main() {
let device = di.open().unwrap();
let interface = device.claim_interface(0).unwrap();
let result = block_on(interface.control_transfer_out(ControlOut {
let result = block_on(interface.control_out(ControlOut {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x81,
@ -23,7 +23,7 @@ fn main() {
}));
println!("{result:?}");
let result = block_on(interface.control_transfer_in(ControlIn {
let result = block_on(interface.control_in(ControlIn {
control_type: ControlType::Vendor,
recipient: Recipient::Device,
request: 0x81,

View file

@ -1,18 +1,19 @@
use std::{
collections::VecDeque,
future::{poll_fn, Future},
marker::PhantomData,
sync::Arc,
};
use crate::{
control::{ControlIn, ControlOut},
platform,
transfer_internal::TransferHandle,
Completion, DeviceInfo, EndpointType, Error, TransferFuture,
transfer::{
Completion, ControlIn, ControlOut, EndpointType, PlatformSubmit, RequestBuffer,
TransferFuture, TransferHandle, TransferRequest,
},
DeviceInfo, Error,
};
type Buffer = Vec<u8>;
#[derive(Clone)]
pub struct Device {
backend: Arc<crate::platform::Device>,
@ -47,29 +48,39 @@ impl Interface {
todo!()
}
pub fn control_transfer_in(&self, data: ControlIn) -> TransferFuture<ControlIn> {
pub fn control_in(&self, data: ControlIn) -> TransferFuture<ControlIn> {
let mut t = self.backend.make_transfer(0, EndpointType::Control);
t.submit::<ControlIn>(data);
TransferFuture::new(t)
}
pub fn control_transfer_out(&self, data: ControlOut) -> TransferFuture<ControlOut> {
pub fn control_out(&self, data: ControlOut) -> TransferFuture<ControlOut> {
let mut t = self.backend.make_transfer(0, EndpointType::Control);
t.submit::<ControlOut>(data);
TransferFuture::new(t)
}
pub fn bulk_transfer(&self, endpoint: u8, buf: Vec<u8>) -> TransferFuture<Vec<u8>> {
pub fn bulk_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture<RequestBuffer> {
let mut t = self.backend.make_transfer(endpoint, EndpointType::Bulk);
t.submit(buf);
TransferFuture::new(t)
}
pub fn bulk_queue(&self, endpoint: u8) -> Queue {
pub fn bulk_out(&self, endpoint: u8, buf: Vec<u8>) -> TransferFuture<Vec<u8>> {
let mut t = self.backend.make_transfer(endpoint, EndpointType::Bulk);
t.submit(buf);
TransferFuture::new(t)
}
pub fn bulk_in_queue(&self, endpoint: u8) -> Queue<RequestBuffer> {
Queue::new(self.backend.clone(), endpoint, EndpointType::Bulk)
}
pub fn interrupt_transfer(&self, endpoint: u8, buf: Vec<u8>) -> TransferFuture<Vec<u8>> {
pub fn bulk_out_queue(&self, endpoint: u8) -> Queue<Vec<u8>> {
Queue::new(self.backend.clone(), endpoint, EndpointType::Bulk)
}
pub fn interrupt_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture<RequestBuffer> {
let mut t = self
.backend
.make_transfer(endpoint, EndpointType::Interrupt);
@ -77,12 +88,24 @@ impl Interface {
TransferFuture::new(t)
}
pub fn interrupt_queue(&self, endpoint: u8) -> Queue {
pub fn interrupt_out(&self, endpoint: u8, buf: Vec<u8>) -> TransferFuture<Vec<u8>> {
let mut t = self
.backend
.make_transfer(endpoint, EndpointType::Interrupt);
t.submit(buf);
TransferFuture::new(t)
}
pub fn interrupt_in_queue(&self, endpoint: u8) -> Queue<RequestBuffer> {
Queue::new(self.backend.clone(), endpoint, EndpointType::Interrupt)
}
pub fn interrupt_out_queue(&self, endpoint: u8) -> Queue<Vec<u8>> {
Queue::new(self.backend.clone(), endpoint, EndpointType::Interrupt)
}
}
pub struct Queue {
pub struct Queue<R: TransferRequest> {
interface: Arc<platform::Interface>,
endpoint: u8,
endpoint_type: EndpointType,
@ -92,33 +115,32 @@ pub struct Queue {
/// An idle transfer that recently completed for re-use. Limiting
cached: Option<TransferHandle<platform::TransferData>>,
bufs: PhantomData<R>,
}
impl Queue {
impl<R> Queue<R>
where
R: TransferRequest,
platform::TransferData: PlatformSubmit<R>,
{
fn new(
interface: Arc<platform::Interface>,
endpoint: u8,
endpoint_type: EndpointType,
) -> Queue {
) -> 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, the transfer size is set by the *capacity* of
/// the buffer, and the length and current contents are ignored. The
/// buffer is returned from a later call to `complete` filled with
/// the data read from the endpoint.
///
/// For an OUT endpoint, the contents of the buffer are written to
/// the endpoint.
pub fn submit(&mut self, data: Buffer) {
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)
@ -130,20 +152,14 @@ impl Queue {
/// Block waiting for the next pending transfer to complete, and return
/// its buffer or an error status.
///
/// For an IN endpoint, the returned buffer contains the data
/// read from the device.
///
/// For an OUT endpoint, the buffer is unmodified, but can be
/// reused for another transfer.
///
/// Panics if there are no transfers pending.
pub fn next_complete<'a>(&'a mut self) -> impl Future<Output = Completion<Vec<u8>>> + 'a {
pub fn next_complete<'a>(&'a mut self) -> impl Future<Output = Completion<R::Response>> + 'a {
poll_fn(|cx| {
let res = self
.pending
.front_mut()
.expect("queue should have pending transfers when calling next_complete")
.poll_completion::<Vec<u8>>(cx);
.poll_completion::<R>(cx);
if res.is_ready() {
self.cached = self.pending.pop_front();
}
@ -168,7 +184,7 @@ impl Queue {
}
}
impl Drop for Queue {
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.

View file

@ -3,18 +3,12 @@ use std::io;
pub mod platform;
pub use platform::list_devices;
mod control;
pub use control::{ControlIn, ControlOut, ControlType, Direction, Recipient};
mod enumeration;
pub use enumeration::{DeviceInfo, Speed, UnknownValue};
mod device;
use device::Device;
mod transfer;
pub use transfer::{Completion, EndpointType, TransferFuture, TransferStatus};
mod transfer_internal;
pub mod transfer;
pub type Error = io::Error;

View file

@ -12,8 +12,8 @@ use super::{
usbfs::{self, Urb},
};
use crate::{
transfer_internal::{self, TransferHandle},
DeviceInfo, EndpointType, Error,
transfer::{notify_completion, EndpointType, TransferHandle},
DeviceInfo, Error,
};
pub(crate) struct LinuxDevice {
@ -66,7 +66,7 @@ impl LinuxDevice {
};
// SAFETY: pointer came from submit via kernel an we're now done with it
unsafe { transfer_internal::notify_completion::<super::TransferData>(user_data) }
unsafe { notify_completion::<super::TransferData>(user_data) }
}
Err(Errno::AGAIN) => {}
Err(Errno::NODEV) => {
@ -143,7 +143,7 @@ impl LinuxInterface {
u.actual_length = 0;
u.status = e.raw_os_error();
}
transfer_internal::notify_completion::<super::TransferData>(urb as *mut c_void)
notify_completion::<super::TransferData>(urb as *mut c_void)
}
} else {
debug!("Submitted URB {urb:?} on ep {ep:x}");

View file

@ -7,9 +7,9 @@ use std::{
use rustix::io::Errno;
use crate::{
control::{ControlIn, ControlOut, SETUP_PACKET_SIZE},
transfer_internal, Completion, EndpointType, TransferStatus,
use crate::transfer::{
Completion, ControlIn, ControlOut, EndpointType, PlatformSubmit, PlatformTransfer,
RequestBuffer, ResponseBuffer, TransferStatus, SETUP_PACKET_SIZE,
};
use super::usbfs::{
@ -23,7 +23,7 @@ use super::usbfs::{
/// 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`.
pub(crate) struct TransferData {
pub struct TransferData {
urb: *mut Urb,
capacity: usize,
interface: Arc<super::Interface>,
@ -35,7 +35,7 @@ impl TransferData {
pub(super) fn new(
interface: Arc<super::Interface>,
endpoint: u8,
ep_type: crate::EndpointType,
ep_type: EndpointType,
) -> TransferData {
let ep_type = match ep_type {
EndpointType::Control => USBDEVFS_URB_TYPE_CONTROL,
@ -101,7 +101,7 @@ impl Drop for TransferData {
}
}
impl transfer_internal::PlatformTransfer for TransferData {
impl PlatformTransfer for TransferData {
fn cancel(&self) {
unsafe {
self.interface.cancel_urb(self.urb);
@ -109,7 +109,7 @@ impl transfer_internal::PlatformTransfer for TransferData {
}
}
impl transfer_internal::PlatformSubmit<Vec<u8>> for TransferData {
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;
let len = if ep & 0x80 == 0 {
@ -123,6 +123,30 @@ impl transfer_internal::PlatformSubmit<Vec<u8>> for TransferData {
unsafe { self.interface.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.interface.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;
@ -133,7 +157,7 @@ impl transfer_internal::PlatformSubmit<Vec<u8>> for TransferData {
}
}
impl transfer_internal::PlatformSubmit<ControlIn> for TransferData {
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);
@ -156,7 +180,7 @@ impl transfer_internal::PlatformSubmit<ControlIn> for TransferData {
}
}
impl transfer_internal::PlatformSubmit<ControlOut<'_>> for TransferData {
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);
@ -172,11 +196,11 @@ impl transfer_internal::PlatformSubmit<ControlOut<'_>> for TransferData {
unsafe { self.interface.submit_urb(self.urb) }
}
unsafe fn take_completed(&mut self) -> Completion<usize> {
unsafe fn take_completed(&mut self) -> Completion<ResponseBuffer> {
let status = urb_status(self.urb_mut());
let len = self.urb_mut().actual_length as usize;
drop(self.take_buf(0));
Completion { data: len, status }
let data = ResponseBuffer::from_vec(self.take_buf(0), len);
Completion { data, status }
}
}

98
src/transfer/buffer.rs Normal file
View file

@ -0,0 +1,98 @@
use std::fmt::Debug;
use std::mem::ManuallyDrop;
use super::TransferRequest;
pub struct RequestBuffer {
pub(crate) buf: *mut u8,
pub(crate) capacity: usize,
pub(crate) requested: usize,
}
impl RequestBuffer {
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,
}
}
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)
}
pub fn reuse(v: Vec<u8>, len: usize) -> RequestBuffer {
let mut v = ManuallyDrop::new(v);
v.reserve_exact(len.saturating_sub(len));
RequestBuffer {
buf: v.as_mut_ptr(),
capacity: v.capacity(),
requested: len,
}
}
}
impl Drop for RequestBuffer {
fn drop(&mut self) {
unsafe { drop(Vec::from_raw_parts(self.buf, 0, self.capacity)) }
}
}
impl Debug for RequestBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RequestBuffer")
.field("requested", &self.requested)
.finish_non_exhaustive()
}
}
impl TransferRequest for RequestBuffer {
type Response = Vec<u8>;
}
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,
}
}
pub fn actual_length(&self) -> usize {
self.transferred
}
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()
}
}
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;
}

View file

@ -1,3 +1,5 @@
use super::{ResponseBuffer, TransferRequest};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Direction {
@ -35,7 +37,7 @@ pub struct ControlOut<'a> {
#[doc(alias = "bRequest")]
pub request: u8,
#[doc(alias = "windex")]
#[doc(alias = "wValue")]
pub value: u16,
#[doc(alias = "wIndex")]
@ -59,6 +61,10 @@ impl<'a> ControlOut<'a> {
}
}
impl TransferRequest for ControlOut<'_> {
type Response = ResponseBuffer;
}
pub struct ControlIn {
#[doc(alias = "bmRequestType")]
pub control_type: ControlType,
@ -117,3 +123,7 @@ fn pack_setup(
(length >> 8) as u8,
]
}
impl TransferRequest for ControlIn {
type Response = Vec<u8>;
}

View file

@ -8,12 +8,9 @@ use std::{
use atomic_waker::AtomicWaker;
use crate::{
control::{ControlIn, ControlOut},
Completion,
};
use super::Completion;
pub(crate) trait PlatformTransfer: Send {
pub trait PlatformTransfer: Send {
/// Request cancellation of a transfer that may or may not currently be
/// pending.
fn cancel(&self);
@ -23,7 +20,7 @@ pub trait TransferRequest {
type Response;
}
pub(crate) trait PlatformSubmit<D: TransferRequest>: PlatformTransfer {
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.
@ -35,18 +32,6 @@ pub(crate) trait PlatformSubmit<D: TransferRequest>: PlatformTransfer {
unsafe fn take_completed(&mut self) -> Completion<D::Response>;
}
impl TransferRequest for Vec<u8> {
type Response = Vec<u8>;
}
impl TransferRequest for ControlIn {
type Response = Vec<u8>;
}
impl TransferRequest for ControlOut<'_> {
type Response = usize;
}
struct TransferInner<P: PlatformTransfer> {
/// Platform-specific data.
///

View file

@ -1,13 +1,23 @@
use crate::{
platform,
transfer_internal::{PlatformSubmit, TransferHandle, TransferRequest},
};
use std::{
future::Future,
marker::PhantomData,
task::{Context, Poll},
};
use crate::platform;
mod buffer;
pub use buffer::{RequestBuffer, ResponseBuffer};
mod control;
pub(crate) use control::SETUP_PACKET_SIZE;
pub use control::{ControlIn, ControlOut, ControlType, Direction, Recipient};
mod internal;
pub(crate) use internal::{
notify_completion, PlatformSubmit, PlatformTransfer, TransferHandle, TransferRequest,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum EndpointType {
Control = 0,