diff --git a/examples/bulk.rs b/examples/bulk.rs index a40e157..e5c7c56 100644 --- a/examples/bulk.rs +++ b/examples/bulk.rs @@ -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:?}"); diff --git a/examples/control.rs b/examples/control.rs index 6e5c2f7..ecf407b 100644 --- a/examples/control.rs +++ b/examples/control.rs @@ -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, diff --git a/src/device.rs b/src/device.rs index caeb722..aeadedb 100644 --- a/src/device.rs +++ b/src/device.rs @@ -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; - #[derive(Clone)] pub struct Device { backend: Arc, @@ -47,29 +48,39 @@ impl Interface { todo!() } - pub fn control_transfer_in(&self, data: ControlIn) -> TransferFuture { + pub fn control_in(&self, data: ControlIn) -> TransferFuture { let mut t = self.backend.make_transfer(0, EndpointType::Control); t.submit::(data); TransferFuture::new(t) } - pub fn control_transfer_out(&self, data: ControlOut) -> TransferFuture { + pub fn control_out(&self, data: ControlOut) -> TransferFuture { let mut t = self.backend.make_transfer(0, EndpointType::Control); t.submit::(data); TransferFuture::new(t) } - pub fn bulk_transfer(&self, endpoint: u8, buf: Vec) -> TransferFuture> { + pub fn bulk_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture { 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) -> TransferFuture> { + 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 { Queue::new(self.backend.clone(), endpoint, EndpointType::Bulk) } - pub fn interrupt_transfer(&self, endpoint: u8, buf: Vec) -> TransferFuture> { + pub fn bulk_out_queue(&self, endpoint: u8) -> Queue> { + Queue::new(self.backend.clone(), endpoint, EndpointType::Bulk) + } + + pub fn interrupt_in(&self, endpoint: u8, buf: RequestBuffer) -> TransferFuture { 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) -> TransferFuture> { + 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 { + Queue::new(self.backend.clone(), endpoint, EndpointType::Interrupt) + } + + pub fn interrupt_out_queue(&self, endpoint: u8) -> Queue> { Queue::new(self.backend.clone(), endpoint, EndpointType::Interrupt) } } -pub struct Queue { +pub struct Queue { interface: Arc, endpoint: u8, endpoint_type: EndpointType, @@ -92,33 +115,32 @@ pub struct Queue { /// An idle transfer that recently completed for re-use. Limiting cached: Option>, + + bufs: PhantomData, } -impl Queue { +impl Queue +where + R: TransferRequest, + platform::TransferData: PlatformSubmit, +{ fn new( interface: Arc, endpoint: u8, endpoint_type: EndpointType, - ) -> Queue { + ) -> Queue { 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>> + 'a { + pub fn next_complete<'a>(&'a mut self) -> impl Future> + 'a { poll_fn(|cx| { let res = self .pending .front_mut() .expect("queue should have pending transfers when calling next_complete") - .poll_completion::>(cx); + .poll_completion::(cx); if res.is_ready() { self.cached = self.pending.pop_front(); } @@ -168,7 +184,7 @@ impl Queue { } } -impl Drop for Queue { +impl Drop for Queue { 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. diff --git a/src/lib.rs b/src/lib.rs index 1d60a57..7e9167d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/platform/linux_usbfs/device.rs b/src/platform/linux_usbfs/device.rs index 62b2b43..068f82a 100644 --- a/src/platform/linux_usbfs/device.rs +++ b/src/platform/linux_usbfs/device.rs @@ -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::(user_data) } + unsafe { notify_completion::(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::(urb as *mut c_void) + notify_completion::(urb as *mut c_void) } } else { debug!("Submitted URB {urb:?} on ep {ep:x}"); diff --git a/src/platform/linux_usbfs/transfer.rs b/src/platform/linux_usbfs/transfer.rs index d5a7e70..90d9029 100644 --- a/src/platform/linux_usbfs/transfer.rs +++ b/src/platform/linux_usbfs/transfer.rs @@ -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, @@ -35,7 +35,7 @@ impl TransferData { pub(super) fn new( interface: Arc, 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> for TransferData { +impl PlatformSubmit> for TransferData { unsafe fn submit(&mut self, data: Vec, 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> for TransferData { unsafe { self.interface.submit_urb(self.urb) } } + unsafe fn take_completed(&mut self) -> Completion { + 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 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> { let status = urb_status(self.urb_mut()); let len = self.urb_mut().actual_length as usize; @@ -133,7 +157,7 @@ impl transfer_internal::PlatformSubmit> for TransferData { } } -impl transfer_internal::PlatformSubmit for TransferData { +impl PlatformSubmit 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 for TransferData { } } -impl transfer_internal::PlatformSubmit> for TransferData { +impl PlatformSubmit> 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> for TransferData { unsafe { self.interface.submit_urb(self.urb) } } - unsafe fn take_completed(&mut self) -> Completion { + unsafe fn take_completed(&mut self) -> Completion { 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 } } } diff --git a/src/transfer/buffer.rs b/src/transfer/buffer.rs new file mode 100644 index 0000000..b25f090 --- /dev/null +++ b/src/transfer/buffer.rs @@ -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, 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, 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; +} +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, 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 { + 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 { + type Response = ResponseBuffer; +} diff --git a/src/control.rs b/src/transfer/control.rs similarity index 91% rename from src/control.rs rename to src/transfer/control.rs index ac5f4a8..bcaf05a 100644 --- a/src/control.rs +++ b/src/transfer/control.rs @@ -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; +} diff --git a/src/transfer_internal.rs b/src/transfer/internal.rs similarity index 94% rename from src/transfer_internal.rs rename to src/transfer/internal.rs index 0c25a21..3a1d658 100644 --- a/src/transfer_internal.rs +++ b/src/transfer/internal.rs @@ -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: PlatformTransfer { +pub trait PlatformSubmit: 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: PlatformTransfer { unsafe fn take_completed(&mut self) -> Completion; } -impl TransferRequest for Vec { - type Response = Vec; -} - -impl TransferRequest for ControlIn { - type Response = Vec; -} - -impl TransferRequest for ControlOut<'_> { - type Response = usize; -} - struct TransferInner { /// Platform-specific data. /// diff --git a/src/transfer.rs b/src/transfer/mod.rs similarity index 77% rename from src/transfer.rs rename to src/transfer/mod.rs index 88fa02c..f764244 100644 --- a/src/transfer.rs +++ b/src/transfer/mod.rs @@ -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,