diff --git a/Cargo.toml b/Cargo.toml index 62fa13a..9552c2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ features = [ nusb = "0.1.5" [dev-dependencies] -futures-lite = "1.13.0" +tokio-test = "0.4.3" [profile.release] opt-level = "s" diff --git a/src/backend/native.rs b/src/backend/native.rs index aaf5efc..d25092d 100644 --- a/src/backend/native.rs +++ b/src/backend/native.rs @@ -35,6 +35,40 @@ pub async fn get_device(vendor_id: u16, product_id: u16) -> Result) -> Result> { + let devices = nusb::list_devices().unwrap(); + + let mut device_info = None; + for device in devices { + match device_filter.iter().position(|i| i == &FilterTuple(device.vendor_id(), device.product_id())) { + Some(_) => { + device_info = Some(device); + break; + }, + None => device_info = None, + } + } + + if device_info.is_none() { + return Err("No devices from the list found".into()) + } + + let device_info = match device_info { + Some(dev) => dev, + None => return Err("Device not found".into()), + }; + + let device = device_info.open()?; + + Ok(UsbDevice { + device_info, + device, + }) +} + impl Device for UsbDevice { type UsbDevice = UsbDevice; type UsbInterface = UsbInterface; diff --git a/src/backend/wasm.rs b/src/backend/wasm.rs index 90221e3..e6b391b 100644 --- a/src/backend/wasm.rs +++ b/src/backend/wasm.rs @@ -29,6 +29,20 @@ pub async fn get_device(vendor_id: u16, product_id: u16) -> Result Result dev.into(), - Err(err) => { - console::log_1(&err.clone()); - return Err(err.into()); + let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?; + + Ok(UsbDevice { device }) +} + +#[wasm_bindgen] +#[derive(PartialEq, Eq, Clone)] +pub struct FilterTuple(pub u16, pub u16); + +#[wasm_bindgen] +pub async fn get_device_filter(device_filter: Vec) -> Result { + let window = web_sys::window().unwrap(); + + let navigator = window.navigator(); + let usb = navigator.usb(); + + let device_list: Array = JsFuture::from(Promise::resolve(&usb.get_devices())).await?.into(); + // 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(); + + for filter_info in &device_filter { + if device.vendor_id() == filter_info.0 && device.product_id() == filter_info.1 { + let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?; + + return Ok(UsbDevice { + device + }) + } } - }; + } + + let arr = Array::new(); + for device in device_filter { + let filter1 = js_sys::Object::new(); + js_sys::Reflect::set( + &filter1, + &JsValue::from_str("vendorId"), + &JsValue::from(device.0), + ) + .unwrap(); + js_sys::Reflect::set( + &filter1, + &JsValue::from_str("productId"), + &JsValue::from(device.1), + ) + .unwrap(); + arr.push(&filter1); + } + + 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?; diff --git a/src/lib.rs b/src/lib.rs index 5594ee6..298d911 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,17 +5,18 @@ //! //! ## Example: //! ```no_run -//! use cross_usb::usb::{Device, Recipient, ControlType, ControlIn}; +//! # tokio_test::block_on(async { +//! use cross_usb::usb::{Device, Interface, Recipient, ControlType, ControlIn}; //! //! // Obtain a device using its VendorID and ProductID -//! let device = cross_usb::get_device(0x054c, 0x0186).await.expect(""); +//! let device = cross_usb::get_device(0x054c, 0x0186).await.expect("Failed to get device"); //! //! // Obtain an interface of the device -//! let interface = usb_device.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 //! // the result and storing it in `result`, and you're done! -//! let result = match interface.control_in(ControlIn { +//! let result = interface.control_in(ControlIn { //! control_type: ControlType::Vendor, //! recipient: Recipient::Interface, //! request: 0x01, @@ -25,6 +26,7 @@ //! }) //! .await //! .expect("Sending control transfer failed"); +//! # }) //! ``` pub mod usb; @@ -46,6 +48,29 @@ pub use crate::context::UsbDevice; /// An implementation of a USB interface pub use crate::context::UsbInterface; +/// A single Device ID and Product ID pair to find when looking +/// for new USB devices using [get_device_filter] +#[doc(inline)] +pub use crate::context::FilterTuple; + /// Gets a single device from the VendorID and ProductID #[doc(inline)] pub use crate::context::get_device; + +/// Gets a single device from a list of VendorID and ProductIDs +/// +/// ## Example +/// ```no_run +/// # tokio_test::block_on(async { +/// use cross_usb::{get_device_filter, FilterTuple}; +/// +/// let filter = vec![ +/// FilterTuple(0x054c, 0x00c9), +/// FilterTuple(0x054c, 0x0186), +/// ]; +/// +/// let device = get_device_filter(filter).await.expect("Could not find device in list"); +/// # }) +/// ``` +#[doc(inline)] +pub use crate::context::get_device_filter; diff --git a/src/usb.rs b/src/usb.rs index 0cabaa2..d853d8b 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -1,7 +1,6 @@ #![allow(async_fn_in_trait)] //! This module contains the traits and associated functions and //! structs which allow for USB communication. -//! use crate::context::UsbInterface; use std::error::Error; @@ -20,10 +19,10 @@ pub trait Device { /// Reset the device, which causes it to no longer be usable async fn reset(&self) -> Result<(), Box>; - /// 16 bit device product ID + /// 16 bit device Product ID async fn product_id(&self) -> u16; - /// 16 bit device vendor ID + /// 16 bit device Vendor ID async fn vendor_id(&self) -> u16; /// Device standard class @@ -51,11 +50,13 @@ pub trait Interface<'a> { async fn control_out(&self, data: ControlOut<'a>) -> Result<(), Box>; /// A USB bulk in transfer (device to host) - /// Returns a [Result] with the bytes in a `Vec` + /// It takes in a bulk endpoint to send to along with the length of + /// data to read, and returns a [Result] with the bytes async fn bulk_in(&self, endpoint: u8, length: usize) -> Result, Box>; /// A USB bulk out transfer (host to device). - /// Returns a [Result] with the number of bytes transferred + /// It takes in a bulk endpoint to send to along with some data as + /// a slice, and returns a [Result] containing the number of bytes transferred async fn bulk_out(&self, endpoint: u8, data: &[u8]) -> Result>; /* Interrupt transfers are a work in progress