Added get_device_filter, made WASM check for previously paired devices

This commit is contained in:
G2-Games 2024-02-01 00:26:55 -06:00
parent b60f5f0eae
commit d70248cc1d
5 changed files with 138 additions and 17 deletions

View file

@ -47,7 +47,7 @@ features = [
nusb = "0.1.5" nusb = "0.1.5"
[dev-dependencies] [dev-dependencies]
futures-lite = "1.13.0" tokio-test = "0.4.3"
[profile.release] [profile.release]
opt-level = "s" opt-level = "s"

View file

@ -35,6 +35,40 @@ pub async fn get_device(vendor_id: u16, product_id: u16) -> Result<UsbDevice, Bo
}) })
} }
#[derive(PartialEq, Eq, Clone)]
pub struct FilterTuple(pub u16, pub u16);
pub async fn get_device_filter(device_filter: Vec<FilterTuple>) -> Result<UsbDevice, Box<dyn Error>> {
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 { impl Device for UsbDevice {
type UsbDevice = UsbDevice; type UsbDevice = UsbDevice;
type UsbInterface = UsbInterface; type UsbInterface = UsbInterface;

View file

@ -29,6 +29,20 @@ pub async fn get_device(vendor_id: u16, product_id: u16) -> Result<UsbDevice, js
let navigator = window.navigator(); let navigator = window.navigator();
let usb = navigator.usb(); 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();
if device.vendor_id() == vendor_id && device.product_id() == product_id {
let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
return Ok(UsbDevice {
device
})
}
}
let arr = Array::new(); let arr = Array::new();
let filter1 = js_sys::Object::new(); let filter1 = js_sys::Object::new();
js_sys::Reflect::set( js_sys::Reflect::set(
@ -48,15 +62,62 @@ pub async fn get_device(vendor_id: u16, product_id: u16) -> Result<UsbDevice, js
let filters2 = UsbDeviceRequestOptions::new(&filters); let filters2 = UsbDeviceRequestOptions::new(&filters);
let device_promise = JsFuture::from(Promise::resolve(&usb.request_device(&filters2))).await; let device: WasmUsbDevice = JsFuture::from(Promise::resolve(&usb.request_device(&filters2))).await?.into();
let device: WasmUsbDevice = match device_promise { let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;
Ok(dev) => dev.into(),
Err(err) => { Ok(UsbDevice { device })
console::log_1(&err.clone());
return Err(err.into());
} }
};
#[wasm_bindgen]
#[derive(PartialEq, Eq, Clone)]
pub struct FilterTuple(pub u16, pub u16);
#[wasm_bindgen]
pub async fn get_device_filter(device_filter: Vec<FilterTuple>) -> Result<UsbDevice, js_sys::Error> {
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?; let _open_promise = JsFuture::from(Promise::resolve(&device.open())).await?;

View file

@ -5,17 +5,18 @@
//! //!
//! ## Example: //! ## Example:
//! ```no_run //! ```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 //! // 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 //! // 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 //! // Send a Control transfer to the device, obtaining
//! // the result and storing it in `result`, and you're done! //! // 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, //! control_type: ControlType::Vendor,
//! recipient: Recipient::Interface, //! recipient: Recipient::Interface,
//! request: 0x01, //! request: 0x01,
@ -25,6 +26,7 @@
//! }) //! })
//! .await //! .await
//! .expect("Sending control transfer failed"); //! .expect("Sending control transfer failed");
//! # })
//! ``` //! ```
pub mod usb; pub mod usb;
@ -46,6 +48,29 @@ pub use crate::context::UsbDevice;
/// An implementation of a USB interface /// An implementation of a USB interface
pub use crate::context::UsbInterface; 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 /// Gets a single device from the VendorID and ProductID
#[doc(inline)] #[doc(inline)]
pub use crate::context::get_device; 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;

View file

@ -1,7 +1,6 @@
#![allow(async_fn_in_trait)] #![allow(async_fn_in_trait)]
//! 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 crate::context::UsbInterface;
use std::error::Error; use std::error::Error;
@ -20,10 +19,10 @@ pub trait Device {
/// Reset the device, which causes it to no longer be usable /// Reset the device, which causes it to no longer be usable
async fn reset(&self) -> Result<(), Box<dyn Error>>; async fn reset(&self) -> Result<(), Box<dyn Error>>;
/// 16 bit device product ID /// 16 bit device Product ID
async fn product_id(&self) -> u16; async fn product_id(&self) -> u16;
/// 16 bit device vendor ID /// 16 bit device Vendor ID
async fn vendor_id(&self) -> u16; async fn vendor_id(&self) -> u16;
/// Device standard class /// Device standard class
@ -51,11 +50,13 @@ pub trait Interface<'a> {
async fn control_out(&self, data: ControlOut<'a>) -> Result<(), Box<dyn Error>>; async fn control_out(&self, data: ControlOut<'a>) -> Result<(), Box<dyn Error>>;
/// A USB bulk in transfer (device to host) /// A USB bulk in transfer (device to host)
/// Returns a [Result] with the bytes in a `Vec<u8>` /// 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<Vec<u8>, Box<dyn Error>>; async fn bulk_in(&self, endpoint: u8, length: usize) -> Result<Vec<u8>, Box<dyn Error>>;
/// A USB bulk out transfer (host to device). /// 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<usize, Box<dyn Error>>; async fn bulk_out(&self, endpoint: u8, data: &[u8]) -> Result<usize, Box<dyn Error>>;
/* Interrupt transfers are a work in progress /* Interrupt transfers are a work in progress