Compare commits

..

No commits in common. "092cb0583adea570d6f662cd6b7acf616dac6239" and "54154d54c3ed9a0a4682c39707a7ba42ad800086" have entirely different histories.

4 changed files with 104 additions and 401 deletions

View file

@ -1,13 +1,7 @@
use crate::usb::{ use crate::usb::{ControlIn, ControlOut, ControlType, Device, Interface, Recipient, UsbError};
ControlIn, ControlOut, ControlType, Descriptor, Device, Interface, Recipient, UsbError,
};
pub struct UsbDescriptor {
device_info: nusb::DeviceInfo,
}
pub struct UsbDevice { pub struct UsbDevice {
device_info: UsbDescriptor, device_info: nusb::DeviceInfo,
device: nusb::Device, device: nusb::Device,
} }
@ -42,13 +36,15 @@ impl DeviceFilter {
} }
} }
pub async fn get_device(device_filters: Vec<DeviceFilter>) -> Result<UsbDescriptor, UsbError> { pub async fn get_device(device_filter: Vec<DeviceFilter>) -> Result<UsbDevice, UsbError> {
let devices = nusb::list_devices().unwrap(); let devices = nusb::list_devices().unwrap();
let mut device_info = None; let mut device_info = None;
for prelim_dev_inf in devices { for prelim_dev_inf in devices {
// See if the device exists in the list // See if the device exists in the list
if device_filters.iter().any(|info| { if device_filter
.iter()
.any(|info| {
let mut result = false; let mut result = false;
if info.vendor_id.is_some() { if info.vendor_id.is_some() {
@ -72,9 +68,10 @@ pub async fn get_device(device_filters: Vec<DeviceFilter>) -> Result<UsbDescript
} }
result result
}) { })
{
device_info = Some(prelim_dev_inf); device_info = Some(prelim_dev_inf);
break; break
} }
} }
@ -83,69 +80,39 @@ pub async fn get_device(device_filters: Vec<DeviceFilter>) -> Result<UsbDescript
None => return Err(UsbError::DeviceNotFound), None => return Err(UsbError::DeviceNotFound),
}; };
Ok(UsbDescriptor { device_info }) let device = match device_info.open() {
Ok(dev) => dev,
Err(_) => return Err(UsbError::CommunicationError),
};
Ok(UsbDevice {
device_info,
device,
})
} }
pub async fn get_device_list( impl Device for UsbDevice {
device_filters: Vec<DeviceFilter>, type UsbDevice = UsbDevice;
) -> Result<Vec<UsbDescriptor>, UsbError> { type UsbInterface = UsbInterface;
let devices_info = nusb::list_devices().unwrap();
let mut devices = Vec::new(); async fn open_interface(&self, number: u8) -> Result<UsbInterface, UsbError> {
for prelim_dev_inf in devices_info { let interface = match self.device.claim_interface(number) {
// See if the device exists in the list Ok(inter) => inter,
if device_filters.iter().any(|info| { Err(_) => return Err(UsbError::CommunicationError),
let mut result = false; };
if info.vendor_id.is_some() { Ok(UsbInterface { interface })
result = info.vendor_id.unwrap() == prelim_dev_inf.vendor_id();
} }
if info.product_id.is_some() { async fn reset(&self) -> Result<(), UsbError> {
result = info.product_id.unwrap() == prelim_dev_inf.product_id(); match self.device.reset() {
} Ok(_) => Ok(()),
Err(_) => Err(UsbError::CommunicationError),
if info.class.is_some() {
result = info.class.unwrap() == prelim_dev_inf.class();
}
if info.subclass.is_some() {
result = info.subclass.unwrap() == prelim_dev_inf.subclass();
}
if info.protocol.is_some() {
result = info.protocol.unwrap() == prelim_dev_inf.protocol();
}
result
}) {
devices.push(prelim_dev_inf);
} }
} }
if devices.is_empty() { async fn forget(&self) -> Result<(), UsbError> {
return Err(UsbError::DeviceNotFound); self.reset().await
}
let devices_opened: Vec<UsbDescriptor> = devices
.into_iter()
.map(|d| UsbDescriptor { device_info: d })
.collect();
Ok(devices_opened)
}
impl Descriptor for UsbDescriptor {
type Device = UsbDevice;
async fn open(self) -> Result<Self::Device, UsbError> {
match self.device_info.open() {
Ok(dev) => Ok(Self::Device {
device_info: self,
device: dev,
}),
Err(err) => Err(UsbError::CommunicationError(err.to_string())),
}
} }
async fn vendor_id(&self) -> u16 { async fn vendor_id(&self) -> u16 {
@ -173,69 +140,6 @@ impl Descriptor for UsbDescriptor {
} }
} }
impl Device for UsbDevice {
type Interface = UsbInterface;
async fn open_interface(&self, number: u8) -> Result<Self::Interface, UsbError> {
let interface = match self.device.claim_interface(number) {
Ok(inter) => inter,
Err(err) => return Err(UsbError::CommunicationError(err.to_string())),
};
Ok(UsbInterface { interface })
}
async fn detach_and_open_interface(&self, number: u8) -> Result<Self::Interface, UsbError> {
let interface = match self.device.detach_and_claim_interface(number) {
Ok(inter) => inter,
Err(err) => return Err(UsbError::CommunicationError(err.to_string())),
};
Ok(UsbInterface { interface })
}
async fn reset(&self) -> Result<(), UsbError> {
match self.device.reset() {
Ok(_) => Ok(()),
Err(err) => Err(UsbError::CommunicationError(err.to_string())),
}
}
async fn forget(&self) -> Result<(), UsbError> {
self.reset().await
}
async fn product_id(&self) -> u16 {
self.device_info.product_id().await
}
async fn vendor_id(&self) -> u16 {
self.device_info.vendor_id().await
}
async fn class(&self) -> u8 {
self.device_info.class().await
}
async fn subclass(&self) -> u8 {
self.device_info.subclass().await
}
async fn manufacturer_string(&self) -> Option<String> {
self.device_info.manufacturer_string().await
}
async fn product_string(&self) -> Option<String> {
self.device_info.product_string().await
}
}
impl Drop for UsbDevice {
fn drop(&mut self) {
let _ = self.device.reset();
}
}
impl<'a> Interface<'a> for UsbInterface { impl<'a> Interface<'a> for UsbInterface {
async fn control_in(&self, data: ControlIn) -> Result<Vec<u8>, UsbError> { async fn control_in(&self, data: ControlIn) -> Result<Vec<u8>, UsbError> {
let result = match self.interface.control_in(data.into()).await.into_result() { let result = match self.interface.control_in(data.into()).await.into_result() {

View file

@ -1,4 +1,4 @@
//#![cfg_attr(debug_assertions, allow(dead_code, unused_imports))] #![cfg_attr(debug_assertions, allow(dead_code, unused_imports))]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use js_sys::{Array, Object, Promise, Uint8Array}; use js_sys::{Array, Object, Promise, Uint8Array};
@ -9,14 +9,7 @@ use web_sys::{
}; };
// Crate stuff // Crate stuff
use crate::usb::{ use crate::usb::{ControlIn, ControlOut, ControlType, Device, Interface, Recipient, UsbError};
ControlIn, ControlOut, ControlType, Descriptor, Device, Interface, Recipient, UsbError,
};
#[wasm_bindgen]
pub struct UsbDescriptor {
device: WasmUsbDevice,
}
#[wasm_bindgen] #[wasm_bindgen]
pub struct UsbDevice { pub struct UsbDevice {
@ -58,7 +51,9 @@ impl DeviceFilter {
} }
#[wasm_bindgen] #[wasm_bindgen]
pub async fn get_device(device_filter: Vec<DeviceFilter>) -> Result<UsbDescriptor, js_sys::Error> { pub async fn get_device(
device_filter: Vec<DeviceFilter>,
) -> Result<UsbDevice, js_sys::Error> {
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let navigator = window.navigator(); let navigator = window.navigator();
@ -73,7 +68,9 @@ pub async fn get_device(device_filter: Vec<DeviceFilter>) -> Result<UsbDescripto
for js_device in device_list { for js_device in device_list {
let device: WasmUsbDevice = js_device.into(); let device: WasmUsbDevice = js_device.into();
if device_filter.iter().any(|info| { if device_filter
.iter()
.any(|info| {
let mut result = false; let mut result = false;
if info.vendor_id.is_some() { if info.vendor_id.is_some() {
@ -97,215 +94,74 @@ pub async fn get_device(device_filter: Vec<DeviceFilter>) -> Result<UsbDescripto
} }
result result
}) {
let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
return Ok(UsbDescriptor { device });
}
}
let arr = Array::new();
for filter in device_filter {
let js_filter = js_sys::Object::new();
if let Some(vid) = filter.vendor_id {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("vendorId"),
&JsValue::from(vid),
)
.unwrap();
}
if let Some(pid) = filter.product_id {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("productId"),
&JsValue::from(pid),
)
.unwrap();
}
if let Some(class) = filter.class {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("classCode"),
&JsValue::from(class),
)
.unwrap();
}
if let Some(subclass) = filter.subclass {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("subclassCode"),
&JsValue::from(subclass),
)
.unwrap();
}
if let Some(pro) = filter.protocol {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("protocolCode"),
&JsValue::from(pro),
)
.unwrap();
}
arr.push(&js_filter);
}
let filters = JsValue::from(&arr);
let filters2 = UsbDeviceRequestOptions::new(&filters);
let device: WasmUsbDevice = JsFuture::from(Promise::resolve(&usb.request_device(&filters2)))
.await?
.into();
let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
Ok(UsbDescriptor { device })
}
/*
#[wasm_bindgen]
pub async fn get_device_list(device_filter: Vec<DeviceFilter>) -> Result<Vec<UsbDescriptor>, js_sys::Error> {
let window = web_sys::window().unwrap();
let navigator = window.navigator();
let usb = navigator.usb();
let device_list: Array = match JsFuture::from(Promise::resolve(&usb.get_devices())).await {
Ok(list) => list.into(),
Err(_) => Array::new(),
};
let mut devices = Vec::new();
// Check if the device is already paired, if so, we don't need to request it again
for js_device in device_list {
let device: WasmUsbDevice = js_device.into();
if device_filter.iter().any(|info| {
let mut result = false;
if info.vendor_id.is_some() {
result = info.vendor_id.unwrap() == device.vendor_id();
}
if info.product_id.is_some() {
result = info.product_id.unwrap() == device.product_id();
}
if info.class.is_some() {
result = info.class.unwrap() == device.device_class();
}
if info.subclass.is_some() {
result = info.subclass.unwrap() == device.device_subclass();
}
if info.protocol.is_some() {
result = info.protocol.unwrap() == device.device_protocol();
}
result
}) {
let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
devices.push(UsbDescriptor { device });
}
}
let arr = Array::new();
for filter in device_filter {
let js_filter = js_sys::Object::new();
if let Some(vid) = filter.vendor_id {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("vendorId"),
&JsValue::from(vid),
)
.unwrap();
}
if let Some(pid) = filter.product_id {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("productId"),
&JsValue::from(pid),
)
.unwrap();
}
if let Some(class) = filter.class {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("classCode"),
&JsValue::from(class),
)
.unwrap();
}
if let Some(subclass) = filter.subclass {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("subclassCode"),
&JsValue::from(subclass),
)
.unwrap();
}
if let Some(pro) = filter.protocol {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("protocolCode"),
&JsValue::from(pro),
)
.unwrap();
}
arr.push(&js_filter);
}
let filters = JsValue::from(&arr);
let filters2 = UsbDeviceRequestOptions::new(&filters);
let device: WasmUsbDevice = JsFuture::from(Promise::resolve(&usb.request_device(&filters2)))
.await?
.into();
let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
devices.push(UsbDescriptor { device });
return Ok(devices);
}
*/
impl Descriptor for UsbDescriptor {
type Device = UsbDevice;
async fn open(self) -> Result<Self::Device, UsbError> {
Ok(Self::Device {
device: self.device,
}) })
{
let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
return Ok(UsbDevice { device });
}
} }
async fn product_id(&self) -> u16 { let arr = Array::new();
self.device.vendor_id() for filter in device_filter {
let js_filter = js_sys::Object::new();
if let Some(vid) = filter.vendor_id {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("vendorId"),
&JsValue::from(vid),
)
.unwrap();
}
if let Some(pid) = filter.product_id {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("productId"),
&JsValue::from(pid),
)
.unwrap();
}
if let Some(class) = filter.class {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("classCode"),
&JsValue::from(class),
)
.unwrap();
}
if let Some(subclass) = filter.subclass {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("subclassCode"),
&JsValue::from(subclass),
)
.unwrap();
}
if let Some(pro) = filter.protocol {
js_sys::Reflect::set(
&js_filter,
&JsValue::from_str("protocolCode"),
&JsValue::from(pro),
)
.unwrap();
}
arr.push(&js_filter);
} }
async fn vendor_id(&self) -> u16 { let filters = JsValue::from(&arr);
self.device.product_id() let filters2 = UsbDeviceRequestOptions::new(&filters);
}
async fn class(&self) -> u8 { let device: WasmUsbDevice = JsFuture::from(Promise::resolve(&usb.request_device(&filters2)))
self.device.device_class() .await?
} .into();
async fn subclass(&self) -> u8 { let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
self.device.device_subclass()
}
async fn manufacturer_string(&self) -> Option<String> { Ok(UsbDevice { device })
self.device.manufacturer_name()
}
async fn product_string(&self) -> Option<String> {
self.device.product_name()
}
} }
impl Device for UsbDevice { impl Device for UsbDevice {
type Interface = UsbInterface; type UsbDevice = UsbDevice;
type UsbInterface = UsbInterface;
async fn open_interface(&self, number: u8) -> Result<UsbInterface, UsbError> { async fn open_interface(&self, number: u8) -> Result<UsbInterface, UsbError> {
let dev_promise = let dev_promise =
@ -314,10 +170,8 @@ impl Device for UsbDevice {
// Wait for the interface to be claimed // Wait for the interface to be claimed
let _device: WasmUsbDevice = match dev_promise { let _device: WasmUsbDevice = match dev_promise {
Ok(dev) => dev.into(), Ok(dev) => dev.into(),
Err(err) => { Err(_) => {
return Err(UsbError::CommunicationError( return Err(UsbError::CommunicationError);
err.as_string().unwrap_or_default(),
));
} }
}; };
@ -327,18 +181,12 @@ impl Device for UsbDevice {
}) })
} }
async fn detach_and_open_interface(&self, number: u8) -> Result<Self::Interface, UsbError> {
self.open_interface(number).await
}
async fn reset(&self) -> Result<(), UsbError> { async fn reset(&self) -> Result<(), UsbError> {
let result = JsFuture::from(Promise::resolve(&self.device.reset())).await; let result = JsFuture::from(Promise::resolve(&self.device.reset())).await;
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => Err(UsbError::CommunicationError( Err(_) => Err(UsbError::CommunicationError),
err.as_string().unwrap_or_default(),
)),
} }
} }
@ -347,9 +195,7 @@ impl Device for UsbDevice {
match result { match result {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(err) => Err(UsbError::CommunicationError( Err(_) => Err(UsbError::CommunicationError),
err.as_string().unwrap_or_default(),
)),
} }
} }

