use std::time::Duration;
use nusb::{
Interface,
transfer::{ControlIn, ControlOut, ControlType, Recipient, RequestBuffer},
};
use tracing::debug;
use super::device::find_device;
use super::types::UsbError;
pub struct UsbHandle {
interface: Interface,
endpoint_in: Option<u8>,
endpoint_out: Option<u8>,
}
impl UsbHandle {
pub async fn open(
vendor_id: u16,
product_id: u16,
interface_number: u8,
) -> Result<Self, UsbError> {
let info = find_device(vendor_id, product_id)?;
let device = info
.open()
.map_err(|e| UsbError::OpenFailed(e.to_string()))?;
let interface = device
.claim_interface(interface_number)
.map_err(|e| UsbError::ClaimFailed(interface_number, e.to_string()))?;
let (endpoint_in, endpoint_out) = discover_bulk_endpoints(&interface);
debug!(
"Opened {vendor_id:04x}:{product_id:04x} iface={interface_number} \
bulk_in={endpoint_in:?} bulk_out={endpoint_out:?}"
);
Ok(Self {
interface,
endpoint_in,
endpoint_out,
})
}
pub async fn control_in(
&self,
request_type: ControlType,
recipient: Recipient,
request: u8,
value: u16,
index: u16,
length: u16,
) -> Result<Vec<u8>, UsbError> {
let ctrl = ControlIn {
control_type: request_type,
recipient,
request,
value,
index,
length,
};
self.interface
.control_in(ctrl)
.await
.into_result()
.map_err(|e| UsbError::TransferFailed {
endpoint: 0x00,
reason: e.to_string(),
})
}
pub async fn control_out(
&self,
request_type: ControlType,
recipient: Recipient,
request: u8,
value: u16,
index: u16,
data: Vec<u8>,
) -> Result<(), UsbError> {
let ctrl = ControlOut {
control_type: request_type,
recipient,
request,
value,
index,
data: &data,
};
self.interface
.control_out(ctrl)
.await
.into_result()
.map_err(|e| UsbError::TransferFailed {
endpoint: 0x00,
reason: e.to_string(),
})?;
Ok(())
}
pub async fn bulk_read(
&self,
endpoint: Option<u8>,
length: usize,
_timeout: Duration,
) -> Result<Vec<u8>, UsbError> {
let ep = endpoint
.or(self.endpoint_in)
.ok_or_else(|| UsbError::Other("no bulk IN endpoint available".into()))?;
self.interface
.bulk_in(ep, RequestBuffer::new(length))
.await
.into_result()
.map_err(|e| UsbError::TransferFailed {
endpoint: ep,
reason: e.to_string(),
})
}
pub async fn bulk_write(
&self,
endpoint: Option<u8>,
data: Vec<u8>,
_timeout: Duration,
) -> Result<usize, UsbError> {
let ep = endpoint
.or(self.endpoint_out)
.ok_or_else(|| UsbError::Other("no bulk OUT endpoint available".into()))?;
let len = data.len();
self.interface
.bulk_out(ep, data)
.await
.into_result()
.map_err(|e| UsbError::TransferFailed {
endpoint: ep,
reason: e.to_string(),
})?;
Ok(len)
}
pub async fn interrupt_read(
&self,
endpoint: u8,
length: usize,
_timeout: Duration,
) -> Result<Vec<u8>, UsbError> {
self.interface
.interrupt_in(endpoint, RequestBuffer::new(length))
.await
.into_result()
.map_err(|e| UsbError::TransferFailed {
endpoint,
reason: e.to_string(),
})
}
pub async fn interrupt_write(
&self,
endpoint: u8,
data: Vec<u8>,
_timeout: Duration,
) -> Result<usize, UsbError> {
let len = data.len();
self.interface
.interrupt_out(endpoint, data)
.await
.into_result()
.map_err(|e| UsbError::TransferFailed {
endpoint,
reason: e.to_string(),
})?;
Ok(len)
}
pub fn bulk_in_endpoint(&self) -> Option<u8> {
self.endpoint_in
}
pub fn bulk_out_endpoint(&self) -> Option<u8> {
self.endpoint_out
}
}
fn discover_bulk_endpoints(interface: &Interface) -> (Option<u8>, Option<u8>) {
let mut ep_in: Option<u8> = None;
let mut ep_out: Option<u8> = None;
if let Some(alt) = interface.descriptors().next() {
for ep in alt.endpoints() {
use nusb::transfer::EndpointType;
if ep.transfer_type() == EndpointType::Bulk {
if ep.direction() == nusb::transfer::Direction::In {
ep_in.get_or_insert(ep.address());
} else {
ep_out.get_or_insert(ep.address());
}
}
}
}
(ep_in, ep_out)
}