Crate nusb

Source
Expand description

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, but written in pure Rust.

Use nusb to write user-space drivers in Rust for non-standard USB devices or those without kernel support. For devices implementing a standard USB class such as Mass Storage, CDC (Serial), HID, Audio, or Video, this is probably not the library you’re looking for – use something built on the existing kernel driver instead. (On some platforms you could detach or 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.)

§Example usage

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;

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)?;

§USB and usage overview

When a USB device connects, the OS queries the device descriptor containing basic device information such as the vendor and product ID (VID / PID) and string descriptors like the manufacturer, product, and serial number strings. list_devices returns an iterator listing connected USB devices, which can be filtered by these fields to identify and select the desired device.

Call device_info.open() to open a selected device. Additional information about the device can be queried with device.active_configuration().

USB devices consist of one or more interfaces. A device with multiple interfaces is known as a composite 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 to perform control transfers or open an Endpoint to perform bulk or interrupt transfers. Submitting a transfer is a non-blocking operation that adds the transfer to an internal queue for the endpoint. Completed transfers can be popped from the queue synchronously or asynchronously.

The EndpointRead and 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 Nutshell is a good overview.

§Logging

nusb uses the log crate to log debug and error information.

When submitting a bug report, please include the logs: use a log backend like env_logger and configure it to enable log output for this crate (for env_logger set environment variable RUST_LOG=nusb=debug.)

§Platform support

§Linux

nusb is built on the kernel’s usbfs API.

A user must have write access on the /dev/bus/usb/XXX/YYY nodes to successfully open a device. Use udev rules to configure these permissions.

For a single-user system used for development, it may be desirable to give your user account access to all USB devices by placing the following in /etc/udev/rules.d/70-plugdev-usb.rules:

SUBSYSTEM=="usb", MODE="0660", GROUP="plugdev"

This grants access on all USB devices to the plugdev group, which your user may be a member of by default on Debian/Ubuntu-based distros. If you are developing an app for others to install, you should scope the permissions more narrowly using the ATTRS{idVendor}=="ZZZZ", ATTRS{idProduct}=="ZZZZ" filters to only apply to your device.

§Windows

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 in the Windows device model, and each interface can be associated with a separate driver.

To use nusb, your device or interface must be associated with the WinUSB 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.

§macOS

nusb uses IOKit on macOS.

Users have access to USB devices by default, with no permission configuration needed. Devices with a kernel driver are not accessible.

§Async support

Many methods in nusb return a MaybeFuture type, which can either be .awaited (via IntoFuture) or .wait()ed (blocking the current thread). This allows for async usage in an async context, or blocking usage in a non-async context.

Operations such as list_devices, list_buses, DeviceInfo::open, Device::set_configuration, Device::reset, Device::claim_interface, Interface::set_alt_setting, and Endpoint::clear_halt require blocking system calls. To use these in an asynchronous context, nusb requires an async runtime to run these operations on an IO thread to avoid blocking in async code. Enable the cargo feature tokio or smol to use the corresponding runtime for blocking IO. If neither feature is enabled, .await on these methods will panic.

For blocking usage, .wait() always runs the blocking operation directly without the overhead of handing off to an IO thread.

These features do not affect and are not required for transfers, which are implemented on top of natively-async OS APIs.

Modules§

descriptors
Utilities for parsing USB descriptors.
hotplug
Types for receiving notifications when USB devices are connected or disconnected from the system.
io
Adapters for using std::io traits and their async equivalents with Bulk and Interrupt endpoints.
transfer
Transfer-related types.

Structs§

ActiveConfigurationError
Error from Device::active_configuration.
BusInfo
Information about a system USB bus.
Device
An opened USB device.
DeviceId
Opaque device identifier
DeviceInfo
Information about a device that can be obtained without opening it.
Endpoint
Exclusive access to an endpoint of a USB device.
Error
Error returned from nusb operations other than transfers.
Interface
An opened interface of a USB device.
InterfaceInfo
Summary information about a device’s interface, available before opening a device.

Enums§

ErrorKind
General category of error as part of an Error.
GetDescriptorError
Error for descriptor reads.
Speed
USB connection speed
UsbControllerType
USB host controller type

Traits§

MaybeFuture
IO that may be performed synchronously or asynchronously.

Functions§

list_buses
Get an iterator listing the system USB buses.
list_devices
Get an iterator listing the connected devices.
watch_devices
Get a Stream that yields an event when a USB device is connected or disconnected from the system.