View file

@ -9,34 +9,24 @@
//! //!
//! When a [UsbInterface] is dropped, it is automatically released. //! When a [UsbInterface] is dropped, it is automatically released.
//! //!
//! ### CURRENT LIMITATIONS:
//! * Hotplug support is not implemented. Waiting on [hotplug support in nusb](https://github.com/kevinmehall/nusb/pull/20).
//!
//! * Until [this pull request](https://github.com/rustwasm/wasm-bindgen/issues/3155)
//! is merged into wasm bindgen, getting a list of USB devices is not possible on WASM
//! targets. However, this isn't a huge deal as the user gets a list to select from anyway.
//!
//! ## Example: //! ## Example:
//! ```no_run //! ```no_run
//! # tokio_test::block_on(async { //! # tokio_test::block_on(async {
//! use cross_usb::usb::{Descriptor, Device, Interface, Recipient, ControlType, ControlIn}; //! use cross_usb::usb::{Device, Interface, Recipient, ControlType, ControlIn};
//! use cross_usb::device_filter; //! use cross_usb::device_filter;
//! //!
//! // Obtain a device descriptor (UsbDescriptor) using a DeviceFilter, //! // Obtain a device using its VendorID and ProductID
//! // in this case with its VendorID and ProductID //! let filter = vec![
//! let filters = vec![
//! device_filter!{vendor_id: 0x054c, product_id: 0x00c9} //! device_filter!{vendor_id: 0x054c, product_id: 0x00c9}
//! ]; //! ];
//! let dev_descriptor = cross_usb::get_device(filters).await.expect("Failed to find device");
//! //!
//! // Open the device that the descriptor is describing //! let device = cross_usb::get_device(filter).await.expect("Failed to get device");
//! let dev = dev_descriptor.open().await.expect("Failed to open device");
//! //!
//! // Obtain an interface of the device //! // Obtain an interface of the device
//! let interface = dev.open_interface(0).await.expect("Failed to open interface"); //! let interface = device.open_interface(0).await.expect("Failed to open interface");
//! //!
//! // Send a Control transfer to the device, obtaining //! // Send a Control transfer to the device, obtaining
//! // the result and storing it in `result` //! // the result and storing it in `result`, and you're done!
//! let result = interface.control_in(ControlIn { //! let result = interface.control_in(ControlIn {
//! control_type: ControlType::Vendor, //! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface, //! recipient: Recipient::Interface,
@ -51,19 +41,16 @@
//! ``` //! ```
pub mod usb; pub mod usb;
/// The context contains the platform specific implementation of the USB transfers
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
#[path = "./backend/native.rs"] #[path = "./backend/native.rs"]
/// The context contains the platform specific implementation of the USB transfers
mod context; mod context;
#[cfg(target_family = "wasm")] #[cfg(target_family = "wasm")]
#[path = "./backend/wasm.rs"] #[path = "./backend/wasm.rs"]
/// The context contains the platform specific implementation of the USB transfers
mod context; mod context;
#[doc(inline)]
/// An implementation of a USB device descriptor
pub use crate::context::UsbDescriptor;
#[doc(inline)] #[doc(inline)]
/// An implementation of a USB device /// An implementation of a USB device
pub use crate::context::UsbDevice; pub use crate::context::UsbDevice;
@ -77,13 +64,14 @@ pub use crate::context::UsbInterface;
#[doc(inline)] #[doc(inline)]
pub use crate::context::DeviceFilter; pub use crate::context::DeviceFilter;
/// Gets a single device descriptor ([UsbDescriptor]) from a list of VendorID and ProductIDs /// Gets a single device from a list of VendorID and ProductIDs
/// ///
/// ## Example /// ## Example
/// ```no_run /// ```no_run
/// # tokio_test::block_on(async { /// # tokio_test::block_on(async {
/// use cross_usb::{get_device, DeviceFilter, device_filter}; /// use cross_usb::{get_device, DeviceFilter, device_filter};
/// ///
///
/// let filter = vec![ /// let filter = vec![
/// device_filter!{vendor_id: 0x054c, product_id: 0x00c9}, /// device_filter!{vendor_id: 0x054c, product_id: 0x00c9},
/// device_filter!{vendor_id: 0x054c}, /// device_filter!{vendor_id: 0x054c},
@ -95,11 +83,6 @@ pub use crate::context::DeviceFilter;
#[doc(inline)] #[doc(inline)]
pub use crate::context::get_device; pub use crate::context::get_device;
/// Gets a list of devices from a list of VendorID and ProductIDs
#[cfg(not(target_family = "wasm"))]
#[doc(inline)]
pub use crate::context::get_device_list;
/// Macro to create a device filter more easily. /// Macro to create a device filter more easily.
/// ///
/// The only valid keys are fields of the [DeviceFilter] struct. /// The only valid keys are fields of the [DeviceFilter] struct.

