blob: d94cd62d742ddfa4d63d6df2c2c34a4a7d698f39 [file]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::convert::TryInto;
use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::mem::size_of_val;
use std::os::raw::c_int;
use std::os::raw::c_uchar;
use std::os::raw::c_uint;
use std::os::raw::c_void;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Weak;
use base::error;
use base::handle_eintr_errno;
use base::warn;
use base::AsRawDescriptor;
use base::IoctlNr;
use base::MappedRegion;
use base::MemoryMapping;
use base::MemoryMappingBuilder;
use base::Protection;
use base::RawDescriptor;
use data_model::vec_with_array_field;
use libc::EAGAIN;
use libc::ECONNRESET;
use libc::ENODEV;
use libc::ENOENT;
use libc::EPIPE;
use libc::ESHUTDOWN;
use sync::Mutex;
use crate::control_request_type;
use crate::descriptor;
use crate::ConfigDescriptorTree;
use crate::ControlRequestDataPhaseTransferDirection;
use crate::ControlRequestRecipient;
use crate::ControlRequestType;
use crate::DeviceDescriptor;
use crate::DeviceDescriptorTree;
use crate::DeviceSpeed;
use crate::Error;
use crate::Result;
use crate::StandardControlRequest;
// This is the maximum block size observed during storage performance test
const MMAP_SIZE: usize = 1024 * 1024;
/// ManagedDmaBuffer represents the entire DMA buffer allocated by a device
struct ManagedDmaBuffer {
/// The entire DMA buffer
buf: MemoryMapping,
/// A DMA buffer lent to a TransferBuffer. This is a part of the entire buffer.
used: Option<Arc<Mutex<DmaBuffer>>>,
}
/// DmaBuffer represents a DMA buffer lent by a device
pub struct DmaBuffer {
/// Host virtual address of the buffer
addr: u64,
/// Size of the buffer
size: usize,
}
impl DmaBuffer {
pub fn address(&mut self) -> *mut c_void {
self.addr as *mut c_void
}
pub fn size(&self) -> usize {
self.size
}
pub fn as_slice(&self) -> &[u8] {
// SAFETY:
// Safe because the region has been lent by a device
unsafe { std::slice::from_raw_parts(self.addr as *const u8, self.size) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
// SAFETY:
// Safe because the region has been lent by a device
unsafe { std::slice::from_raw_parts_mut(self.addr as *mut u8, self.size) }
}
}
/// TransferBuffer is used for data transfer between crosvm and the host kernel
#[derive(Clone)]
pub enum TransferBuffer {
Vector(Vec<u8>),
Dma(Weak<Mutex<DmaBuffer>>),
}
impl TransferBuffer {
pub fn address(&mut self) -> Option<*mut c_void> {
match self {
TransferBuffer::Vector(v) => Some(v.as_mut_ptr() as *mut c_void),
TransferBuffer::Dma(buf) => buf.upgrade().map(|buf| buf.lock().address()),
}
}
pub fn size(&self) -> Option<usize> {
match self {
TransferBuffer::Vector(v) => Some(v.len()),
TransferBuffer::Dma(buf) => buf.upgrade().map(|buf| buf.lock().size()),
}
}
}
/// Device represents a USB device.
pub struct Device {
fd: Arc<File>,
device_descriptor_tree: DeviceDescriptorTree,
dma_buffer: Option<ManagedDmaBuffer>,
in_flight_transfers: AtomicUsize,
detaching: AtomicBool,
is_lost: AtomicBool,
is_unrecoverable: AtomicBool,
cancel_lock: Arc<Mutex<()>>,
}
/// Transfer contains the information necessary to submit a USB request
/// and, once it has been submitted and completed, contains the response.
pub struct Transfer {
// NOTE: This Vec is actually a single URB with a trailing
// variable-length field created by vec_with_array_field().
urb: Vec<usb_sys::usbdevfs_urb>,
pub buffer: TransferBuffer,
callback: Option<Box<dyn Fn(Transfer) + Send + Sync>>,
}
/// TransferHandle is a handle that allows cancellation of in-flight transfers
/// between submit_transfer() and get_completed_transfer().
/// Attempting to cancel a transfer that has already completed is safe and will
/// return an error.
pub struct TransferHandle {
weak_transfer: std::sync::Weak<Transfer>,
fd: std::sync::Weak<File>,
cancel_lock: Arc<Mutex<()>>,
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum TransferStatus {
Completed,
Error,
Cancelled,
NoDevice,
Stalled,
}
impl Device {
/// Create a new `Device` from a file descriptor.
/// `fd` should be a file in usbdevfs (e.g. `/dev/bus/usb/001/002`).
pub fn new(mut fd: File) -> Result<Self> {
fd.seek(SeekFrom::Start(0)).map_err(Error::DescriptorRead)?;
let mut descriptor_data = Vec::new();
fd.read_to_end(&mut descriptor_data)
.map_err(Error::DescriptorRead)?;
let device_descriptor_tree = descriptor::parse_usbfs_descriptors(&descriptor_data)?;
let mut device = Device {
fd: Arc::new(fd),
device_descriptor_tree,
dma_buffer: None,
in_flight_transfers: AtomicUsize::new(0),
detaching: AtomicBool::new(false),
is_lost: AtomicBool::new(false),
is_unrecoverable: AtomicBool::new(false),
cancel_lock: Arc::new(Mutex::new(())),
};
let map = MemoryMappingBuilder::new(MMAP_SIZE)
.from_file(&device.fd)
.protection(Protection::read_write())
.build();
match map {
Ok(map) => {
device.dma_buffer = Some(ManagedDmaBuffer {
buf: map,
used: None,
});
}
Err(e) => {
// Ignore the error since we can process requests without DMA buffer
warn!(
"mmap() failed. User-provided buffer will be used for data transfer. {}",
e
);
}
}
Ok(device)
}
pub fn fd(&self) -> Arc<File> {
self.fd.clone()
}
unsafe fn ioctl(&self, nr: IoctlNr) -> Result<i32> {
let ret = handle_eintr_errno!(base::ioctl(&*self.fd, nr));
if ret < 0 {
return Err(Error::IoctlFailed(nr, base::Error::last()));
}
Ok(ret)
}
unsafe fn ioctl_with_ref<T>(&self, nr: IoctlNr, arg: &T) -> Result<i32> {
let ret = handle_eintr_errno!(base::ioctl_with_ref(&*self.fd, nr, arg));
if ret < 0 {
return Err(Error::IoctlFailed(nr, base::Error::last()));
}
Ok(ret)
}
unsafe fn ioctl_with_mut_ref<T>(&self, nr: IoctlNr, arg: &mut T) -> Result<i32> {
let ret = handle_eintr_errno!(base::ioctl_with_mut_ref(&*self.fd, nr, arg));
if ret < 0 {
return Err(Error::IoctlFailed(nr, base::Error::last()));
}
Ok(ret)
}
unsafe fn ioctl_with_mut_ptr<T>(&self, nr: IoctlNr, arg: *mut T) -> Result<i32> {
let ret = handle_eintr_errno!(base::ioctl_with_mut_ptr(&*self.fd, nr, arg));
if ret < 0 {
return Err(Error::IoctlFailed(nr, base::Error::last()));
}
Ok(ret)
}
pub fn reserve_dma_buffer(&mut self, size: usize) -> Result<Weak<Mutex<DmaBuffer>>> {
if let Some(managed) = &mut self.dma_buffer {
if managed.used.is_none() {
let buf = Arc::new(Mutex::new(DmaBuffer {
addr: managed.buf.as_ptr() as u64,
size,
}));
let ret = Ok(Arc::downgrade(&buf));
managed.used = Some(buf);
return ret;
}
}
Err(Error::GetDmaBufferFailed(size))
}
pub fn release_dma_buffer(&mut self, dmabuf: Weak<Mutex<DmaBuffer>>) -> Result<()> {
if let Some(managed) = &mut self.dma_buffer {
if let Some(released) = dmabuf.upgrade() {
let addr = { released.lock().address() as u64 };
if let Some(lent) = &managed.used {
if lent.lock().addr == addr {
managed.used = None;
return Ok(());
}
}
}
}
Err(Error::ReleaseDmaBufferFailed)
}
/// Submit a transfer to the device.
/// The transfer will be processed asynchronously by the device.
/// Call `poll_transfers()` on this device to check for completed transfers.
pub fn submit_transfer(&mut self, transfer: Transfer) -> Result<TransferHandle> {
if self.is_detaching() || self.is_device_lost() || self.is_unrecoverable() {
return Err(Error::NoDevice);
}
let mut rc_transfer = Arc::new(transfer);
// Technically, Arc::from_raw() should only be called on pointers returned
// from Arc::into_raw(). However, we need to stash this value inside the
// Arc<Transfer> itself, so we manually calculate the address that would be
// returned from Arc::into_raw() via Deref and then call Arc::into_raw()
// to forget the Arc without dropping its contents.
// Do not remove the into_raw() call!
let raw_transfer = (&*rc_transfer) as *const Transfer as usize;
match Arc::get_mut(&mut rc_transfer) {
Some(t) => t.urb_mut().usercontext = raw_transfer,
None => {
// This should never happen, since there is only one strong reference
// at this point.
return Err(Error::RcGetMutFailed);
}
}
let _ = Arc::into_raw(rc_transfer.clone());
let urb_ptr = rc_transfer.urb.as_ptr() as *mut usb_sys::usbdevfs_urb;
// SAFETY:
// Safe because we control the lifetime of the URB via Arc::into_raw() and
// Arc::from_raw() in poll_transfers().
unsafe {
if let Err(e) = self.ioctl_with_mut_ptr(usb_sys::USBDEVFS_SUBMITURB, urb_ptr) {
// Reclaim the leaked Arc reference if submission failed.
let leaked_transfer = Arc::from_raw(raw_transfer as *const Transfer);
if let TransferBuffer::Dma(buf) = &leaked_transfer.buffer {
if self.release_dma_buffer(buf.clone()).is_err() {
warn!("failed to release dma buffer");
}
}
return Err(e);
}
}
let _ = self.in_flight_transfers.fetch_add(1, Ordering::SeqCst);
let weak_transfer = Arc::downgrade(&rc_transfer);
Ok(TransferHandle {
weak_transfer,
fd: Arc::downgrade(&self.fd),
cancel_lock: self.cancel_lock.clone(),
})
}
/// Check for completed asynchronous transfers submitted via `submit_transfer()`.
/// The callback for each completed transfer will be called.
pub fn poll_transfers(&mut self) -> Result<()> {
// Reap completed transfers until we get EAGAIN.
loop {
let mut urb_ptr: *mut usb_sys::usbdevfs_urb = std::ptr::null_mut();
let result =
// SAFETY:
// Safe because we provide a valid urb_ptr to be filled by the kernel.
unsafe { self.ioctl_with_mut_ref(usb_sys::USBDEVFS_REAPURBNDELAY, &mut urb_ptr) };
match result {
// EAGAIN indicates no more completed transfers right now.
Err(Error::IoctlFailed(_nr, e)) if e.errno() == EAGAIN => break,
// ENODEV/ESHUTDOWN indicates the device is gone.
Err(Error::IoctlFailed(_nr, e))
if e.errno() == ENODEV || e.errno() == ESHUTDOWN =>
{
self.is_lost.store(true, Ordering::SeqCst);
break;
}
Err(e) => {
self.is_unrecoverable.store(true, Ordering::SeqCst);
return Err(e);
}
Ok(_) => {}
}
if urb_ptr.is_null() {
break;
}
let _ = self.in_flight_transfers.fetch_sub(1, Ordering::SeqCst);
let mut transfer = {
// Synchronize with TransferHandle::cancel to ensure it drops its strong Arc
// reference before we attempt try_unwrap.
let _guard = self.cancel_lock.lock();
let rc_transfer: Arc<Transfer> =
// SAFETY:
// Safe because the URB usercontext field is always set to the result of
// Arc::into_raw() in submit_transfer().
unsafe { Arc::from_raw((*urb_ptr).usercontext as *const Transfer) };
// There should always be exactly one strong reference to rc_transfer because
// cancel_lock guarantees cancel() is not holding a reference.
Arc::try_unwrap(rc_transfer).map_err(|_| Error::RcUnwrapFailed)?
};
let dmabuf = match &mut transfer.buffer {
TransferBuffer::Dma(buf) => Some(buf.clone()),
TransferBuffer::Vector(_) => None,
};
if let Some(cb) = transfer.callback.take() {
cb(transfer);
}
if let Some(dmabuf) = dmabuf {
if self.release_dma_buffer(dmabuf).is_err() {
warn!("failed to release dma buffer");
}
}
}
Ok(())
}
fn no_in_flight_transfer(&self) -> bool {
self.in_flight_transfers.load(Ordering::SeqCst) == 0
}
fn is_detaching(&self) -> bool {
self.detaching.load(Ordering::SeqCst)
}
/// Return true if the device is lost.
pub fn is_device_lost(&self) -> bool {
self.is_lost.load(Ordering::SeqCst)
}
/// Request the device to get ready for detaching. Check the status with ready_to_detach().
pub fn set_detaching(&self) {
self.detaching.store(true, Ordering::SeqCst);
}
/// Check if the device is ready to be detached, i.e., if we have reaped all the transfers
/// we've submitted to the host. Returns true when ready.
pub fn ready_to_detach(&self) -> bool {
self.is_detaching() && (self.is_unrecoverable() || self.no_in_flight_transfer())
}
fn is_unrecoverable(&self) -> bool {
self.is_unrecoverable.load(Ordering::SeqCst)
}
/// Drop the DMA buffer.
pub fn drop_dma_buffer(&mut self) {
self.dma_buffer.take();
}
/// Perform a USB port reset to reinitialize a device.
pub fn reset(&self) -> Result<()> {
// TODO(dverkamp): re-enable reset once crbug.com/1058059 is resolved.
// Skip reset for all non-Edge TPU devices.
let vid = self.device_descriptor_tree.idVendor;
let pid = self.device_descriptor_tree.idProduct;
match (vid, pid) {
(0x1a6e, 0x089a) => (),
_ => return Ok(()),
}
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor.
let result = unsafe { self.ioctl(usb_sys::USBDEVFS_RESET) };
if let Err(Error::IoctlFailed(_nr, errno_err)) = result {
// The device may disappear after a reset if e.g. its firmware changed.
// Treat that as success.
if errno_err.errno() == libc::ENODEV {
return Ok(());
}
}
result?;
Ok(())
}
/// Claim an interface on this device.
pub fn claim_interface(&self, interface_number: u8) -> Result<()> {
let disconnect_claim = usb_sys::usbdevfs_disconnect_claim {
interface: interface_number.into(),
flags: 0,
driver: [0u8; 256],
};
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevs_disconnect_claim structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_DISCONNECT_CLAIM, &disconnect_claim)?;
}
Ok(())
}
/// Release an interface previously claimed with `claim_interface()`.
pub fn release_interface(&self, interface_number: u8) -> Result<()> {
let ifnum: c_uint = interface_number.into();
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to unsigned int.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_RELEASEINTERFACE, &ifnum)?;
}
Ok(())
}
/// Activate an alternate setting for an interface.
pub fn set_interface_alt_setting(
&self,
interface_number: u8,
alternative_setting: u8,
) -> Result<()> {
let setinterface = usb_sys::usbdevfs_setinterface {
interface: interface_number.into(),
altsetting: alternative_setting.into(),
};
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_setinterface structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_SETINTERFACE, &setinterface)?;
}
Ok(())
}
/// Set active configuration for this device.
pub fn set_active_configuration(&mut self, config: u8) -> Result<()> {
let config: c_int = config.into();
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to int.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_SETCONFIGURATION, &config)?;
}
Ok(())
}
/// Get the device descriptor of this device.
pub fn get_device_descriptor(&self) -> Result<DeviceDescriptor> {
Ok(*self.device_descriptor_tree)
}
pub fn get_device_descriptor_tree(&self) -> &DeviceDescriptorTree {
&self.device_descriptor_tree
}
/// Get active config descriptor of this device.
pub fn get_config_descriptor(&self, config: u8) -> Result<ConfigDescriptorTree> {
match self.device_descriptor_tree.get_config_descriptor(config) {
Some(config_descriptor) => Ok(config_descriptor.clone()),
None => Err(Error::NoSuchDescriptor),
}
}
/// Get a configuration descriptor by its index within the list of descriptors returned
/// by the device.
pub fn get_config_descriptor_by_index(&self, config_index: u8) -> Result<ConfigDescriptorTree> {
match self
.device_descriptor_tree
.get_config_descriptor_by_index(config_index)
{
Some(config_descriptor) => Ok(config_descriptor.clone()),
None => Err(Error::NoSuchDescriptor),
}
}
/// Get bConfigurationValue of the currently active configuration.
pub fn get_active_configuration(&self) -> Result<u8> {
// If the device only exposes a single configuration, bypass the control transfer below
// by looking up the configuration value from the descriptor.
if self.device_descriptor_tree.bNumConfigurations == 1 {
if let Some(config_descriptor) = self
.device_descriptor_tree
.get_config_descriptor_by_index(0)
{
return Ok(config_descriptor.bConfigurationValue);
}
}
// Send a synchronous control transfer to get the active configuration.
let mut active_config: u8 = 0;
let ctrl_transfer = usb_sys::usbdevfs_ctrltransfer {
bRequestType: control_request_type(
ControlRequestType::Standard,
ControlRequestDataPhaseTransferDirection::DeviceToHost,
ControlRequestRecipient::Device,
),
bRequest: StandardControlRequest::GetConfiguration as u8,
wValue: 0,
wIndex: 0,
wLength: size_of_val(&active_config) as u16,
timeout: 5000, // milliseconds
data: &mut active_config as *mut u8 as *mut c_void,
};
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_ctrltransfer structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_CONTROL, &ctrl_transfer)?;
}
Ok(active_config)
}
/// Get the total number of configurations for this device.
pub fn get_num_configurations(&self) -> u8 {
self.device_descriptor_tree.bNumConfigurations
}
/// Clear the halt/stall condition for an endpoint.
pub fn clear_halt(&self, ep_addr: u8) -> Result<()> {
let endpoint: c_uint = ep_addr.into();
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to unsigned int.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_CLEAR_HALT, &endpoint)?;
}
Ok(())
}
/// Get speed of this device.
pub fn get_speed(&self) -> Result<Option<DeviceSpeed>> {
// SAFETY: args are valid and the return value is checked
let speed = unsafe { self.ioctl(usb_sys::USBDEVFS_GET_SPEED) }?;
match speed {
1 => Ok(Some(DeviceSpeed::Low)), // Low Speed
2 => Ok(Some(DeviceSpeed::Full)), // Full Speed
3 => Ok(Some(DeviceSpeed::High)), // High Speed
4 => Ok(Some(DeviceSpeed::High)), // Wireless, treat as a High Speed device
5 => Ok(Some(DeviceSpeed::Super)), // Super Speed
6 => Ok(Some(DeviceSpeed::SuperPlus)), // Super Speed Plus
_ => {
error!("unexpected speed: {:?}", speed);
Ok(None)
}
}
}
/// Allocate streams for the endpoint
pub fn alloc_streams(&self, ep: u8, num_streams: u16) -> Result<()> {
let mut streams = vec_with_array_field::<usb_sys::usbdevfs_streams, c_uchar>(1);
streams[0].num_streams = num_streams as c_uint;
streams[0].num_eps = 1 as c_uint;
// SAFETY:
// Safe because we have allocated enough memory
let eps = unsafe { streams[0].eps.as_mut_slice(1) };
eps[0] = ep as c_uchar;
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_streams structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_ALLOC_STREAMS, &streams[0])?;
}
Ok(())
}
/// Free streams for the endpoint
pub fn free_streams(&self, ep: u8) -> Result<()> {
let mut streams = vec_with_array_field::<usb_sys::usbdevfs_streams, c_uchar>(1);
streams[0].num_eps = 1 as c_uint;
// SAFETY:
// Safe because we have allocated enough memory
let eps = unsafe { streams[0].eps.as_mut_slice(1) };
eps[0] = ep as c_uchar;
// SAFETY:
// Safe because self.fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_streams structure.
unsafe {
self.ioctl_with_ref(usb_sys::USBDEVFS_FREE_STREAMS, &streams[0])?;
}
Ok(())
}
}
impl AsRawDescriptor for Device {
fn as_raw_descriptor(&self) -> RawDescriptor {
self.fd.as_raw_descriptor()
}
}
impl Transfer {
fn urb(&self) -> &usb_sys::usbdevfs_urb {
// self.urb is a Vec created with `vec_with_array_field`; the first entry is
// the URB itself.
&self.urb[0]
}
fn urb_mut(&mut self) -> &mut usb_sys::usbdevfs_urb {
&mut self.urb[0]
}
fn new(
transfer_type: u8,
endpoint: u8,
buffer: TransferBuffer,
iso_packets: &[usb_sys::usbdevfs_iso_packet_desc],
) -> Result<Transfer> {
let mut transfer = Transfer {
urb: vec_with_array_field::<usb_sys::usbdevfs_urb, usb_sys::usbdevfs_iso_packet_desc>(
iso_packets.len(),
),
buffer,
callback: None,
};
transfer.urb_mut().urb_type = transfer_type;
transfer.urb_mut().endpoint = endpoint;
transfer.urb_mut().buffer = transfer.buffer.address().ok_or(Error::InvalidBuffer)?;
transfer.urb_mut().buffer_length = transfer
.buffer
.size()
.ok_or(Error::InvalidBuffer)?
.try_into()
.map_err(Error::InvalidBufferLength)?;
// SAFETY:
// Safe because we ensured there is enough space in transfer.urb to hold the number of
// isochronous frames required.
let iso_frame_desc = unsafe {
transfer
.urb_mut()
.iso_frame_desc
.as_mut_slice(iso_packets.len())
};
iso_frame_desc.copy_from_slice(iso_packets);
Ok(transfer)
}
/// Create a control transfer.
pub fn new_control(buffer: TransferBuffer) -> Result<Transfer> {
let endpoint = 0;
Self::new(usb_sys::USBDEVFS_URB_TYPE_CONTROL, endpoint, buffer, &[])
}
/// Create an interrupt transfer.
pub fn new_interrupt(endpoint: u8, buffer: TransferBuffer) -> Result<Transfer> {
Self::new(usb_sys::USBDEVFS_URB_TYPE_INTERRUPT, endpoint, buffer, &[])
}
/// Create a bulk transfer.
pub fn new_bulk(
endpoint: u8,
buffer: TransferBuffer,
stream_id: Option<u16>,
) -> Result<Transfer> {
let mut transfer = Self::new(usb_sys::USBDEVFS_URB_TYPE_BULK, endpoint, buffer, &[])?;
if let Some(stream_id) = stream_id {
transfer.urb_mut().number_of_packets_or_stream_id = stream_id as u32;
}
Ok(transfer)
}
/// Create an isochronous transfer.
pub fn new_isochronous(
endpoint: u8,
buffer: TransferBuffer,
packet_size: u32,
) -> Result<Transfer> {
let buffer_size: u32 = buffer
.size()
.ok_or(Error::InvalidBuffer)?
.try_into()
.map_err(Error::InvalidBufferLength)?;
// Isochronous transfers divide the buffer into multiple packets.
if buffer_size == 0 || packet_size == 0 {
error!("invalid ISOC parameters: buffer_size={buffer_size}, packet_size={packet_size}");
return Err(Error::InvalidIsochronousParameters);
}
let count = buffer_size.div_ceil(packet_size);
let mut iso_packets = vec![
usb_sys::usbdevfs_iso_packet_desc {
length: packet_size,
actual_length: 0,
status: 0,
};
count as usize
];
let last_entry = iso_packets
.last_mut()
.expect("there should be at least one entry for ISOC packet");
last_entry.length = buffer_size - packet_size * (count - 1);
let mut transfer = Self::new(
usb_sys::USBDEVFS_URB_TYPE_ISO,
endpoint,
buffer,
&iso_packets,
)?;
transfer.urb_mut().number_of_packets_or_stream_id = count;
transfer.urb_mut().flags = usb_sys::USBDEVFS_URB_ISO_ASAP;
Ok(transfer)
}
/// Get the status of a completed transfer.
pub fn status(&self) -> TransferStatus {
let status = self.urb().status;
if status == 0 {
TransferStatus::Completed
} else if status == -ENODEV || status == -ESHUTDOWN {
TransferStatus::NoDevice
} else if status == -ENOENT || status == -ECONNRESET {
TransferStatus::Cancelled
} else if status == -EPIPE {
TransferStatus::Stalled
} else {
TransferStatus::Error
}
}
/// Get the actual amount of data transferred, which may be less than
/// the original length.
pub fn actual_length(&self) -> usize {
self.urb().actual_length as usize
}
/// Set callback function for transfer completion.
pub fn set_callback<C: 'static + Fn(Transfer) + Send + Sync>(&mut self, cb: C) {
self.callback = Some(Box::new(cb));
}
}
impl TransferHandle {
/// Attempt to cancel the transfer associated with this `TransferHandle`.
/// Safe to call even if the transfer has already completed;
/// `Error::TransferAlreadyCompleted` will be returned in this case.
pub fn cancel(&self) -> Result<()> {
let _guard = self.cancel_lock.lock();
let rc_transfer = match self.weak_transfer.upgrade() {
None => return Err(Error::TransferAlreadyCompleted),
Some(rc_transfer) => rc_transfer,
};
let urb_ptr = rc_transfer.urb.as_ptr() as *mut usb_sys::usbdevfs_urb;
let fd = match self.fd.upgrade() {
None => return Err(Error::NoDevice),
Some(fd) => fd,
};
// SAFETY:
// Safe because fd is a valid usbdevfs file descriptor and we pass a valid
// pointer to a usbdevfs_urb structure.
if unsafe {
handle_eintr_errno!(base::ioctl_with_mut_ptr(
&*fd,
usb_sys::USBDEVFS_DISCARDURB,
urb_ptr
))
} < 0
{
return Err(Error::IoctlFailed(
usb_sys::USBDEVFS_DISCARDURB,
base::Error::last(),
));
}
Ok(())
}
}