#[cfg(feature = "serde_derive")]
use serde::Deserialize;
#[cfg(feature = "deno_ffi")]
use serde::Deserialize;
#[cfg(feature = "serde_derive")]
use serde::Serialize;
#[cfg(feature = "deno_ffi")]
use serde::Serialize;
use std::ops::DerefMut;
#[cfg(feature = "libusb")]
use rusb::UsbContext;
use core::convert::TryFrom;
#[cfg(feature = "libusb")]
pub use rusb;
pub mod constants;
mod descriptors;
#[cfg(feature = "deno_ffi")]
pub mod ffi;
use crate::constants::BOS_DESCRIPTOR_TYPE;
use crate::constants::GET_URL_REQUEST;
use crate::descriptors::parse_bos;
use crate::descriptors::parse_webusb_url;
#[cfg(feature = "deno_ffi")]
use deno_bindgen::deno_bindgen;
const EP_DIR_IN: u8 = 0x80;
const EP_DIR_OUT: u8 = 0x0;
#[derive(Debug, PartialEq)]
#[non_exhaustive]
pub enum Error {
#[cfg(feature = "libusb")]
Usb(rusb::Error),
NotFound,
InvalidState,
InvalidAccess,
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(feature = "libusb")]
impl From<rusb::Error> for Error {
fn from(err: rusb::Error) -> Self {
Self::Usb(err)
}
}
impl<T> From<Option<T>> for Error {
fn from(_: Option<T>) -> Self {
Self::NotFound
}
}
#[derive(Clone)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "camelCase"))]
pub struct UsbConfiguration {
configuration_name: Option<String>,
configuration_value: u8,
interfaces: Vec<UsbInterface>,
}
#[cfg(feature = "libusb")]
impl UsbConfiguration {
pub fn from(
config_descriptor: rusb::ConfigDescriptor,
handle: &rusb::DeviceHandle<rusb::Context>,
) -> Result<Self> {
Ok(UsbConfiguration {
configuration_name: match config_descriptor.description_string_index() {
None => None,
Some(idx) => Some(handle.read_string_descriptor_ascii(idx)?),
},
configuration_value: config_descriptor.number(),
interfaces: config_descriptor
.interfaces()
.map(|i| UsbInterface::from(i, &handle))
.collect::<Vec<UsbInterface>>(),
})
}
}
#[derive(Clone)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "camelCase"))]
pub struct UsbInterface {
interface_number: u8,
alternate: UsbAlternateInterface,
alternates: Vec<UsbAlternateInterface>,
claimed: bool,
}
#[cfg(feature = "libusb")]
impl UsbInterface {
pub fn from(
i: rusb::Interface,
handle: &rusb::DeviceHandle<rusb::Context>,
) -> Self {
UsbInterface {
interface_number: i.number(),
claimed: false,
alternate: {
let interface =
i.descriptors().find(|d| d.setting_number() == 0).unwrap();
UsbAlternateInterface::from(interface, &handle)
},
alternates: i
.descriptors()
.map(|interface| UsbAlternateInterface::from(interface, &handle))
.collect(),
}
}
}
#[derive(Copy, Clone)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "camelCase"))]
pub enum UsbEndpointType {
Bulk,
Interrupt,
Isochronous,
Control,
}
#[derive(Clone, PartialEq)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "lowercase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "lowercase"))]
pub enum Direction {
In,
Out,
}
#[derive(Clone)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "camelCase"))]
pub struct UsbEndpoint {
endpoint_number: u8,
direction: Direction,
r#type: UsbEndpointType,
packet_size: u16,
}
#[derive(Clone)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "camelCase"))]
pub struct UsbAlternateInterface {
pub alternate_setting: u8,
pub interface_class: u8,
pub interface_subclass: u8,
pub interface_protocol: u8,
pub interface_name: Option<String>,
pub endpoints: Vec<UsbEndpoint>,
}
#[cfg(feature = "libusb")]
impl UsbAlternateInterface {
pub fn from(
d: rusb::InterfaceDescriptor,
handle: &rusb::DeviceHandle<rusb::Context>,
) -> Self {
UsbAlternateInterface {
alternate_setting: d.setting_number(),
interface_class: d.class_code(),
interface_subclass: d.sub_class_code(),
interface_protocol: d.protocol_code(),
interface_name: d
.description_string_index()
.map(|idx| handle.read_string_descriptor_ascii(idx).unwrap()),
endpoints: d
.endpoint_descriptors()
.map(|e| UsbEndpoint {
endpoint_number: e.number(),
packet_size: e.max_packet_size(),
direction: match e.direction() {
rusb::Direction::In => Direction::In,
rusb::Direction::Out => Direction::Out,
},
r#type: match e.transfer_type() {
rusb::TransferType::Control => UsbEndpointType::Control,
rusb::TransferType::Isochronous => UsbEndpointType::Isochronous,
rusb::TransferType::Bulk => UsbEndpointType::Bulk,
rusb::TransferType::Interrupt => UsbEndpointType::Interrupt,
},
})
.collect(),
}
}
}
#[cfg(feature = "deno_ffi")]
macro_rules! get_device_handle {
($self: expr) => {
ffi::RESOURCES
.lock()
.unwrap()
.get_mut(&$self.rid)
.unwrap()
.lock()
.unwrap()
.device_handle
.as_mut()
};
}
#[cfg(not(feature = "deno_ffi"))]
macro_rules! get_device_handle {
($self: expr) => {
$self.device_handle.as_mut()
};
}
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "camelCase"))]
pub struct UsbDevice {
pub configurations: Vec<UsbConfiguration>,
pub configuration: Option<UsbConfiguration>,
pub device_class: u8,
pub device_subclass: u8,
pub device_protocol: u8,
pub device_version_major: u8,
pub device_version_minor: u8,
pub device_version_subminor: u8,
pub manufacturer_name: Option<String>,
pub product_id: u16,
pub product_name: Option<String>,
pub serial_number: Option<String>,
pub usb_version_major: u8,
pub usb_version_minor: u8,
pub usb_version_subminor: u8,
pub vendor_id: u16,
pub opened: bool,
#[cfg_attr(
feature = "serde_derive",
doc = "NOTE: Skipped during serde deserialization.",
serde(skip)
)]
pub url: Option<String>,
#[cfg(feature = "deno_ffi")]
pub rid: i32,
#[cfg_attr(feature = "serde_derive", serde(skip))]
#[cfg(feature = "libusb")]
#[cfg(not(feature = "deno_ffi"))]
device: rusb::Device<rusb::Context>,
#[cfg_attr(feature = "serde_derive", serde(skip))]
#[cfg(feature = "libusb")]
#[cfg(not(feature = "deno_ffi"))]
device_handle: Option<rusb::DeviceHandle<rusb::Context>>,
}
impl UsbDevice {
fn validate_control_setup(
&mut self,
setup: &UsbControlTransferParameters,
) -> Result<()> {
if let Some(configuration) = &self.configuration {
match setup.recipient {
UsbRecipient::Interface => {
let interface_number: u8 = (setup.index & 0xFF) as u8;
let interface = configuration
.interfaces
.iter()
.find(|itf| itf.interface_number == interface_number)
.ok_or(Error::NotFound)?;
if !interface.claimed {
return Err(Error::InvalidState);
}
}
UsbRecipient::Endpoint => {
let endpoint_number = setup.index as u8 & (1 << 4);
let direction = match (setup.index >> 8) & 1 {
1 => Direction::In,
_ => Direction::Out,
};
let interface = configuration
.interfaces
.iter()
.find(|itf| {
itf
.alternates
.iter()
.find(|alt| {
alt
.endpoints
.iter()
.find(|endpoint| {
endpoint.endpoint_number == endpoint_number
&& endpoint.direction == direction
})
.is_some()
})
.is_some()
})
.ok_or(Error::NotFound)?;
}
_ => {}
}
}
Ok(())
}
}
impl UsbDevice {
pub fn isochronous_transfer_in(&mut self) {
unimplemented!()
}
pub fn isochronous_transfer_out(&mut self) {
unimplemented!()
}
pub fn open(&mut self) -> Result<()> {
if self.opened {
return Ok(());
}
#[cfg(feature = "libusb")]
{
#[cfg(feature = "deno_ffi")]
{
let mut handle = ffi::RESOURCES
.lock()
.unwrap()
.get_mut(&self.rid)
.unwrap()
.lock()
.unwrap()
.device
.open()?;
ffi::RESOURCES
.lock()
.unwrap()
.get_mut(&self.rid)
.unwrap()
.lock()
.unwrap()
.device_handle
.replace(handle);
}
#[cfg(not(feature = "deno_ffi"))]
{
let handle = self.device.open()?;
self.device_handle = Some(handle);
}
}
self.opened = true;
Ok(())
}
pub fn close(&mut self) -> Result<()> {
if !self.opened {
return Ok(());
}
#[cfg(feature = "libusb")]
{
match get_device_handle!(self) {
Some(handle_ref) => {
drop(handle_ref);
}
None => unreachable!(),
};
#[cfg(not(feature = "deno_ffi"))]
{
self.device_handle = None;
}
}
self.opened = false;
Ok(())
}
pub fn select_configuration(
&mut self,
configuration_value: u8,
) -> Result<()> {
#[cfg(feature = "libusb")]
{
let configuration = match self
.configurations
.iter()
.position(|c| c.configuration_value == configuration_value)
{
Some(config_idx) => {
#[cfg(not(feature = "deno_ffi"))]
{
self.device.config_descriptor(config_idx as u8)?
}
#[cfg(feature = "deno_ffi")]
ffi::RESOURCES
.lock()
.unwrap()
.get_mut(&self.rid)
.unwrap()
.lock()
.unwrap()
.device
.config_descriptor(config_idx as u8)?
}
None => return Err(Error::NotFound),
};
if !self.opened {
return Err(Error::InvalidState);
}
let handle = match get_device_handle!(self) {
Some(ref mut handle_ref) => {
handle_ref.set_active_configuration(configuration_value)?;
handle_ref
}
None => unreachable!(),
};
self.configuration = Some(UsbConfiguration::from(
configuration,
&get_device_handle!(self).unwrap(),
)?);
}
Ok(())
}
pub fn claim_interface(&mut self, interface_number: u8) -> Result<()> {
#[cfg(feature = "libusb")]
{
let active_configuration =
self.configuration.as_mut().ok_or(Error::NotFound)?;
let mut interface = match active_configuration
.interfaces
.iter_mut()
.find(|i| i.interface_number == interface_number)
{
Some(i) => i,
None => return Err(Error::NotFound),
};
if !self.opened {
return Err(Error::InvalidState);
}
if interface.claimed {
return Ok(());
}
interface.claimed = true;
match get_device_handle!(self) {
Some(ref mut handle_ref) => {
handle_ref.claim_interface(interface_number)?;
}
None => unreachable!(),
};
}
Ok(())
}
pub fn release_interface(&mut self, interface_number: u8) -> Result<()> {
#[cfg(feature = "libusb")]
{
let active_configuration =
self.configuration.as_mut().ok_or(Error::NotFound)?;
let mut interface = match active_configuration
.interfaces
.iter_mut()
.find(|i| i.interface_number == interface_number)
{
Some(i) => i,
None => return Err(Error::NotFound),
};
if !self.opened {
return Err(Error::InvalidState);
}
if !interface.claimed {
return Ok(());
}
interface.claimed = false;
match get_device_handle!(self) {
Some(ref mut handle_ref) => {
handle_ref.release_interface(interface_number)?;
}
None => unreachable!(),
};
}
Ok(())
}
pub fn select_alternate_interface(
&mut self,
interface_number: u8,
alternate_setting: u8,
) -> Result<()> {
#[cfg(feature = "libusb")]
{
let active_configuration =
self.configuration.as_mut().ok_or(Error::NotFound)?;
let interface = match active_configuration
.interfaces
.iter_mut()
.find(|i| i.interface_number == interface_number)
{
Some(i) => i,
None => return Err(Error::NotFound),
};
if !self.opened || !interface.claimed {
return Err(Error::InvalidState);
}
match get_device_handle!(self) {
Some(ref mut handle_ref) => {
handle_ref
.set_alternate_setting(interface_number, alternate_setting)?;
}
None => unreachable!(),
};
}
return Ok(());
}
pub fn control_transfer_in(
&mut self,
setup: UsbControlTransferParameters,
length: usize,
) -> Result<Vec<u8>> {
#[cfg(feature = "libusb")]
{
if !self.opened {
return Err(Error::InvalidState);
}
self.validate_control_setup(&setup)?;
let mut buffer = vec![0u8; length];
let bytes_transferred = match get_device_handle!(self) {
Some(ref mut handle_ref) => {
let req = match setup.request_type {
UsbRequestType::Standard => rusb::RequestType::Standard,
UsbRequestType::Class => rusb::RequestType::Class,
UsbRequestType::Vendor => rusb::RequestType::Vendor,
};
let recipient = match setup.recipient {
UsbRecipient::Device => rusb::Recipient::Device,
UsbRecipient::Interface => rusb::Recipient::Interface,
UsbRecipient::Endpoint => rusb::Recipient::Endpoint,
UsbRecipient::Other => rusb::Recipient::Other,
};
let req_type =
rusb::request_type(rusb::Direction::In, req, recipient);
handle_ref.read_control(
req_type,
setup.request,
setup.value,
setup.index,
&mut buffer,
std::time::Duration::new(0, 0),
)?
}
None => unreachable!(),
};
let result = &buffer[0..bytes_transferred];
Ok(result.to_vec())
}
}
pub fn control_transfer_out(
&mut self,
setup: UsbControlTransferParameters,
data: &[u8],
) -> Result<usize> {
#[cfg(feature = "libusb")]
{
if !self.opened {
return Err(Error::InvalidState);
}
self.validate_control_setup(&setup)?;
let bytes_written = match get_device_handle!(self) {
Some(ref mut handle_ref) => {
let req = match setup.request_type {
UsbRequestType::Standard => rusb::RequestType::Standard,
UsbRequestType::Class => rusb::RequestType::Class,
UsbRequestType::Vendor => rusb::RequestType::Vendor,
};
let recipient = match setup.recipient {
UsbRecipient::Device => rusb::Recipient::Device,
UsbRecipient::Interface => rusb::Recipient::Interface,
UsbRecipient::Endpoint => rusb::Recipient::Endpoint,
UsbRecipient::Other => rusb::Recipient::Other,
};
let req_type =
rusb::request_type(rusb::Direction::Out, req, recipient);
handle_ref.write_control(
req_type,
setup.request,
setup.value,
setup.index,
data,
std::time::Duration::new(0, 0),
)?
}
None => unreachable!(),
};
Ok(bytes_written)
}
}
pub fn clear_halt(
&mut self,
direction: Direction,
endpoint_number: u8,
) -> Result<()> {
#[cfg(feature = "libusb")]
{
let active_configuration =
self.configuration.as_ref().ok_or(Error::NotFound)?;
let interface = active_configuration
.interfaces
.iter()
.find(|itf| {
itf
.alternates
.iter()
.find(|alt| {
alt
.endpoints
.iter()
.find(|endpoint| {
endpoint.endpoint_number == endpoint_number
&& endpoint.direction == direction
})
.is_some()
})
.is_some()
})
.ok_or(Error::NotFound)?;
if !self.opened || !interface.claimed {
return Err(Error::InvalidState);
}
match get_device_handle!(self) {
Some(ref mut handle_ref) => {
let mut endpoint = endpoint_number;
match direction {
Direction::In => endpoint |= EP_DIR_IN,
Direction::Out => endpoint |= EP_DIR_OUT,
};
handle_ref.clear_halt(endpoint)?
}
None => unreachable!(),
};
}
Ok(())
}
pub fn transfer_in(
&mut self,
endpoint_number: u8,
length: usize,
) -> Result<Vec<u8>> {
#[cfg(feature = "libusb")]
{
let endpoint = self
.configuration
.as_ref()
.ok_or(Error::NotFound)?
.interfaces
.iter()
.find_map(|itf| {
itf.alternates.iter().find_map(|alt| {
alt.endpoints.iter().find(|endpoint| {
endpoint.endpoint_number == endpoint_number
&& endpoint.direction == Direction::In
})
})
})
.ok_or(Error::NotFound)?;
match endpoint.r#type {
UsbEndpointType::Bulk | UsbEndpointType::Interrupt => {}
_ => return Err(Error::InvalidAccess),
}
if !self.opened {
return Err(Error::InvalidState);
}
let mut buffer = vec![0u8; length];
let ty = endpoint.r#type;
let bytes_transferred = match get_device_handle!(self) {
Some(ref mut handle_ref) => {
let endpoint_addr = EP_DIR_IN | endpoint_number;
match ty {
UsbEndpointType::Bulk => handle_ref.read_bulk(
endpoint_addr,
&mut buffer,
std::time::Duration::new(0, 0),
)?,
UsbEndpointType::Interrupt => handle_ref.read_interrupt(
endpoint_addr,
&mut buffer,
std::time::Duration::new(0, 0),
)?,
_ => unreachable!(),
}
}
None => unreachable!(),
};
let result = &buffer[0..bytes_transferred];
Ok(result.to_vec())
}
}
pub fn transfer_out(
&mut self,
endpoint_number: u8,
data: &[u8],
) -> Result<usize> {
#[cfg(feature = "libusb")]
{
let endpoint = self
.configuration
.as_ref()
.ok_or(Error::NotFound)?
.interfaces
.iter()
.find_map(|itf| {
itf.alternates.iter().find_map(|alt| {
alt.endpoints.iter().find(|endpoint| {
endpoint.endpoint_number == endpoint_number
&& endpoint.direction == Direction::Out
})
})
})
.ok_or(Error::NotFound)?;
match endpoint.r#type {
UsbEndpointType::Bulk | UsbEndpointType::Interrupt => {}
_ => return Err(Error::InvalidAccess),
}
if !self.opened {
return Err(Error::InvalidState);
}
let ty = endpoint.r#type;
let bytes_written = match get_device_handle!(self) {
Some(ref mut handle_ref) => {
let endpoint_addr = EP_DIR_OUT | endpoint_number;
match ty {
UsbEndpointType::Bulk => handle_ref.write_bulk(
endpoint_addr,
data,
std::time::Duration::new(0, 0),
)?,
UsbEndpointType::Interrupt => handle_ref.write_interrupt(
endpoint_addr,
data,
std::time::Duration::new(0, 0),
)?,
_ => unreachable!(),
}
}
None => unreachable!(),
};
Ok(bytes_written)
}
}
pub fn reset(&mut self) -> Result<()> {
#[cfg(feature = "libusb")]
{
if !self.opened {
return Err(Error::InvalidState);
}
match get_device_handle!(self) {
Some(ref mut handle_ref) => handle_ref.reset()?,
None => unreachable!(),
};
}
Ok(())
}
}
#[derive(Clone)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "lowercase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "lowercase"))]
pub enum UsbRequestType {
Standard,
Class,
Vendor,
}
#[derive(Clone, PartialEq)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "lowercase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "lowercase"))]
pub enum UsbRecipient {
Device,
Interface,
Endpoint,
Other,
}
#[derive(Clone)]
#[cfg_attr(
feature = "serde_derive",
derive(Serialize, Deserialize),
serde(rename_all = "camelCase")
)]
#[cfg_attr(feature = "deno_ffi", deno_bindgen, serde(rename_all = "camelCase"))]
pub struct UsbControlTransferParameters {
pub request_type: UsbRequestType,
pub recipient: UsbRecipient,
pub request: u8,
pub value: u16,
pub index: u16,
}
#[cfg(feature = "libusb")]
impl TryFrom<rusb::Device<rusb::Context>> for UsbDevice {
type Error = Error;
fn try_from(device: rusb::Device<rusb::Context>) -> Result<UsbDevice> {
let device_descriptor = device.device_descriptor()?;
let device_class = device_descriptor.class_code();
let usb_version = device_descriptor.usb_version();
let config_descriptor = device.active_config_descriptor();
let handle = device.open()?;
let read_bos_descriptors = usb_version.0 >= 2 && usb_version.1 >= 1;
let url = if read_bos_descriptors {
let request_type = rusb::request_type(
rusb::Direction::In,
rusb::RequestType::Standard,
rusb::Recipient::Device,
);
let kGetDescriptorRequest = 0x06;
let mut buffer = [0; 5];
let length = handle.read_control(
request_type,
kGetDescriptorRequest,
BOS_DESCRIPTOR_TYPE << 8,
0,
&mut buffer,
core::time::Duration::new(2, 0),
)?;
assert_eq!(length, 5);
let new_length = buffer[2] | (buffer[3].wrapping_shl(8));
let mut new_buffer = vec![0; new_length as usize];
let request_type = rusb::request_type(
rusb::Direction::In,
rusb::RequestType::Standard,
rusb::Recipient::Device,
);
handle.read_control(
request_type,
kGetDescriptorRequest,
BOS_DESCRIPTOR_TYPE << 8,
0,
&mut new_buffer,
core::time::Duration::new(2, 0),
)?;
if let Some((vendor_code, landing_page_id)) = parse_bos(&new_buffer) {
let mut buffer = [0; 255];
let request_type = rusb::request_type(
rusb::Direction::In,
rusb::RequestType::Vendor,
rusb::Recipient::Device,
);
handle.read_control(
request_type,
vendor_code,
landing_page_id as u16,
GET_URL_REQUEST,
&mut buffer,
core::time::Duration::new(2, 0),
)?;
let url = parse_webusb_url(&buffer);
url
} else {
None
}
} else {
None
};
let configuration = match config_descriptor {
Ok(config_descriptor) => {
UsbConfiguration::from(config_descriptor, &handle).ok()
}
Err(_) => None,
};
let num_configurations = device_descriptor.num_configurations();
let mut configurations: Vec<UsbConfiguration> = vec![];
for idx in 0..num_configurations {
if let Ok(curr_config_descriptor) = device.config_descriptor(idx) {
configurations
.push(UsbConfiguration::from(curr_config_descriptor, &handle)?);
}
}
let device_version = device_descriptor.device_version();
let manufacturer_name = handle
.read_manufacturer_string_ascii(&device_descriptor)
.ok();
let product_name =
handle.read_product_string_ascii(&device_descriptor).ok();
let serial_number = handle
.read_serial_number_string_ascii(&device_descriptor)
.ok();
#[cfg(feature = "deno_ffi")]
let rid = ffi::RESOURCES.lock().unwrap().len() as i32;
let usb_device = UsbDevice {
configurations,
configuration,
device_class,
device_subclass: device_descriptor.sub_class_code(),
device_protocol: device_descriptor.protocol_code(),
device_version_major: device_version.major(),
device_version_minor: device_version.minor(),
device_version_subminor: device_version.sub_minor(),
product_id: device_descriptor.product_id(),
usb_version_major: usb_version.major(),
usb_version_minor: usb_version.minor(),
usb_version_subminor: usb_version.sub_minor(),
vendor_id: device_descriptor.vendor_id(),
manufacturer_name,
product_name,
serial_number,
opened: false,
url,
#[cfg(not(feature = "deno_ffi"))]
device,
#[cfg(not(feature = "deno_ffi"))]
device_handle: None,
#[cfg(feature = "deno_ffi")]
rid, };
#[cfg(feature = "deno_ffi")]
ffi::insert_device(rid, device);
drop(handle);
Ok(usb_device)
}
}
#[cfg(feature = "libusb")]
pub struct Context(rusb::Context);
#[cfg(feature = "libusb")]
impl Context {
pub fn init() -> Result<Self> {
let ctx = rusb::Context::new()?;
Ok(Self(ctx))
}
pub fn devices(&self) -> Result<Vec<UsbDevice>> {
let devices = self.0.devices()?;
let usb_devices: Vec<UsbDevice> = devices
.iter()
.filter(|d| {
d.device_descriptor().is_ok()
&& d.device_descriptor().unwrap().class_code() != 9
})
.map(|d| UsbDevice::try_from(d))
.filter(|d| {
d.is_ok()
|| d.as_ref().err().unwrap() != &Error::Usb(rusb::Error::Access)
})
.map(|d| d.unwrap())
.collect::<Vec<UsbDevice>>();
Ok(usb_devices)
}
}
#[cfg(test)]
mod tests {
use crate::Context;
use crate::Direction;
use crate::Error;
use crate::UsbControlTransferParameters;
use crate::UsbDevice;
use crate::UsbRecipient;
use crate::UsbRequestType;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
fn test_device() -> UsbDevice {
let ctx = Context::init().unwrap();
let devices = ctx.devices().unwrap();
let device = devices.into_iter().find(|d| d.vendor_id == 0x2341 && d.product_id == 0x8036).expect("Device not found.\nhelp: ensure you follow the test setup instructions carefully");
device
}
#[test]
fn test_bos() -> crate::Result<()> {
let mut device = test_device();
assert_eq!(
device.url,
Some("https://webusb.github.io/arduino/demos/console".to_string())
);
Ok(())
}
#[test]
fn test_device_initial_state() -> crate::Result<()> {
let mut device = test_device();
device.open()?;
device.open()?;
device.close()?;
device.close()?;
Ok(())
}
#[test]
fn test_device_invalid_state() -> crate::Result<()> {
let mut device = test_device();
device.select_configuration(1).unwrap_err();
device.claim_interface(2).unwrap_err();
device.select_alternate_interface(2, 0).unwrap_err();
device
.control_transfer_out(
UsbControlTransferParameters {
request_type: crate::UsbRequestType::Class,
recipient: crate::UsbRecipient::Interface,
request: 0x22,
value: 0x01,
index: 2,
},
&[],
)
.unwrap_err();
device.transfer_out(4, b"H").unwrap_err();
device.clear_halt(Direction::Out, 4).unwrap_err();
device.transfer_out(4, b"L").unwrap_err();
device.clear_halt(Direction::Out, 4).unwrap_err();
device
.control_transfer_out(
UsbControlTransferParameters {
request_type: crate::UsbRequestType::Class,
recipient: crate::UsbRecipient::Interface,
request: 0x22,
value: 0x00,
index: 2,
},
&[],
)
.unwrap_err();
device.release_interface(2).unwrap_err();
device.reset().unwrap_err();
device.close()?;
Ok(())
}
#[flaky_test::flaky_test]
fn test_device_blink() {
fn test(device: &mut UsbDevice) {
device.transfer_out(4, b"H").unwrap();
device.clear_halt(Direction::Out, 4).unwrap();
device.transfer_out(4, b"L").unwrap();
device.clear_halt(Direction::Out, 4).unwrap();
let recv = device.transfer_in(5, 64).unwrap();
let mut first_run = false;
match recv.as_slice() {
b"Sketch begins.\r\n> " => {
first_run = true;
}
b"H\r\nTurning LED on.\r\n> " => {}
_ => unreachable!(),
};
let recv = device.transfer_in(5, 64).unwrap();
match (first_run, recv.as_slice()) {
(true, b"H\r\nTurning LED on.\r\n> ")
| (false, b"L\r\nTurning LED off.\r\n> ") => {}
_ => unreachable!(),
};
}
let mut device = test_device();
device.open().unwrap();
match device.select_configuration(1) {
Ok(_) => {} Err(crate::Error::Usb(rusb::Error::Busy))
| Err(crate::Error::InvalidState) => {}
_ => unreachable!(),
}
if device.claim_interface(2).is_ok() {
device.select_alternate_interface(2, 0).unwrap();
device
.control_transfer_out(
UsbControlTransferParameters {
request_type: crate::UsbRequestType::Class,
recipient: crate::UsbRecipient::Interface,
request: 0x22,
value: 0x01,
index: 2,
},
&[],
)
.unwrap();
test(&mut device);
device
.control_transfer_out(
UsbControlTransferParameters {
request_type: crate::UsbRequestType::Class,
recipient: crate::UsbRecipient::Interface,
request: 0x22,
value: 0x00,
index: 2,
},
&[],
)
.unwrap();
} else {
test(&mut device);
}
device.release_interface(2).unwrap();
device.reset().unwrap();
device.close().unwrap();
}
#[test]
fn test_device_control() {
async fn test(device: &mut UsbDevice) {
let device_descriptor_bytes = device
.control_transfer_in(
UsbControlTransferParameters {
request_type: crate::UsbRequestType::Standard,
recipient: crate::UsbRecipient::Device,
request: 0x06,
value: 0x01 << 8,
index: 0,
},
18,
)
.unwrap();
assert_eq!(device_descriptor_bytes.len(), 18);
assert_eq!(device_descriptor_bytes[0], 18);
let bcd_usb = u16::from_le_bytes([
device_descriptor_bytes[2],
device_descriptor_bytes[3],
]);
assert_eq!((bcd_usb >> 8) as u8, device.usb_version_major);
assert_eq!(((bcd_usb & 0xf0) >> 4) as u8, device.usb_version_minor);
assert_eq!((bcd_usb & 0xf) as u8, device.usb_version_subminor);
assert_eq!(device_descriptor_bytes[4], device.device_class);
assert_eq!(device_descriptor_bytes[5], device.device_subclass);
assert_eq!(device_descriptor_bytes[6], device.device_protocol);
let vendor_id = u16::from_le_bytes([
device_descriptor_bytes[8],
device_descriptor_bytes[9],
]);
assert_eq!(vendor_id, device.vendor_id);
let product_id = u16::from_le_bytes([
device_descriptor_bytes[10],
device_descriptor_bytes[11],
]);
assert_eq!(product_id, device.product_id);
let bcd_device = u16::from_le_bytes([
device_descriptor_bytes[12],
device_descriptor_bytes[13],
]);
assert_eq!((bcd_device >> 8) as u8, device.device_version_major);
assert_eq!(
((bcd_device & 0xf0) >> 4) as u8,
device.device_version_minor
);
assert_eq!((bcd_device & 0xf) as u8, device.device_version_subminor);
assert_eq!(
device_descriptor_bytes[17],
device.configurations.len() as u8
);
}
let mut device = test_device();
device.open().unwrap();
match device.select_configuration(1) {
Ok(_) => {} Err(crate::Error::Usb(rusb::Error::Busy))
| Err(crate::Error::InvalidState) => {}
_ => unreachable!(),
}
if device.claim_interface(2).is_ok() {
device.select_alternate_interface(2, 0).unwrap();
device
.control_transfer_out(
UsbControlTransferParameters {
request_type: crate::UsbRequestType::Class,
recipient: crate::UsbRecipient::Interface,
request: 0x22,
value: 0x01,
index: 2,
},
&[],
)
.unwrap();
test(&mut device);
device
.control_transfer_out(
UsbControlTransferParameters {
request_type: crate::UsbRequestType::Class,
recipient: crate::UsbRecipient::Interface,
request: 0x22,
value: 0x00,
index: 2,
},
&[],
)
.unwrap();
} else {
test(&mut device);
}
device.release_interface(2).unwrap();
device.reset().unwrap();
device.close().unwrap();
}
#[test]
#[should_panic]
fn test_unimplemented1() {
let mut device = test_device();
device.isochronous_transfer_in();
}
#[test]
#[should_panic]
fn test_unimplemented2() {
let mut device = test_device();
device.isochronous_transfer_out();
}
#[test]
fn test_device_not_found() -> crate::Result<()> {
let mut device = test_device();
device.open()?;
device.select_configuration(255).unwrap_err();
device.claim_interface(255).unwrap_err();
device.release_interface(255).unwrap_err();
device.select_alternate_interface(255, 0).unwrap_err();
device.close()?;
Ok(())
}
#[test]
fn test_validate_control_setup() {
let mut device = test_device();
device.open().unwrap();
fn standard_ctrl_req(device: &mut UsbDevice) -> crate::Result<()> {
device.validate_control_setup(&UsbControlTransferParameters {
request_type: UsbRequestType::Class,
recipient: UsbRecipient::Interface,
request: 0x22,
value: 0x01,
index: 2,
})
}
standard_ctrl_req(&mut device).unwrap_err();
device.claim_interface(2).unwrap();
standard_ctrl_req(&mut device).unwrap();
}
#[test]
fn test_error_impl() {
let nope: Option<()> = None;
assert_eq!(Error::from(nope), Error::NotFound);
}
}