View file

@ -2,49 +2,19 @@
//! This module contains the traits and associated functions and //! This module contains the traits and associated functions and
//! structs which allow for USB communication. //! structs which allow for USB communication.
use crate::context::UsbInterface;
use thiserror::Error; use thiserror::Error;
pub trait Descriptor {
/// A unique USB Device
type Device;
/// Opens the USB connection, returning a [Self::Device]
async fn open(self) -> Result<Self::Device, UsbError>;
/// 16 bit device Product ID
async fn product_id(&self) -> u16;
/// 16 bit device Vendor ID
async fn vendor_id(&self) -> u16;
/// Device standard class
async fn class(&self) -> u8;
/// Device standard subclass
async fn subclass(&self) -> u8;
/// Get the manufacturer string string of the device, if available without device IO
///
/// Not available on Windows
async fn manufacturer_string(&self) -> Option<String>;
/// Get the product string of the device, if available without device IO
async fn product_string(&self) -> Option<String>;
}
/// A unique USB device /// A unique USB device
pub trait Device { pub trait Device {
/// A unique USB Device
type UsbDevice;
/// A unique Interface on a USB Device /// A unique Interface on a USB Device
type Interface; type UsbInterface;
/// Open a specific interface of the device /// Open a specific interface of the device
async fn open_interface(&self, number: u8) -> Result<Self::Interface, UsbError>; async fn open_interface(&self, number: u8) -> Result<UsbInterface, UsbError>;
/// Open a specific interface of the device, detaching any
/// kernel drivers and claiming it.
///
/// **Note:** This only has an effect on Native, and only on Linux.
async fn detach_and_open_interface(&self, number: u8) -> Result<Self::Interface, UsbError>;
/// Reset the device, which causes it to no longer be usable. You must /// Reset the device, which causes it to no longer be usable. You must
/// request a new device with [crate::get_device] /// request a new device with [crate::get_device]
@ -52,7 +22,7 @@ pub trait Device {
/// Remove the device from the paired devices list, causing it to no longer be usable. You must request to reconnect using [crate::get_device] /// Remove the device from the paired devices list, causing it to no longer be usable. You must request to reconnect using [crate::get_device]
/// ///
/// **Note:** On Native this simply resets the device. /// **Note: on Native with `nusb` this simply resets the device**
async fn forget(&self) -> Result<(), UsbError>; async fn forget(&self) -> Result<(), UsbError>;
/// 16 bit device Product ID /// 16 bit device Product ID
@ -117,7 +87,7 @@ pub enum UsbError {
TransferError, TransferError,
#[error("device communication failed")] #[error("device communication failed")]
CommunicationError(String), CommunicationError,
#[error("device disconnected")] #[error("device disconnected")]
Disconnected, Disconnected,