CosmoUsb 0.2.0

dotnet add package CosmoUsb --version 0.2.0
                    
NuGet\Install-Package CosmoUsb -Version 0.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CosmoUsb" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CosmoUsb" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="CosmoUsb" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CosmoUsb --version 0.2.0
                    
#r "nuget: CosmoUsb, 0.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CosmoUsb@0.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CosmoUsb&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=CosmoUsb&version=0.2.0
                    
Install as a Cake Tool

CosmoUsb

Cross-platform, low-level USB device access for .NET 10 — a native C# reimplementation of the Rust nusb library. CosmoUsb talks to the operating system's USB stack directly:

Platform Backend Enumeration Transfers Hotplug
Linux usbfs sysfs URBs + epoll event loop udev netlink
Windows WinUSB cfgmgr32 + hub IOCTLs overlapped I/O CM_Register_Notification
macOS IOKit / IOUSBLib IORegistry async pipe calls + CFRunLoop IOServiceAddMatchingNotification

No libusb. No shelling out. No context object to manage — a shared event loop thread starts lazily when the first device is opened.

All three backends are verified against real hardware — enumeration, device open, interface claim, control transfers, and bulk transfers (submit/timeout/cancel) — on macOS (IOKit), Windows 11 (WinUSB, ARM64 and x64), and Debian 13 (usbfs). Hotplug arrival/removal delivery is exercised end to end on macOS and Windows; on Linux the netlink watcher's setup/teardown is verified. See docs/limitations.md for the per-backend details.

Like nusb (and libusb), CosmoUsb is for devices you talk to directly — vendor-class hardware, firmware tools, instruments. Devices with standard-class kernel drivers (mass storage, HID keyboards, audio, ...) belong to the OS.

Quick start

using CosmoUsb;

// Enumerate (no device IO; data comes from the OS cache)
foreach (var device in Usb.ListDevices())
    Console.WriteLine($"{device.VendorId:X4}:{device.ProductId:X4} {device.Product}");

// Find and open
var info = Usb.FindDevice(new UsbDeviceSelector { VendorId = 0x1234, ProductId = 0x5678 });
await using var device = await info!.OpenAsync();

// Claim an interface (detaching a kernel driver first on Linux)
await using var iface = await device.DetachAndClaimInterfaceAsync(0);

// Simple transfer API
await using var outEp = iface.OpenEndpoint(0x01, UsbTransferType.Bulk, UsbDirection.Out);
await using var inEp = iface.OpenEndpoint(0x81, UsbTransferType.Bulk, UsbDirection.In);
await outEp.WriteAsync(new byte[] { 1, 2, 3 });
var buffer = new byte[4096];
int received = await inEp.ReadAsync(buffer);

Every blocking operation has both a synchronous form (Open, ClaimInterface, ControlIn, ...) and an async form (OpenAsync, ...), mirroring nusb's MaybeFuture design. Transfers are natively asynchronous on every platform.

The transfer queue

For streaming, use the submit/complete queue directly — it keeps multiple transfers in flight so the host controller never goes idle, and completions are always delivered in submission order:

for (int i = 0; i < 4; i++)
    inEp.Submit(inEp.AllocateBuffer(16384));      // never throws; errors surface below

while (true)
{
    var completion = await inEp.NextCompleteAsync(ct);   // cancel-safe
    completion.ThrowIfFailed();
    Process(completion.Buffer.AsSpan());
    completion.Buffer.SetRequestedLength(completion.Buffer.Capacity);
    inEp.Submit(completion.Buffer);               // reuse the buffer: no allocation
}

AllocateBuffer returns kernel-mapped (DMA-capable) memory on Linux and pinned-object-heap memory elsewhere, so no copying or pinning happens per transfer. IN transfer lengths must be a nonzero multiple of endpoint.MaxPacketSize.

Or wrap an endpoint in a buffered Stream:

using var reader = inEp.ToReader(16384);   // NumTransfers = 2+ for continuous streaming
reader.NumTransfers = 4;
int n = await reader.ReadAsync(buffer);

using var writer = outEp.ToWriter(16384);
await writer.WriteAsync(payload);
writer.FlushEnd();                          // terminates with a short/zero-length packet

Control transfers

