Documentation improvements

This commit is contained in:
Kevin Mehall 2025-07-21 22:44:56 -06:00
parent 426516549a
commit 4009138dc7
9 changed files with 162 additions and 67 deletions

View file

@ -3,24 +3,24 @@ nusb
A new pure-Rust library for cross-platform low-level access to USB devices. A new pure-Rust library for cross-platform low-level access to USB devices.
[Documentation](https://docs.rs/nusb) * [Documentation](https://docs.rs/nusb)
* [Changelog](https://github.com/kevinmehall/nusb/releases)
* [Issues](https://github.com/kevinmehall/nusb/issues)
* [Discussions](https://github.com/kevinmehall/nusb/discussions)
`nusb` supports Windows, macOS, and Linux, and provides both async and
blocking APIs for listing and watching USB devices, reading descriptor
details, opening and managing devices and interfaces, and performing
transfers on control, bulk, and interrupt endpoints.
### Compared to [rusb](https://docs.rs/rusb/latest/rusb/) and [libusb](https://libusb.info/) ### Compared to [rusb](https://docs.rs/rusb/latest/rusb/) and [libusb](https://libusb.info/)
* Pure Rust, no dependency on libusb or any other C library. * Pure Rust, no dependency on libusb or any other C library.
* Async-first, while not requiring an async runtime like `tokio` or * Async-first, while not requiring an async runtime.
`async-std`. Still easily supports blocking with
`futures_lite::block_on`.
* No context object. You just open a device. There is a global event loop thread * No context object. You just open a device. There is a global event loop thread
that is started when opening the first device. that is started when opening the first device.
* Thinner layer over OS APIs, with less internal state. * Thinner layer over OS APIs, with less internal state.
### Current status
* Support for Linux, Windows, and macOS
* Device listing and descriptor parsing
* Transfers on control, bulk and interrupt endpoints
* Used in production by [probe-rs](https://github.com/probe-rs/probe-rs) and others, but still relatively new. Please test with your device and report issues.
### License ### License
MIT or Apache 2.0, at your option MIT or Apache 2.0, at your option

View file

@ -657,7 +657,7 @@ descriptor_fields! {
/// Get the raw value of the `bmAttributes` descriptor field. /// Get the raw value of the `bmAttributes` descriptor field.
/// ///
/// See [`transfer_type``][Self::transfer_type] for the transfer type field. /// See [`transfer_type`][Self::transfer_type] for the transfer type field.
#[doc(alias = "bmAttributes")] #[doc(alias = "bmAttributes")]
pub fn attributes at 3 -> u8; pub fn attributes at 3 -> u8;

View file

@ -56,7 +56,15 @@ impl Device {
platform::Device::from_device_info(d).map(|d| d.map(Device::wrap)) platform::Device::from_device_info(d).map(|d| d.map(Device::wrap))
} }
/// Wraps a device that is already open. /// Wrap a usbdevfs file descriptor that is already open.
///
/// This opens a device from a file descriptor for a `/dev/bus/usb/*` device
/// provided externally, such as from
/// [Android](https://developer.android.com/reference/android/hardware/usb/UsbDeviceConnection#getFileDescriptor()),
/// [xdg-desktop-portal](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Usb.html),
/// etc.
///
/// *Supported on Linux and Android only.*
#[cfg(any(target_os = "android", target_os = "linux"))] #[cfg(any(target_os = "android", target_os = "linux"))]
pub fn from_fd(fd: std::os::fd::OwnedFd) -> impl MaybeFuture<Output = Result<Device, Error>> { pub fn from_fd(fd: std::os::fd::OwnedFd) -> impl MaybeFuture<Output = Result<Device, Error>> {
platform::Device::from_fd(fd).map(|d| d.map(Device::wrap)) platform::Device::from_fd(fd).map(|d| d.map(Device::wrap))
@ -75,7 +83,7 @@ impl Device {
/// Detach kernel drivers and open an interface of the device and claim it for exclusive use. /// Detach kernel drivers and open an interface of the device and claim it for exclusive use.
/// ///
/// ### Platform notes /// ### Platform-specific details
/// This function can only detach kernel drivers on Linux. Calling on other platforms has /// This function can only detach kernel drivers on Linux. Calling on other platforms has
/// the same effect as [`claim_interface`][`Device::claim_interface`]. /// the same effect as [`claim_interface`][`Device::claim_interface`].
pub fn detach_and_claim_interface( pub fn detach_and_claim_interface(
@ -90,7 +98,7 @@ impl Device {
/// Detach kernel drivers for the specified interface. /// Detach kernel drivers for the specified interface.
/// ///
/// ### Platform notes /// ### Platform-specific details
/// This function can only detach kernel drivers on Linux. Calling on other platforms has /// This function can only detach kernel drivers on Linux. Calling on other platforms has
/// no effect. /// no effect.
pub fn detach_kernel_driver(&self, interface: u8) -> Result<(), Error> { pub fn detach_kernel_driver(&self, interface: u8) -> Result<(), Error> {
@ -103,7 +111,7 @@ impl Device {
/// Attach kernel drivers for the specified interface. /// Attach kernel drivers for the specified interface.
/// ///
/// ### Platform notes /// ### Platform-specific details
/// This function can only attach kernel drivers on Linux. Calling on other platforms has /// This function can only attach kernel drivers on Linux. Calling on other platforms has
/// no effect. /// no effect.
pub fn attach_kernel_driver(&self, interface: u8) -> Result<(), Error> { pub fn attach_kernel_driver(&self, interface: u8) -> Result<(), Error> {
@ -121,7 +129,7 @@ impl Device {
self.backend.device_descriptor() self.backend.device_descriptor()
} }
/// Get device speed. /// Get the device's connection speed.
pub fn speed(&self) -> Option<Speed> { pub fn speed(&self) -> Option<Speed> {
self.backend.speed() self.backend.speed()
} }
@ -156,7 +164,7 @@ impl Device {
/// descriptor field from [`ConfigurationDescriptor::configuration_value`] or `0` to /// descriptor field from [`ConfigurationDescriptor::configuration_value`] or `0` to
/// unconfigure the device. /// unconfigure the device.
/// ///
/// ### Platform-specific notes /// ### Platform-specific details
/// * Not supported on Windows /// * Not supported on Windows
pub fn set_configuration( pub fn set_configuration(
&self, &self,
@ -267,7 +275,7 @@ impl Device {
/// This `Device` will no longer be usable, and you should drop it and call /// This `Device` will no longer be usable, and you should drop it and call
/// [`list_devices`][`super::list_devices`] to find and re-open it again. /// [`list_devices`][`super::list_devices`] to find and re-open it again.
/// ///
/// ### Platform-specific notes /// ### Platform-specific details
/// * Not supported on Windows /// * Not supported on Windows
pub fn reset(&self) -> impl MaybeFuture<Output = Result<(), Error>> { pub fn reset(&self) -> impl MaybeFuture<Output = Result<(), Error>> {
self.backend.clone().reset() self.backend.clone().reset()
@ -297,7 +305,7 @@ impl Device {
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
/// ///
/// ### Platform-specific notes /// ### Platform-specific details
/// ///
/// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`] /// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
/// and use the interface handle to submit transfers. /// and use the interface handle to submit transfers.
@ -334,7 +342,7 @@ impl Device {
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
/// ///
/// ### Platform-specific notes /// ### Platform-specific details
/// ///
/// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`] /// * Not supported on Windows. You must [claim an interface][`Device::claim_interface`]
/// and use the interface handle to submit transfers. /// and use the interface handle to submit transfers.
@ -376,6 +384,9 @@ impl Interface {
/// An alternate setting is a mode of the interface that makes particular endpoints available /// An alternate setting is a mode of the interface that makes particular endpoints available
/// and may enable or disable functionality of the device. The OS resets the device to the default /// and may enable or disable functionality of the device. The OS resets the device to the default
/// alternate setting when the interface is released or the program exits. /// alternate setting when the interface is released or the program exits.
///
/// You must not have any pending transfers or open `Endpoints` on this interface when changing
/// the alternate setting.
pub fn set_alt_setting(&self, alt_setting: u8) -> impl MaybeFuture<Output = Result<(), Error>> { pub fn set_alt_setting(&self, alt_setting: u8) -> impl MaybeFuture<Output = Result<(), Error>> {
self.backend.clone().set_alt_setting(alt_setting) self.backend.clone().set_alt_setting(alt_setting)
} }
@ -410,14 +421,13 @@ impl Interface {
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
/// ///
/// ### Platform-specific notes /// ### Platform-specific details
/// * On Windows, if the `recipient` is `Interface`, the WinUSB driver sends /// * On Windows, if the `recipient` is `Interface`, the least significant
/// the interface number in the least significant byte of `index`, /// byte of `index` must match the interface number, or
/// overriding any value passed. A warning is logged if the passed `index` /// `TransferError::InvalidArgument` will be returned. This is a WinUSB
/// least significant byte differs from the interface number, and this may /// limitation.
/// become an error in the future. /// * On Windows, the timeout is currently fixed to 5 seconds and the
/// * On Windows, the timeout is currently fixed to 5 seconds and the timeout /// timeout argument is ignored.
/// argument is ignored.
pub fn control_in( pub fn control_in(
&self, &self,
data: ControlIn, data: ControlIn,
@ -452,14 +462,13 @@ impl Interface {
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
/// ///
/// ### Platform-specific notes /// ### Platform-specific details
/// * On Windows, if the `recipient` is `Interface`, the WinUSB driver sends /// * On Windows, if the `recipient` is `Interface`, the least significant
/// the interface number in the least significant byte of `index`, /// byte of `index` must match the interface number, or
/// overriding any value passed. A warning is logged if the passed `index` /// `TransferError::InvalidArgument` will be returned. This is a WinUSB
/// least significant byte differs from the interface number, and this may /// limitation.
/// become an error in the future. /// * On Windows, the timeout is currently fixed to 5 seconds and the
/// * On Windows, the timeout is currently fixed to 5 seconds and the timeout /// timeout argument is ignored.
/// argument is ignored.
pub fn control_out( pub fn control_out(
&self, &self,
data: ControlOut, data: ControlOut,
@ -498,6 +507,11 @@ impl Interface {
} }
/// Open an endpoint. /// Open an endpoint.
///
/// This claims exclusive access to the endpoint and returns an [`Endpoint`]
/// that can be used to submit transfers. The type-level `EndpointType` and
/// `EndpointDirection` parameters must match the endpoint type and
/// direction.
pub fn endpoint<EpType: EndpointType, Dir: EndpointDirection>( pub fn endpoint<EpType: EndpointType, Dir: EndpointDirection>(
&self, &self,
address: u8, address: u8,
@ -629,6 +643,10 @@ impl<EpType: BulkOrInterrupt, Dir: EndpointDirection> Endpoint<EpType, Dir> {
/// with the system allocator, it cannot be converted to a [`Vec`] without /// with the system allocator, it cannot be converted to a [`Vec`] without
/// copying. /// copying.
/// ///
/// This is a somewhat expensive operation, requiring a `mmap` system call,
/// so is likely only beneficial for buffers that will be used repeatedly.
/// Consider using [`Buffer::new`] for one-off transfers.
///
/// This is currently only supported on Linux, falling back to [`Buffer::new`] /// This is currently only supported on Linux, falling back to [`Buffer::new`]
/// on other platforms, or if the memory allocation fails. /// on other platforms, or if the memory allocation fails.
pub fn allocate(&self, len: usize) -> Buffer { pub fn allocate(&self, len: usize) -> Buffer {

View file

@ -12,12 +12,12 @@ pub struct DeviceId(pub(crate) crate::platform::DeviceId);
/// Information about a device that can be obtained without opening it. /// Information about a device that can be obtained without opening it.
/// ///
/// Found in the results of [`crate::list_devices`]. /// `DeviceInfo` is returned by [`list_devices`][crate::list_devices].
/// ///
/// ### Platform-specific notes /// ### Platform-specific notes
/// ///
/// * Some fields are platform-specific /// * Some fields are platform-specific
/// * Linux: `sysfs_path` /// * Linux: `sysfs_path`, `busnum`
/// * Windows: `instance_id`, `parent_instance_id`, `port_number`, `driver` /// * Windows: `instance_id`, `parent_instance_id`, `port_number`, `driver`
/// * macOS: `registry_id`, `location_id` /// * macOS: `registry_id`, `location_id`
#[derive(Clone)] #[derive(Clone)]
@ -213,10 +213,9 @@ impl DeviceInfo {
self.usb_version self.usb_version
} }
/// Code identifying the standard device class, from the `bDeviceClass` device descriptor field. /// Code identifying the [standard device
/// /// class](https://www.usb.org/defined-class-codes), from the `bDeviceClass`
/// `0x00`: specified at the interface level.\ /// device descriptor field.
/// `0xFF`: vendor-defined.
#[doc(alias = "bDeviceClass")] #[doc(alias = "bDeviceClass")]
pub fn class(&self) -> u8 { pub fn class(&self) -> u8 {
self.class self.class
@ -473,7 +472,7 @@ impl UsbControllerType {
/// Information about a system USB bus. /// Information about a system USB bus.
/// ///
/// Platform-specific fields: /// Platform-specific fields:
/// * Linux: `path`, `parent_path`, `busnum`, `root_hub` /// * Linux: `path`, `busnum`, `root_hub`
/// * Windows: `instance_id`, `parent_instance_id`, `location_paths`, `devinst`, `root_hub_description` /// * Windows: `instance_id`, `parent_instance_id`, `location_paths`, `devinst`, `root_hub_description`
/// * macOS: `registry_id`, `location_id`, `name`, `provider_class_name`, `class_name` /// * macOS: `registry_id`, `location_id`, `name`, `provider_class_name`, `class_name`
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
@ -616,12 +615,6 @@ impl BusInfo {
/// Detected USB controller type /// Detected USB controller type
/// ///
/// None means the controller type could not be determined. /// None means the controller type could not be determined.
///
/// ### Platform-specific notes
///
/// * Linux: Parsed from driver in use.
/// * macOS: The IOService entry matched.
/// * Windows: Parsed from the numbers following ROOT_HUB in the instance_id.
pub fn controller_type(&self) -> Option<UsbControllerType> { pub fn controller_type(&self) -> Option<UsbControllerType> {
self.controller_type self.controller_type
} }

View file

@ -97,7 +97,7 @@ pub enum ErrorKind {
Other, Other,
} }
/// Error from [`crate::Device::active_configuration`] /// Error from [`Device::active_configuration`][crate::Device::active_configuration].
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ActiveConfigurationError { pub struct ActiveConfigurationError {
pub(crate) configuration_value: u8, pub(crate) configuration_value: u8,

View file

@ -16,8 +16,28 @@ use crate::{
}; };
/// Wrapper for a Bulk or Interrupt IN [`Endpoint`](crate::Endpoint) that /// Wrapper for a Bulk or Interrupt IN [`Endpoint`](crate::Endpoint) that
/// implements [`Read`](std::io::Read) and [`BufRead`](std::io::BufRead) and /// manages transfers to provide a higher-level buffered API.
/// their `tokio` and `smol` async equivalents. ///
/// Most of the functionality of this type is provided through standard IO
/// traits; you'll want to use one of the following:
///
/// * [`std::io::Read`](std::io::Read) and [`BufRead`](std::io::BufRead) for
/// blocking IO.
/// * With the `tokio` cargo feature,
/// [`tokio::io::AsyncRead`](tokio::io::AsyncRead) and
/// [`AsyncBufRead`](tokio::io::AsyncBufRead) for async IO. Tokio also
/// provides `AsyncReadExt` and `AsyncBufReadExt` with additional methods.
/// * With the `smol` cargo feature,
/// [`futures_io::AsyncRead`](futures_io::AsyncRead) and
/// [`AsyncBufRead`](futures_io::AsyncBufRead) for async IO.
/// `futures_lite` provides `AsyncReadExt` and `AsyncBufReadExt` with
/// additional methods.
///
/// By default, this type ignores USB packet lengths and boundaries. For protocols
/// that use short or zero-length packets as delimiters, you can use the
/// [`until_short_packet()`](Self::until_short_packet) method to get an
/// [`EndpointReadUntilShortPacket`](EndpointReadUntilShortPacket) adapter
/// that observes these delimiters.
pub struct EndpointRead<EpType: BulkOrInterrupt> { pub struct EndpointRead<EpType: BulkOrInterrupt> {
endpoint: Endpoint<EpType, In>, endpoint: Endpoint<EpType, In>,
reading: Option<ReadBuffer>, reading: Option<ReadBuffer>,
@ -381,6 +401,9 @@ impl<EpType: BulkOrInterrupt> futures_io::AsyncBufRead for EndpointRead<EpType>
/// have any state other than that of the underlying [`EndpointRead`], so /// have any state other than that of the underlying [`EndpointRead`], so
/// dropping and re-creating with another call to /// dropping and re-creating with another call to
/// [`EndpointRead::until_short_packet()`] has no effect. /// [`EndpointRead::until_short_packet()`] has no effect.
///
/// This implements the same traits as `EndpointRead` but observes packet
/// boundaries instead of ignoring them.
pub struct EndpointReadUntilShortPacket<'a, EpType: BulkOrInterrupt> { pub struct EndpointReadUntilShortPacket<'a, EpType: BulkOrInterrupt> {
reader: &'a mut EndpointRead<EpType>, reader: &'a mut EndpointRead<EpType>,
} }
@ -399,10 +422,11 @@ impl std::fmt::Display for ExpectedShortPacket {
impl Error for ExpectedShortPacket {} impl Error for ExpectedShortPacket {}
impl<EpType: BulkOrInterrupt> EndpointReadUntilShortPacket<'_, EpType> { impl<EpType: BulkOrInterrupt> EndpointReadUntilShortPacket<'_, EpType> {
/// Check if the underlying endpoint has reached the end of a short packet. /// Check if the endpoint has reached the end of a short packet.
/// ///
/// Upon reading the end of a short packet, the next `read()` or `fill_buf()` /// Upon reading the end of a short packet, the next `read()` or
/// will return 0 bytes (EOF). To read the next message, call `consume_end()`. /// `fill_buf()` will return 0 bytes (EOF) and this method will return
/// `true`. To begin reading the next message, call `consume_end()`.
pub fn is_end(&self) -> bool { pub fn is_end(&self) -> bool {
self.reader self.reader
.reading .reading

View file

@ -13,7 +13,22 @@ use std::{
use std::{pin::Pin, task::ready}; use std::{pin::Pin, task::ready};
/// Wrapper for a Bulk or Interrupt OUT [`Endpoint`](crate::Endpoint) that /// Wrapper for a Bulk or Interrupt OUT [`Endpoint`](crate::Endpoint) that
/// implements [`Write`](std::io::Write) and its `tokio` and `smol` async equivalents. /// manages transfers to provide a higher-level buffered API.
///
/// Most of the functionality of this type is provided through standard IO
/// traits; you'll want to use one of the following:
///
/// * [`std::io::Write`](std::io::Write) for blocking IO.
/// * With the `tokio` cargo feature,
/// [`tokio::io::AsyncWrite`](tokio::io::AsyncWrite). Tokio also provides
/// `AsyncWriteExt` with additional methods.
/// * With the `smol` cargo feature,
/// [`futures_io::AsyncWrite`](futures_io::AsyncWrite) for async IO.
/// `futures_lite` provides `AsyncWriteExt` with additional methods.
///
/// Written data is buffered and may not be sent until the buffer is full or
/// [`submit`](Self::submit) / [`submit_end`](Self::submit_end) or
/// [`flush`](Self::flush) / [`flush_end`](Self::flush_end) are called.
pub struct EndpointWrite<EpType: BulkOrInterrupt> { pub struct EndpointWrite<EpType: BulkOrInterrupt> {
endpoint: Endpoint<EpType, Out>, endpoint: Endpoint<EpType, Out>,
writing: Option<Buffer>, writing: Option<Buffer>,

View file

@ -1,9 +1,13 @@
#![warn(missing_docs)] #![warn(missing_docs)]
//! A new library for cross-platform low-level access to USB devices. //! A new library for cross-platform low-level access to USB devices.
//! //!
//! `nusb` supports Windows, macOS, and Linux, and provides both async and
//! blocking APIs for listing and watching USB devices, reading descriptor
//! details, opening and managing devices and interfaces, and performing
//! transfers on control, bulk, and interrupt endpoints.
//!
//! `nusb` is comparable to the C library [libusb] and its Rust bindings [rusb], //! `nusb` is comparable to the C library [libusb] and its Rust bindings [rusb],
//! but written in pure Rust. It supports usage from both async and //! but written in pure Rust.
//! blocking contexts, and transfers are natively async.
//! //!
//! [libusb]: https://libusb.info //! [libusb]: https://libusb.info
//! [rusb]: https://docs.rs/rusb/ //! [rusb]: https://docs.rs/rusb/
@ -16,6 +20,41 @@
//! replace the kernel driver and program the device from user-space using this //! replace the kernel driver and program the device from user-space using this
//! library, but you'd have to re-implement the class functionality yourself.) //! library, but you'd have to re-implement the class functionality yourself.)
//! //!
//! ## Example usage
//!
//! ```no_run
//! use nusb::{list_devices, MaybeFuture};
//! use nusb::transfer::{Bulk, In, Out, ControlOut, ControlType, Recipient};
//! use std::io::{Read, Write, Error, ErrorKind};
//! use std::time::Duration;
//!
//! # fn main() -> Result<(), std::io::Error> {
//! let device = list_devices().wait()?
//! .find(|dev| dev.vendor_id() == 0xAAAA && dev.product_id() == 0xBBBB)
//! .ok_or(Error::new(ErrorKind::NotFound, "device not found"))?;
//!
//! let device = device.open().wait()?;
//! let interface = device.claim_interface(0).wait()?;
//!
//! interface.control_out(ControlOut {
//! control_type: ControlType::Vendor,
//! recipient: Recipient::Device,
//! request: 0x10,
//! value: 0x0,
//! index: 0x0,
//! data: &[0x01, 0x02, 0x03, 0x04],
//! }, Duration::from_millis(100)).wait()?;
//!
//! let mut writer = interface.endpoint::<Bulk, Out>(0x01)?.writer(4096);
//! writer.write_all(&[0x00, 0xff])?;
//! writer.flush()?;
//!
//! let mut reader = interface.endpoint::<Bulk, In>(0x81)?.reader(4096);
//! let mut buf = [0; 64];
//! reader.read_exact(&mut buf)?;
//! # Ok(()) }
//! ```
//!
//! ## USB and usage overview //! ## USB and usage overview
//! //!
//! When a USB device connects, the OS queries the device descriptor containing //! When a USB device connects, the OS queries the device descriptor containing
@ -29,10 +68,10 @@
//! Additional information about the device can be queried with //! Additional information about the device can be queried with
//! [`device.active_configuration()`](`Device::active_configuration`). //! [`device.active_configuration()`](`Device::active_configuration`).
//! //!
//! USB devices consist of one or more interfaces exposing a group of //! USB devices consist of one or more interfaces. A device with multiple
//! functionality. A device with multiple interfaces is known as a composite //! interfaces is known as a composite device. To open an interface, call
//! device. To open an interface, call [`Device::claim_interface`]. Only one //! [`Device::claim_interface`]. Only one program (or kernel driver) may claim
//! program (or kernel driver) may claim an interface at a time. //! an interface at a time.
//! //!
//! Use the resulting [`Interface`] to perform control transfers or open //! Use the resulting [`Interface`] to perform control transfers or open
//! an [`Endpoint`] to perform bulk or interrupt transfers. Submitting a //! an [`Endpoint`] to perform bulk or interrupt transfers. Submitting a
@ -40,6 +79,11 @@
//! internal queue for the endpoint. Completed transfers can be popped //! internal queue for the endpoint. Completed transfers can be popped
//! from the queue synchronously or asynchronously. //! from the queue synchronously or asynchronously.
//! //!
//! The [`EndpointRead`][io::EndpointRead] and
//! [`EndpointWrite`][io::EndpointWrite] types wrap the endpoint and
//! manage transfers and buffers to implement the standard `Read` and
//! `Write` traits and their async equivalents.
//!
//! *For more details on how USB works, [USB in a //! *For more details on how USB works, [USB in a
//! Nutshell](https://beyondlogic.org/usbnutshell/usb1.shtml) is a good //! Nutshell](https://beyondlogic.org/usbnutshell/usb1.shtml) is a good
//! overview.* //! overview.*
@ -188,10 +232,11 @@ pub fn list_devices() -> impl MaybeFuture<Output = Result<impl Iterator<Item = D
/// use nusb::MaybeFuture; /// use nusb::MaybeFuture;
/// ///
/// let devices = nusb::list_devices().wait().unwrap().collect::<Vec<_>>(); /// let devices = nusb::list_devices().wait().unwrap().collect::<Vec<_>>();
/// let buses: HashMap<String, (nusb::BusInfo, Vec::<nusb::DeviceInfo>)> = nusb::list_buses().wait().unwrap() /// let buses: HashMap<_, _> = nusb::list_buses().wait().unwrap()
/// .map(|bus| { /// .map(|bus| {
/// let bus_id = bus.bus_id().to_owned(); /// let bus_id = bus.bus_id().to_owned();
/// (bus.bus_id().to_owned(), (bus, devices.clone().into_iter().filter(|dev| dev.bus_id() == bus_id).collect())) /// let devs: Vec<_> = devices.iter().filter(|dev| dev.bus_id() == bus_id).cloned().collect();
/// (bus_id, (bus, devs))
/// }) /// })
/// .collect(); /// .collect();
/// ``` /// ```

View file

@ -133,7 +133,7 @@ pub mod blocking {
} }
#[cfg(not(any(feature = "smol", feature = "tokio")))] #[cfg(not(any(feature = "smol", feature = "tokio")))]
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!() unreachable!()
} }
} }