Compare commits

...

5 commits

Author SHA1 Message Date
bbdb47add7 v0.3.1 2024-03-24 06:41:25 -05:00
43e19eda94 Fixed typos 2024-03-24 06:41:08 -05:00
35ed1e273b v0.3.0 2024-03-24 06:18:53 -05:00
15f8875b6e Fixed example 2024-03-24 06:09:07 -05:00
93e621e969 Renamed many items to be less redundant 2024-03-24 06:05:28 -05:00
5 changed files with 104 additions and 46 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "cross_usb" name = "cross_usb"
version = "0.2.3" version = "0.3.1"
authors = ["G2-Games <ke0bhogsg@gmail.com>"] authors = ["G2-Games <ke0bhogsg@gmail.com>"]
repository = "https://github.com/G2-Games/cross-usb" repository = "https://github.com/G2-Games/cross-usb"
documentation = "https://docs.rs/cross_usb" documentation = "https://docs.rs/cross_usb"

View file

@ -1,18 +1,34 @@
use crate::usb::{ use crate::usb::{
ControlIn, ControlOut, ControlType, Descriptor, Device, Interface, Recipient, UsbError, ControlIn, ControlOut, ControlType, UsbDescriptor, UsbDevice, UsbInterface, Recipient, UsbError,
}; };
pub struct UsbDescriptor { #[derive(Clone, Debug)]
pub struct Descriptor {
device_info: nusb::DeviceInfo, device_info: nusb::DeviceInfo,
} }
pub struct UsbDevice { #[derive(Clone)]
device_info: UsbDescriptor, pub struct Device {
device_info: Descriptor,
device: nusb::Device, device: nusb::Device,
} }
pub struct UsbInterface { impl std::fmt::Debug for Device {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.device_info)
}
}
#[derive(Clone)]
pub struct Interface {
interface: nusb::Interface, interface: nusb::Interface,
number: u8,
}
impl std::fmt::Debug for Interface {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Interface {:?}", self.number)
}
} }
#[derive(PartialEq, Clone, Default)] #[derive(PartialEq, Clone, Default)]
@ -42,7 +58,9 @@ impl DeviceFilter {
} }
} }
pub async fn get_device(device_filters: Vec<DeviceFilter>) -> Result<UsbDescriptor, UsbError> { pub async fn get_device(
device_filters: Vec<DeviceFilter>
) -> Result<Descriptor, UsbError> {
let devices = nusb::list_devices().unwrap(); let devices = nusb::list_devices().unwrap();
let mut device_info = None; let mut device_info = None;
@ -83,12 +101,12 @@ 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 }) Ok(Descriptor { device_info })
} }
pub async fn get_device_list( pub async fn get_device_list(
device_filters: Vec<DeviceFilter>, device_filters: Vec<DeviceFilter>,
) -> Result<Vec<UsbDescriptor>, UsbError> { ) -> Result<impl Iterator<Item = Descriptor>, UsbError> {
let devices_info = nusb::list_devices().unwrap(); let devices_info = nusb::list_devices().unwrap();
let mut devices = Vec::new(); let mut devices = Vec::new();
@ -127,16 +145,16 @@ pub async fn get_device_list(
return Err(UsbError::DeviceNotFound); return Err(UsbError::DeviceNotFound);
} }
let devices_opened: Vec<UsbDescriptor> = devices let devices_opened: Vec<Descriptor> = devices
.into_iter() .into_iter()
.map(|d| UsbDescriptor { device_info: d }) .map(|d| Descriptor { device_info: d })
.collect(); .collect();
Ok(devices_opened) Ok(devices_opened.into_iter())
} }
impl Descriptor for UsbDescriptor { impl UsbDescriptor for Descriptor {
type Device = UsbDevice; type Device = Device;
async fn open(self) -> Result<Self::Device, UsbError> { async fn open(self) -> Result<Self::Device, UsbError> {
match self.device_info.open() { match self.device_info.open() {
@ -173,8 +191,8 @@ impl Descriptor for UsbDescriptor {
} }
} }
impl Device for UsbDevice { impl UsbDevice for Device {
type Interface = UsbInterface; type Interface = Interface;
async fn open_interface(&self, number: u8) -> Result<Self::Interface, UsbError> { async fn open_interface(&self, number: u8) -> Result<Self::Interface, UsbError> {
let interface = match self.device.claim_interface(number) { let interface = match self.device.claim_interface(number) {
@ -182,7 +200,10 @@ impl Device for UsbDevice {
Err(err) => return Err(UsbError::CommunicationError(err.to_string())), Err(err) => return Err(UsbError::CommunicationError(err.to_string())),
}; };
Ok(UsbInterface { interface }) Ok(Interface {
interface,
number
})
} }
async fn detach_and_open_interface(&self, number: u8) -> Result<Self::Interface, UsbError> { async fn detach_and_open_interface(&self, number: u8) -> Result<Self::Interface, UsbError> {
@ -191,7 +212,10 @@ impl Device for UsbDevice {
Err(err) => return Err(UsbError::CommunicationError(err.to_string())), Err(err) => return Err(UsbError::CommunicationError(err.to_string())),
}; };
Ok(UsbInterface { interface }) Ok(Interface {
interface,
number
})
} }
async fn reset(&self) -> Result<(), UsbError> { async fn reset(&self) -> Result<(), UsbError> {
@ -230,13 +254,13 @@ impl Device for UsbDevice {
} }
} }
impl Drop for UsbDevice { impl Drop for Device {
fn drop(&mut self) { fn drop(&mut self) {
let _ = self.device.reset(); let _ = self.device.reset();
} }
} }
impl<'a> Interface<'a> for UsbInterface { impl<'a> UsbInterface<'a> for Interface {
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() {
Ok(res) => res, Ok(res) => res,

View file

@ -14,16 +14,19 @@ use crate::usb::{
}; };
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug)]
pub struct UsbDescriptor { pub struct UsbDescriptor {
device: WasmUsbDevice, device: WasmUsbDevice,
} }
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug)]
pub struct UsbDevice { pub struct UsbDevice {
device: WasmUsbDevice, device: WasmUsbDevice,
} }
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug)]
pub struct UsbInterface { pub struct UsbInterface {
device: WasmUsbDevice, device: WasmUsbDevice,
_number: u8, _number: u8,

View file

@ -7,7 +7,7 @@
//! and comparable to the very popular `libusb` C library. Web Assembly support is provided by [web-sys](https://docs.rs/web-sys/latest/web_sys/) //! and comparable to the very popular `libusb` C library. Web Assembly support is provided by [web-sys](https://docs.rs/web-sys/latest/web_sys/)
//! with the [Web USB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API). //! with the [Web USB API](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API).
//! //!
//! When a [UsbInterface] is dropped, it is automatically released. //! When an [`Interface`] is dropped, it is automatically released.
//! //!
//! ### CURRENT LIMITATIONS: //! ### CURRENT LIMITATIONS:
//! * Hotplug support is not implemented. Waiting on [hotplug support in nusb](https://github.com/kevinmehall/nusb/pull/20). //! * Hotplug support is not implemented. Waiting on [hotplug support in nusb](https://github.com/kevinmehall/nusb/pull/20).
@ -19,10 +19,11 @@
//! ## 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::prelude::*;
//! use cross_usb::usb::{Recipient, ControlType, ControlIn};
//! use cross_usb::device_filter; //! use cross_usb::device_filter;
//! //!
//! // Obtain a device descriptor (UsbDescriptor) using a DeviceFilter, //! // Obtain a device descriptor using a DeviceFilter,
//! // in this case with its VendorID and ProductID //! // in this case with its VendorID and ProductID
//! let filters = vec![ //! let filters = vec![
//! device_filter!{vendor_id: 0x054c, product_id: 0x00c9} //! device_filter!{vendor_id: 0x054c, product_id: 0x00c9}
@ -51,6 +52,18 @@
//! ``` //! ```
pub mod usb; pub mod usb;
/// This prelude imports all the necessary traits needed to actually use USB
/// devices and interfaces.
///
/// ```
/// use cross_usb::prelude::*;
/// ```
pub mod prelude {
pub use crate::usb::UsbDescriptor;
pub use crate::usb::UsbDevice;
pub use crate::usb::UsbInterface;
}
/// The context contains the platform specific implementation of the USB transfers /// 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"]
@ -61,23 +74,25 @@ mod context;
mod context; mod context;
#[doc(inline)] #[doc(inline)]
/// An implementation of a USB device descriptor /// A descriptor of a USB device, containing information about a device
pub use crate::context::UsbDescriptor; /// without claiming it
pub use crate::context::Descriptor;
#[doc(inline)] #[doc(inline)]
/// An implementation of a USB device /// A USB device, you must open an [`Interface`] to perform transfers
pub use crate::context::UsbDevice; pub use crate::context::Device;
#[doc(inline)] #[doc(inline)]
/// An implementation of a USB interface /// A USB interface with which to perform transfers on
pub use crate::context::UsbInterface; pub use crate::context::Interface;
/// Information about a USB device for finding it while trying /// Information about a USB device for use in [`get_device`]
/// to look for new USB devices using [get_device] /// or [`get_device_list`]
#[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 (the first found) device as a [`Descriptor`] from a list of VendorID
/// and ProductIDs
/// ///
/// ## Example /// ## Example
/// ```no_run /// ```no_run
@ -89,22 +104,38 @@ pub use crate::context::DeviceFilter;
/// device_filter!{vendor_id: 0x054c}, /// device_filter!{vendor_id: 0x054c},
/// ]; /// ];
/// ///
/// let device = get_device(filter).await.expect("Could not find device in list"); /// let device = get_device(filter).await.expect("Could not find device matching filters");
/// # }) /// # })
/// ``` /// ```
#[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 /// Gets a list of [`Descriptor`]s from a list of VendorID and ProductIDs
///
/// ## Example
/// ```no_run
/// # tokio_test::block_on(async {
/// use cross_usb::{get_device_list, DeviceFilter, device_filter};
///
/// let filter = vec![
/// device_filter!{vendor_id: 0x054c, product_id: 0x00c9},
/// device_filter!{vendor_id: 0x054c},
/// ];
///
/// let device_list = get_device_list(filter).await.expect("Could not find device matching filters");
///
/// /* Do something with the list of devices... */
/// # })
/// ```
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
#[doc(inline)] #[doc(inline)]
pub use crate::context::get_device_list; 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.
/// You may use as many or as few of them as you need, the rest /// You may use as many or as few of them as you need, the rest
/// of the values will be filled with [None]. /// of the values will be filled with [`None`].
/// ///
/// ## Usage /// ## Usage
/// ``` /// ```
@ -112,11 +143,11 @@ pub use crate::context::get_device_list;
/// ///
/// // Example with all fields filled /// // Example with all fields filled
/// device_filter!{ /// device_filter!{
/// vendor_id: 0x054c, /// vendor_id: 0x054c, // u16
/// product_id: 0x0186, /// product_id: 0x0186, // u16
/// class: 0xFF, /// class: 0xFF, // u8
/// subclass: 0x02, /// subclass: 0x02, // u8
/// protocol: 0x15, /// protocol: 0x15, // u8
/// }; /// };
/// ``` /// ```
#[macro_export] #[macro_export]

View file

@ -4,7 +4,7 @@
use thiserror::Error; use thiserror::Error;
pub trait Descriptor { pub trait UsbDescriptor {
/// A unique USB Device /// A unique USB Device
type Device; type Device;
@ -33,7 +33,7 @@ pub trait Descriptor {
} }
/// A unique USB device /// A unique USB device
pub trait Device { pub trait UsbDevice {
/// A unique Interface on a USB Device /// A unique Interface on a USB Device
type Interface; type Interface;
@ -77,7 +77,7 @@ pub trait Device {
} }
/// A specific interface of a USB device /// A specific interface of a USB device
pub trait Interface<'a> { pub trait UsbInterface<'a> {
/// A USB control in transfer (device to host) /// A USB control in transfer (device to host)
/// Returns a [Result] with the bytes in a `Vec<u8>` /// Returns a [Result] with the bytes in a `Vec<u8>`
async fn control_in(&self, data: ControlIn) -> Result<Vec<u8>, UsbError>; async fn control_in(&self, data: ControlIn) -> Result<Vec<u8>, UsbError>;
@ -141,7 +141,7 @@ pub enum Recipient {
Other = 3, Other = 3,
} }
/// Parameters for [Interface::control_in] /// Parameters for [UsbInterface::control_in]
pub struct ControlIn { pub struct ControlIn {
pub control_type: ControlType, pub control_type: ControlType,
pub recipient: Recipient, pub recipient: Recipient,
@ -151,7 +151,7 @@ pub struct ControlIn {
pub length: u16, pub length: u16,
} }
/// Parameters for [Interface::control_out] /// Parameters for [UsbInterface::control_out]
pub struct ControlOut<'a> { pub struct ControlOut<'a> {
pub control_type: ControlType, pub control_type: ControlType,
pub recipient: Recipient, pub recipient: Recipient,