var data = await iface.ControlInAsync(
    new UsbControlRequest(UsbControlType.Vendor, UsbRecipient.Interface,
        request: 0x01, value: 0x0001, index: iface.InterfaceNumber),
    length: 64, timeout: TimeSpan.FromSeconds(1));

On Windows, control transfers are only available on a claimed UsbInterface (a WinUSB restriction), and interface-recipient requests must carry the interface number in Index.

Hotplug monitoring

using var watcher = Usb.WatchDevices();   // create first...
var present = Usb.ListDevices();          // ...then list, so nothing is missed

await foreach (var change in watcher)
{
    if (change.Type == UsbDeviceChangeType.Arrived)
        Console.WriteLine($"arrived: {change.Device}");
    else
        Console.WriteLine($"removed: {change.DeviceId}");
}

Removal events carry only the UsbDeviceId; keep a dictionary if you need details at removal time.

Platform requirements

Linux

Users need write access to the device node /dev/bus/usb/BBB/DDD. Typical udev rule (/etc/udev/rules.d/70-myproduct.rules):

SUBSYSTEM=="usb", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678", MODE="0660", TAG+="uaccess"

DetachAndClaimInterface detaches a bound kernel driver (e.g. usbhid) atomically; the driver is reattached on release.

Windows

The device (or, for composite devices, the target function) must be bound to the WinUSB driver — via a WCID/MS OS descriptor in the firmware, an INF, or a tool like Zadig. Devices with other drivers (or none) can be enumerated but not opened. SetConfiguration and Reset are not available on Windows, and the manufacturer string is never cached by the OS.

macOS

Users can access USB devices by default; devices claimed by a kernel driver are not accessible. macOS only auto-configures composite-class devices — for vendor-class devices, run:

try { device.GetActiveConfiguration(); }
catch (UsbException) { device.SetConfiguration(1); }

Error model

Control-plane failures (open, claim, configure) throw UsbException with a coarse UsbErrorKind (Disconnected, Busy, PermissionDenied, NotFound, Unsupported, Other) and the raw OS code in NativeErrorCode (errno, Win32 error, or IOReturn). Data-plane failures are reported per transfer as UsbTransferStatus (Cancelled, Stall, Disconnected, Fault, InvalidArgument, Unknown) on the completion, or as UsbTransferException from the convenience methods. After a Stall, call endpoint.ClearHalt().

NativeAOT

The library uses source-generated P/Invoke throughout, no reflection, and no runtime code generation. It compiles under NativeAOT with zero trim/AOT warnings (verified; the UsbList sample builds to a ~1.4 MB self-contained binary). See docs/nativeaot.md.

Diagnostics

Diagnostics flow through a System.Diagnostics.TraceSource named "CosmoUsb"; attach a TraceListener and raise the switch level to see enumeration, transfer, and native-error details. Payload data is never logged.

Documentation

  • docs/architecture.md — layering and the per-platform event models
  • docs/transfers.md — the transfer queue, buffers, cancellation, timeouts
  • docs/linux.md, docs/windows.md, docs/macos.md — backend details
  • docs/error-handling.md — error taxonomy and native code mapping
  • docs/api-mapping.md — nusb ↔ CosmoUsb compatibility matrix
  • docs/nativeaot.md — trimming/AOT compatibility report
  • docs/performance.md — benchmarks and allocation behavior
  • docs/limitations.md — known gaps relative to nusb
  • docs/windows-scanner-setup.md — headless WinUSB binding walkthrough (worked example)

Samples

samples/ contains UsbList, UsbMonitor, UsbControlTransfer, UsbBulkTransfer, and UsbInterruptTransfer, plus FingerprintCapture — a real-world demo that drives a ZKTeco Live20R optical fingerprint scanner (1B55:0120) entirely through CosmoUsb, reproducing the vendor's reverse- engineered register protocol over control transfers and streaming a genuine 300×400 fingerprint image over bulk IN (verified on Linux and Windows hardware).

License

MIT OR Apache-2.0, matching nusb's dual license.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on CosmoUsb:

Package Downloads
CosmoBiometric.Fingerprint.Zkt

ZKTeco Live20R/SLK20R capture adapter for the CosmoBiometric fingerprint engine, driving the scanner over CosmoUsb (no vendor SDK, no libusb).

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.2.0 165 8/19/2026
0.1.0 142 8/16/2026