use log::{debug, error, info, trace, warn};
use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};
use tokio::sync::oneshot;
use libusb1_sys::constants::*;
use libusb1_sys::{
libusb_cancel_transfer, libusb_free_transfer, libusb_submit_transfer, libusb_transfer,
};
use wasmtime::component::Resource;
use crate::usb::WasiUsbCtxView;
use crate::usb::bindings::wasi::usb;
use usb::errors::LibusbError;
use usb::transfers::{Transfer, TransferSetup};
#[derive(Debug)]
pub struct UsbTransfer {
pub transfer: *mut libusb_transfer,
pub completed: Arc<AtomicBool>,
pub buffer: Option<Box<[u8]>>,
pub buf_len: u32,
pub receiver: Option<oneshot::Receiver<Result<Vec<u8>, LibusbError>>>,
pub control_setup: Option<TransferSetup>,
}
unsafe impl Send for UsbTransfer {}
struct TransferContext {
sender: oneshot::Sender<Result<Vec<u8>, LibusbError>>,
completed: Arc<AtomicBool>,
#[allow(dead_code)]
buffer: Box<[u8]>,
}
impl usb::transfers::Host for WasiUsbCtxView<'_> {
async fn await_transfer(
&mut self,
xfer: Resource<UsbTransfer>,
) -> Result<Vec<u8>, LibusbError> {
info!("Awaiting transfer");
let usb_transfer = self.table.get_mut(&xfer).expect("Failed to get transfer");
if usb_transfer.receiver.is_none() {
error!("Transfer receiver not set");
return Err(LibusbError::NotFound);
}
let receiver = usb_transfer.receiver.take().ok_or(LibusbError::NotFound)?;
info!("Transfer receiver set");
let result = match receiver.await {
Ok(result) => {
info!("Transfer result: {:?}", result);
result
}
Err(_) => Err(LibusbError::Interrupted),
};
self.table.delete(xfer).ok();
result
}
}
impl usb::transfers::HostTransfer for WasiUsbCtxView<'_> {
fn submit_transfer(
&mut self,
self_: Resource<Transfer>,
data: Vec<u8>,
) -> Result<(), LibusbError> {
debug!("Submit transfer");
let usb_transfer = self.table.get_mut(&self_).expect("Failed to get transfer");
debug!("Transfer: {:?}", usb_transfer);
let transfer_ptr = usb_transfer.transfer;
if usb_transfer.completed.load(Ordering::SeqCst) {
warn!("Transfer already completed");
return Err(LibusbError::Busy);
}
unsafe {
let transfer_type = (*transfer_ptr).transfer_type;
debug!("Transfer type: {:?}", transfer_type);
if transfer_type == LIBUSB_TRANSFER_TYPE_CONTROL {
let setup_buf = (*transfer_ptr).buffer;
if !setup_buf.is_null() {
let bm_request_type = usb_transfer.control_setup.unwrap().bm_request_type;
let direction_in = bm_request_type & 0x80 != 0;
if direction_in {
debug!("Control transfer IN");
} else {
debug!("Control transfer OUT");
if data.len() as u32 != usb_transfer.buf_len {
error!(
"Invalid data length for control transfer OUT: {}, expected {}",
data.len(),
usb_transfer.buf_len
);
return Err(LibusbError::InvalidParam);
}
let buf_ptr = (*transfer_ptr).buffer;
if !buf_ptr.is_null() {
debug!("Copying data to control transfer OUT buffer");
std::ptr::copy_nonoverlapping(
data.as_ptr(),
setup_buf.add(8),
data.len(),
);
}
}
}
} else if (*transfer_ptr).endpoint & 0x80 != 0 {
info!("IN transfer");
} else {
info!("OUT transfer");
if data.len() as u32 != usb_transfer.buf_len {
error!(
"Invalid data length for OUT transfer: {}, expected {}",
data.len(),
usb_transfer.buf_len
);
return Err(LibusbError::InvalidParam);
}
let buf_ptr = (*transfer_ptr).buffer;
if !buf_ptr.is_null() {
debug!("Copying data to OUT transfer buffer");
std::ptr::copy_nonoverlapping(data.as_ptr(), buf_ptr, data.len());
}
}
debug!("creating transfer context");
let (sender, receiver) = oneshot::channel();
let buffer_box = usb_transfer.buffer.take().expect("buffer not allocated");
let ctx = Box::new(TransferContext {
sender,
completed: usb_transfer.completed.clone(),
buffer: buffer_box,
});
(*transfer_ptr).user_data = Box::into_raw(ctx) as *mut _;
(*transfer_ptr).callback = transfer_callback;
debug!("submitting transfer: {:?}", transfer_ptr);
let submit_result = libusb_submit_transfer(transfer_ptr);
if submit_result < 0 {
error!(
"Failed to submit transfer: {}",
LibusbError::from_raw(submit_result)
);
let _ = Box::from_raw((*transfer_ptr).user_data as *mut TransferContext);
(*transfer_ptr).callback = empty_callback;
(*transfer_ptr).user_data = std::ptr::null_mut();
return Err(LibusbError::from_raw(submit_result));
} else {
debug!("transfer submitted");
let transfer_mut = self.table.get_mut(&self_).expect("Failed to get transfer");
transfer_mut.receiver = Some(receiver);
}
}
Ok(())
}
fn cancel_transfer(&mut self, self_: Resource<Transfer>) -> Result<(), LibusbError> {
let usb_transfer = self.table.get(&self_).expect("Failed to get transfer");
let transfer_ptr = usb_transfer.transfer;
unsafe {
if !usb_transfer.completed.load(Ordering::SeqCst) {
let res = libusb_cancel_transfer(transfer_ptr);
if res < 0 {
return Err(LibusbError::from_raw(res));
}
}
}
Ok(())
}
fn drop(&mut self, rep: Resource<Transfer>) -> wasmtime::Result<()> {
trace!("Drop transfer");
if let Ok(transfer) = self.table.get(&rep) {
unsafe {
if !transfer.completed.load(Ordering::SeqCst) {
let _ = libusb_cancel_transfer(transfer.transfer);
} else {
}
}
}
Ok(())
}
}
#[allow(dead_code)]
extern "system" fn transfer_callback(transfer: *mut libusb_transfer) {
unsafe {
let ctx_ptr = (*transfer).user_data as *mut TransferContext;
let ctx = Box::from_raw(ctx_ptr);
let status = (*transfer).status;
let result: Result<Vec<u8>, LibusbError> = if status == LIBUSB_TRANSFER_COMPLETED {
let mut data_vec = Vec::new();
if (*transfer).num_iso_packets > 0 {
let num_packets = (*transfer).num_iso_packets as usize;
let mut total_len: usize = 0;
for i in 0..num_packets {
let desc = (*transfer).iso_packet_desc.as_ptr().add(i);
total_len += (*desc).actual_length as usize;
}
let buf_ptr = (*transfer).buffer;
if !buf_ptr.is_null() && total_len > 0 {
let data_slice = std::slice::from_raw_parts(buf_ptr, total_len);
data_vec = data_slice.to_vec();
}
} else if (*transfer).transfer_type == LIBUSB_TRANSFER_TYPE_CONTROL {
let actual_len = (*transfer).actual_length as usize;
debug!(
"Control transfer completed with actual length: {}",
actual_len
);
let buf_ptr = (*transfer).buffer;
let bm_request_type = if !buf_ptr.is_null() { *buf_ptr } else { 0 };
let is_device_to_host = (bm_request_type & 0x80) != 0;
if is_device_to_host && actual_len > 0 {
if !buf_ptr.is_null() {
let data_slice = std::slice::from_raw_parts(buf_ptr.add(8), actual_len);
data_vec = data_slice.to_vec();
debug!("Control IN transfer data: {:?}", data_vec);
}
} else {
data_vec = Vec::new();
}
} else {
let actual_len = (*transfer).actual_length as usize;
if (*transfer).endpoint & 0x80 != 0 {
if actual_len > 0 {
let buf_ptr = (*transfer).buffer;
if !buf_ptr.is_null() {
let data_slice = std::slice::from_raw_parts(buf_ptr, actual_len);
data_vec = data_slice.to_vec();
}
}
} else {
data_vec = Vec::new();
}
}
Ok(data_vec)
} else {
let err = match status {
LIBUSB_TRANSFER_TIMED_OUT => LibusbError::Timeout,
LIBUSB_TRANSFER_CANCELLED => LibusbError::Interrupted,
LIBUSB_TRANSFER_STALL => LibusbError::Pipe,
LIBUSB_TRANSFER_NO_DEVICE => LibusbError::NoDevice,
LIBUSB_TRANSFER_OVERFLOW => LibusbError::Overflow,
LIBUSB_TRANSFER_ERROR => LibusbError::Io,
_ => LibusbError::Other,
};
Err(err)
};
ctx.completed.store(true, Ordering::SeqCst);
let _ = ctx.sender.send(result);
libusb_free_transfer(transfer);
}
}
#[allow(dead_code)]
extern "system" fn empty_callback(_transfer: *mut libusb_transfer) {}