Pre-release docs tweaks
This commit is contained in:
parent
efd9c3484f
commit
99dec9729d
9 changed files with 107 additions and 40 deletions
|
|
@ -1,7 +1,9 @@
|
|||
[package]
|
||||
name = "nusb"
|
||||
version = "0.1.0"
|
||||
description = "Low-level access to USB devices in pure Rust"
|
||||
description = "Cross-platform low-level access to USB devices in pure Rust"
|
||||
categories = ["hardware-support"]
|
||||
keywords = ["usb", "hardware"]
|
||||
authors = ["Kevin Mehall <km@kevinmehall.net>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ nusb
|
|||
|
||||
A new pure-Rust library for cross-platform low-level access to USB devices.
|
||||
|
||||
[Documentation](https://docs.rs/nusb)
|
||||
|
||||
### Compared to [rusb](https://docs.rs/rusb/latest/rusb/) and libusb
|
||||
|
||||
* Pure Rust, no dependency on libusb or any other C library.
|
||||
|
|
@ -11,14 +13,13 @@ A new pure-Rust library for cross-platform low-level access to USB devices.
|
|||
`futures_lite::block_on`.
|
||||
* No context object. You just open a device. There is a global event loop thread
|
||||
that is started when opening the first device.
|
||||
* Doesn't try to paper over OS differences. For example, on Windows, you must open
|
||||
a specific interface, not a device as a whole. `nusb`'s API makes working with interfaces
|
||||
a required step so that it can map directly to Windows APIs.
|
||||
* Thinner layer over OS APIs, with less internal state.
|
||||
|
||||
### :construction: Current status
|
||||
|
||||
* Linux: Control, bulk and interrupt transfers work, minimally tested
|
||||
* Windows: Control, bulk and interrupt transfers work, minimally tested
|
||||
* macOS : Not yet implemented
|
||||
|
||||
### License
|
||||
MIT or Apache 2.0, at your option
|
||||
|
|
|
|||
|
|
@ -8,7 +8,17 @@ use crate::{
|
|||
|
||||
/// An opened USB device.
|
||||
///
|
||||
/// Obtain a `Device` by calling [`DeviceInfo::open`].
|
||||
/// Obtain a `Device` by calling [`DeviceInfo::open`]:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use nusb;
|
||||
/// let device_info = nusb::list_devices().unwrap()
|
||||
/// .find(|dev| dev.vendor_id() == 0xAAAA && dev.product_id() == 0xBBBB)
|
||||
/// .expect("device not connected");
|
||||
///
|
||||
/// let device = device_info.open().expect("failed to open device");
|
||||
/// let interface = device.claim_interface(0);
|
||||
/// ```
|
||||
///
|
||||
/// This type is reference-counted with an [`Arc`] internally, and can be cloned cheaply for
|
||||
/// use in multiple places in your program. The device is closed when all clones and all
|
||||
|
|
@ -74,6 +84,27 @@ impl Interface {
|
|||
}
|
||||
|
||||
/// Submit a single **IN (device-to-host)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::{ ControlIn, ControlType, Recipient };
|
||||
/// # fn main() -> Result<(), std::io::Error> {
|
||||
/// # let di = nusb::list_devices().unwrap().next().unwrap();
|
||||
/// # let device = di.open().unwrap();
|
||||
/// # let interface = device.claim_interface(0).unwrap();
|
||||
///
|
||||
/// let data: Vec<u8> = block_on(interface.control_in(ControlIn {
|
||||
/// control_type: ControlType::Vendor,
|
||||
/// recipient: Recipient::Device,
|
||||
/// request: 0x30,
|
||||
/// value: 0x0,
|
||||
/// index: 0x0,
|
||||
/// length: 64,
|
||||
/// })).into_result()?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
pub fn control_in(&self, data: ControlIn) -> TransferFuture<ControlIn> {
|
||||
let mut t = self.backend.make_transfer(0, EndpointType::Control);
|
||||
t.submit::<ControlIn>(data);
|
||||
|
|
@ -81,6 +112,27 @@ impl Interface {
|
|||
}
|
||||
|
||||
/// Submit a single **OUT (host-to-device)** transfer on the default **control** endpoint.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::{ ControlOut, ControlType, Recipient };
|
||||
/// # fn main() -> Result<(), std::io::Error> {
|
||||
/// # let di = nusb::list_devices().unwrap().next().unwrap();
|
||||
/// # let device = di.open().unwrap();
|
||||
/// # let interface = device.claim_interface(0).unwrap();
|
||||
///
|
||||
/// block_on(interface.control_out(ControlOut {
|
||||
/// control_type: ControlType::Vendor,
|
||||
/// recipient: Recipient::Device,
|
||||
/// request: 0x32,
|
||||
/// value: 0x0,
|
||||
/// index: 0x0,
|
||||
/// data: &[0x01, 0x02, 0x03, 0x04],
|
||||
/// })).into_result()?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
pub fn control_out(&self, data: ControlOut) -> TransferFuture<ControlOut> {
|
||||
let mut t = self.backend.make_transfer(0, EndpointType::Control);
|
||||
t.submit::<ControlOut>(data);
|
||||
|
|
|
|||
32
src/lib.rs
32
src/lib.rs
|
|
@ -32,10 +32,10 @@
|
|||
//! device. To open an interface, call [`Device::claim_interface`]. Only one
|
||||
//! program (or kernel driver) may claim an interface at a time.
|
||||
//!
|
||||
//! Use the resulting [`Interface`] struct to transfer data on the device's
|
||||
//! control, bulk or interrupt endpoints. Transfers are async by default, and
|
||||
//! can be awaited as individual `Future`s, or use a [`transfer::Queue`] to
|
||||
//! manage streams of data.
|
||||
//! Use the resulting [`Interface`] to transfer data on the device's control,
|
||||
//! bulk or interrupt endpoints. Transfers are async by default, and can be
|
||||
//! awaited as individual [`Future`][`transfer::TransferFuture`]s, or use a
|
||||
//! [`Queue`][`transfer::Queue`] to manage streams of data.
|
||||
//!
|
||||
//! *For more details on how USB works, [USB in a
|
||||
//! Nutshell](https://beyondlogic.org/usbnutshell/usb1.shtml) is a good
|
||||
|
|
@ -44,10 +44,14 @@
|
|||
//! ## Logging
|
||||
//!
|
||||
//! `nusb` uses the [`log`](https://docs.rs/log) crate to log debug and error
|
||||
//! information. When submitting a bug report, please include the logs: include
|
||||
//! a logging backend like [`env_logger`](https://docs.rs/env_logger) and
|
||||
//! configure it to enable log output for this crate (for `env_logger` set
|
||||
//! environment variable `RUST_LOG=nusb=debug`.)
|
||||
//! information.
|
||||
//!
|
||||
//! When [submitting a bug report][gh-issues], please include the logs: use a
|
||||
//! `log` backend like [`env_logger`](https://docs.rs/env_logger) and configure
|
||||
//! it to enable log output for this crate (for `env_logger` set environment
|
||||
//! variable `RUST_LOG=nusb=debug`.)
|
||||
//!
|
||||
//! [gh-issues]: https://github.com/kevinmehall/nusb/issues
|
||||
//!
|
||||
//! ## Platform support
|
||||
//!
|
||||
|
|
@ -78,7 +82,7 @@
|
|||
//!
|
||||
//! ### Windows
|
||||
//!
|
||||
//! `nusb` uses [SetupAPI] to find devices and [WinUSB] to access them.
|
||||
//! `nusb` uses [WinUSB] on Windows.
|
||||
//!
|
||||
//! On Windows, devices are associated with a particular driver, which persists
|
||||
//! across connections and reboots. Composite devices appear as multiple devices
|
||||
|
|
@ -86,11 +90,11 @@
|
|||
//! separate driver.
|
||||
//!
|
||||
//! To use `nusb`, your device or interface must be associated with the `WinUSB`
|
||||
//! driver. The recommended way to this if you control the device firmware is to
|
||||
//! use a [WCID] descriptor to tell Windows to install the WinUSB driver
|
||||
//! automatically when the device is first connected. Alternatively [Zadig]
|
||||
//! (GUI) or [libwdi] (CLI / C library) can be used to manually install the
|
||||
//! WinUSB driver for a device.
|
||||
//! driver. If you control the device firmware, the recommended way is to use a
|
||||
//! [WCID] descriptor to tell Windows to install the WinUSB driver automatically
|
||||
//! when the device is first connected. Alternatively [Zadig] (GUI) or [libwdi]
|
||||
//! (CLI / C library) can be used to manually install the WinUSB driver for a
|
||||
//! device.
|
||||
//!
|
||||
//! [SetupAPI]:
|
||||
//! https://learn.microsoft.com/en-us/windows-hardware/drivers/install/setupapi
|
||||
|
|
|
|||
|
|
@ -132,7 +132,10 @@ impl LinuxInterface {
|
|||
}
|
||||
|
||||
pub fn set_alt_setting(&self, alt_setting: u8) -> Result<(), Error> {
|
||||
debug!("Set interface {} alt setting to {alt_setting}", self.interface);
|
||||
debug!(
|
||||
"Set interface {} alt setting to {alt_setting}",
|
||||
self.interface
|
||||
);
|
||||
Ok(usbfs::set_interface(
|
||||
&self.device.fd,
|
||||
self.interface,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
//! Wrappers for the [usbfs] character device ioctls, translated from the
|
||||
//! [C structures and ioctl definitions][uapi].
|
||||
//!
|
||||
//! [usbfs]: https://www.kernel.org/doc/html/latest/driver-api/usb/usb.html#the-usb-character-device-nodes
|
||||
//! [uapi]: https://github.com/torvalds/linux/blob/master/tools/include/uapi/linux/usbdevice_fs.h
|
||||
#![allow(dead_code)]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ use std::{
|
|||
|
||||
use log::{debug, error};
|
||||
use windows_sys::Win32::{
|
||||
Devices::Usb::{WinUsb_Free, WinUsb_Initialize, WINUSB_INTERFACE_HANDLE, WinUsb_SetCurrentAlternateSetting},
|
||||
Devices::Usb::{
|
||||
WinUsb_Free, WinUsb_Initialize, WinUsb_SetCurrentAlternateSetting, WINUSB_INTERFACE_HANDLE,
|
||||
},
|
||||
Foundation::{FALSE, TRUE},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
//! Transfer-related types.
|
||||
//!
|
||||
//!
|
||||
//! Use the methods on an [`Interface`][`super::Interface`] to make individual
|
||||
//! transfers or obtain a [`Queue`] to manage multiple transfers.
|
||||
|
||||
use std::{
|
||||
fmt::Display,
|
||||
future::Future,
|
||||
io,
|
||||
marker::PhantomData,
|
||||
task::{Context, Poll}, io, fmt::Display,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use crate::platform;
|
||||
|
|
|
|||
|
|
@ -10,17 +10,17 @@ use crate::platform;
|
|||
use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRequest};
|
||||
|
||||
/// Manages a stream of transfers on an endpoint.
|
||||
///
|
||||
///
|
||||
/// A `Queue` optimizes a common pattern when streaming data to or from a USB
|
||||
/// endpoint: To maximize throughput and minimize latency, the host controller
|
||||
/// needs to attempt a transfer in every possible frame. That requires always
|
||||
/// having a transfer request pending with the kernel by submitting multiple
|
||||
/// transfer requests and re-submitting them as they complete.
|
||||
///
|
||||
///
|
||||
/// When the `Queue` is dropped, all pending transfers are cancelled.
|
||||
///
|
||||
/// ### Why use a `Queue` instead of submitting multiple transfers individually
|
||||
/// with the methods on [`Interface`][`crate::Interface`]?
|
||||
///
|
||||
/// ### Why use a `Queue` instead of submitting multiple transfers individually with the methods on [`Interface`][`crate::Interface`]?
|
||||
///
|
||||
/// * Individual transfers give you individual `Future`s, which you then have
|
||||
/// to keep track of and poll using something like `FuturesUnordered`.
|
||||
/// * A `Queue` provides better cancellation semantics than `Future`'s
|
||||
|
|
@ -36,9 +36,9 @@ use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRe
|
|||
/// * A queue caches the internal transfer data structures of the last
|
||||
/// completed transfer, meaning that if you re-use the data buffer there is
|
||||
/// no memory allocation involved in continued streaming.
|
||||
///
|
||||
///
|
||||
/// ### Example (read from an endpoint)
|
||||
///
|
||||
///
|
||||
/// ```no_run
|
||||
/// use futures_lite::future::block_on;
|
||||
/// use nusb::transfer::RequestBuffer;
|
||||
|
|
@ -47,18 +47,18 @@ use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRe
|
|||
/// # let interface = device.claim_interface(0).unwrap();
|
||||
/// # fn handle_data(_: &[u8]) {}
|
||||
/// let mut queue = interface.bulk_in_queue(0x81);
|
||||
///
|
||||
///
|
||||
/// let n_transfers = 8;
|
||||
/// let transfer_size = 256;
|
||||
///
|
||||
/// while queue.pending() < n_transfers {
|
||||
/// queue.submit(RequestBuffer::new(transfer_size));
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// loop {
|
||||
/// let completion = block_on(queue.next_complete());
|
||||
/// handle_data(&completion.data); // your function
|
||||
///
|
||||
///
|
||||
/// if completion.status.is_err() {
|
||||
/// break;
|
||||
/// }
|
||||
|
|
@ -66,7 +66,7 @@ use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRe
|
|||
/// queue.submit(RequestBuffer::reuse(completion.data, transfer_size))
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ### Example (write to an endpoint)
|
||||
/// ```no_run
|
||||
/// use std::mem;
|
||||
|
|
@ -77,18 +77,18 @@ use super::{Completion, EndpointType, PlatformSubmit, TransferHandle, TransferRe
|
|||
/// # fn fill_data(_: &mut Vec<u8>) {}
|
||||
/// # fn data_confirmed_sent(_: usize) {}
|
||||
/// let mut queue = interface.bulk_out_queue(0x02);
|
||||
///
|
||||
///
|
||||
/// let n_transfers = 8;
|
||||
///
|
||||
/// let mut next_buf = Vec::new();
|
||||
///
|
||||
///
|
||||
/// loop {
|
||||
/// while queue.pending() < n_transfers {
|
||||
/// let mut buf = mem::replace(&mut next_buf, Vec::new());
|
||||
/// fill_data(&mut buf); // your function
|
||||
/// queue.submit(buf);
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// let completion = block_on(queue.next_complete());
|
||||
/// data_confirmed_sent(completion.data.actual_length()); // your function
|
||||
/// next_buf = completion.data.reuse();
|
||||
|
|
@ -133,7 +133,7 @@ where
|
|||
}
|
||||
|
||||
/// Submit a new transfer on the endpoint.
|
||||
///
|
||||
///
|
||||
/// For an `IN` endpoint, pass a [`RequestBuffer`][`super::RequestBuffer`].\
|
||||
/// For an `OUT` endpoint, pass a [`Vec<u8>`].
|
||||
pub fn submit(&mut self, data: R) {
|
||||
|
|
@ -147,7 +147,7 @@ where
|
|||
|
||||
/// Return a `Future` that waits for the next pending transfer to complete, and yields its
|
||||
/// buffer and status.
|
||||
///
|
||||
///
|
||||
/// For an `IN` endpoint, the completion contains a [`Vec<u8>`].\
|
||||
/// For an `OUT` endpoint, the completion contains a [`ResponseBuffer`][`super::ResponseBuffer`].
|
||||
///
|
||||
|
|
@ -173,7 +173,7 @@ where
|
|||
}
|
||||
|
||||
/// Cancel all pending transfers.
|
||||
///
|
||||
///
|
||||
/// They will still be returned from subsequent calls to `next_complete` so
|
||||
/// you can tell which were completed, partially-completed, or cancelled.
|
||||
pub fn cancel_all(&mut self) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue