USB mit Lyx
Die Lyx USB-Bibliothek nutzt den Linux usbdevfs-Treiber (dev/bus/usb/) — kein libusb, keine externen Abhängigkeiten. Die 13 Units decken die gesamte USB-Stack-Breite ab: von rohen Syscalls über Descriptor-Parsing bis hin zu typsicheren Endpoint-Wrappern und URB-Pools für überlappende I/O.
→ Guides · std.hardware.usb Referenz
Architektur
usb_syscalls — open/read/write/ioctl auf /dev/bus/usb/
usb_util — Pfad-Hilfsfunktionen, String/Num-Helfer
└─ usb_discovery — Gerätesuche via /dev/bus/usb/, UsbFindDevice
usb_types — Descriptor-Strukturkonstanten
└─ usb_parse — Configuration-Parsing (Device/Config/Interface/Endpoint)
usb_control — Control-Transfers, Interface-Claim/Release
usb_bulk — Bulk-Transfers (IN/OUT)
usb_interrupt — Interrupt-Transfers (asynchron, URB-basiert)
usb_iso — Isochronous-Transfers (Audio/Video)
usb_endpoint_types — Typsichere Endpoint-Wrapper (BulkIn/BulkOut/InterruptIn)
usb_endpoint_bind — Endpoint-Registry (numerischer Schlüssel)
usb_ifc_mgr — Idempotenter Interface-Claim/Release-Manager
usb_urb_pool — URB-Pool (4 URBs, überlappende I/O)
Alle Units basieren auf dem Linux-Kernel-Interface:
USBDEVFS_CONTROL— Control-Transfer via ioctlUSBDEVFS_BULK— Bulk-Transfer via ioctlUSBDEVFS_SUBMITURB/USBDEVFS_REAPURB— Async-URB für Interrupt/ISO
Typischer Workflow
1. Gerät finden
import std.hardware.usb_discovery;
import std.hardware.usb_types;
import std.alloc;
fn main(): int64 {
// Gerät mit Vendor-ID 0x046D (Logitech) und Product-ID 0xC52B suchen:
var dev: int64 := alloc(USB_SIZEOF_DEVICE);
var rc: int64 := UsbFindDevice(0x046D, 0xC52B, dev);
if rc < 0 {
free(dev, USB_SIZEOF_DEVICE);
return -1;
}
// dev enthält jetzt den Gerätepfad und Basisinformationen
// ...
free(dev, USB_SIZEOF_DEVICE);
return 0;
}
Alternativ manueller Pfad: /dev/bus/usb/001/003 (Bus 1, Device 3).
2. Gerät öffnen und Deskriptoren lesen
import std.hardware.usb_syscalls;
import std.hardware.usb_control;
import std.hardware.usb_parse;
import std.alloc;
var dev_fd: int64 := usb_open("/dev/bus/usb/001/003"c, USB_O_RDWR);
if dev_fd < 0 { return -1; }
// Device-Descriptor lesen:
var descBuf: int64 := alloc(USB_SIZEOF_DEVICE_DESC);
UsbGetDeviceDescriptor(dev_fd, descBuf);
var vendor: int64 := UsbDevDesc_idVendor(descBuf);
var product: int64 := UsbDevDesc_idProduct(descBuf);
free(descBuf, USB_SIZEOF_DEVICE_DESC);
// Configuration-Descriptor parsen (alle Interfaces + Endpoints):
var devBuf: int64 := alloc(USB_SIZEOF_DEVICE);
UsbParseConfiguration(dev_fd, devBuf);
3. Interface beanspruchen
import std.hardware.usb_control;
import std.hardware.usb_ifc_mgr;
// Einfach direkt (ohne Manager):
UsbClaimInterface(dev_fd, 0); // Interface 0
// Oder mit idempotent-sicherem Manager:
var mgr: int64 := UsbIfcMgrAlloc(dev_fd, 0);
UsbIfcAcquire(mgr); // Claim — doppelter Aufruf ist ein No-op
// ... Arbeit ...
UsbIfcRelease(mgr); // Release
UsbIfcMgrFree(mgr);
Transfer-Typen
Bulk-Transfer
Für Massendatentransfer (USB-Sticks, Drucker, serielle Adapter).
import std.hardware.usb_bulk;
import std.alloc;
// Daten senden (OUT, Endpoint 0x01):
var data: pchar := "Hello USB"c;
var rc: int64 := UsbBulkWrite(dev_fd, 0x01, data as int64, 9, USB_TIMEOUT_DEFAULT);
// Daten empfangen (IN, Endpoint 0x81):
var buf: int64 := alloc(512);
var read: int64 := UsbBulkRead(dev_fd, 0x81, buf, 512, USB_TIMEOUT_DEFAULT);
free(buf, 512);
Typsichere Variante über usb_endpoint_types:
import std.hardware.usb_endpoint_types;
var out_ep: UsbBulkOutEndpoint := UsbMakeBulkOut(dev_fd, 0x01, USB_TIMEOUT_DEFAULT);
var in_ep: UsbBulkInEndpoint := UsbMakeBulkIn(dev_fd, 0x81, USB_TIMEOUT_DEFAULT);
if UsbBulkOutValid(out_ep) {
UsbBulkOutWrite(out_ep, data, dataLen);
}
if UsbBulkInValid(in_ep) {
UsbBulkInRead(in_ep, buf, bufLen);
}
Control-Transfer
Für Konfigurations-Requests an das Gerät (Device/Interface/Endpoint).
import std.hardware.usb_control;
import std.alloc;
var ctrlBuf: int64 := alloc(USB_CTRL_STRUCT_SIZE);
// GET_DESCRIPTOR für Interface 0:
UsbCtrlSetRequest(ctrlBuf,
USB_DIR_IN | USB_RECIP_INTERFACE, // bmRequestType
USB_REQ_GET_DESCRIPTOR, // bRequest
(USB_DT_INTERFACE << 8), // wValue
0, // wIndex = Interface 0
9 // wLength
);
var respBuf: int64 := alloc(9);
UsbCtrlSetTransfer(ctrlBuf, respBuf, 9, USB_TIMEOUT_DEFAULT);
var rc: int64 := UsbControlTransferRaw(dev_fd, ctrlBuf);
free(respBuf, 9);
free(ctrlBuf, USB_CTRL_STRUCT_SIZE);
Interrupt-Transfer (asynchron)
Für HID-Geräte (Maus, Tastatur, Gamepad) und andere periodische Daten.
import std.hardware.usb_interrupt;
import std.alloc;
// URB anlegen und einreichen:
var urbPtr: int64 := UsbAllocUrb();
var buf: int64 := alloc(64);
UsbInitInterruptRead(urbPtr, 0x81, buf, 64);
UsbSubmitUrb(dev_fd, urbPtr);
// Auf Antwort warten (500 ms):
var reapedUrb: int64 := UsbWaitForUrb(dev_fd, 500);
if reapedUrb != 0 {
var actualLen: int64 := UsbUrbActualLength(reapedUrb);
// ... buf[0..actualLen-1] auswerten ...
}
UsbFreeUrb(urbPtr);
free(buf, 64);
Für kontinuierlichen Empfang → URB-Pool verwenden:
import std.hardware.usb_urb_pool;
// Pool mit 4 URBs, je 64 Bytes, Endpoint 0x81:
var pool: int64 := UsbUrbPoolAlloc(dev_fd, 0x81, 64);
UsbUrbPoolInit(pool); // alle 4 URBs einreichen
// Poll-Schleife:
var idx: int64 := UsbUrbPoolPoll(pool, 100); // 100 ms Timeout
if idx >= 0 {
var buf: int64 := UsbUrbPoolGetBuffer(pool, idx);
var len: int64 := UsbUrbPoolGetActualLen(pool, idx);
// ... Daten verarbeiten ...
UsbUrbPoolResubmit(pool, idx); // URB für nächsten Transfer wiederverwenden
}
UsbUrbPoolFree(pool);
Isochronous-Transfer (Audio/Video)
Für USB-Audio, Webcams und andere zeitkritische Streams.
import std.hardware.usb_iso;
import std.alloc;
// ISO-URB mit 8 Paketen à 192 Bytes (USB-Audio 48kHz, 32bit):
var urbPtr: int64 := UsbAllocIsoUrb(8);
var buf: int64 := alloc(8 * 192);
UsbInitIsoRead(urbPtr, 0x83, buf, 8, 192);
UsbSubmitIsoUrb(dev_fd, urbPtr);
// Antwort holen:
var reaped: int64 := UsbReapIsoUrb(dev_fd);
if reaped != 0 {
var errors: int64 := UsbIsoErrorCount(reaped);
var i: int64 := 0;
while i < 8 {
var pkLen: int64 := UsbIsoPacketActualLen(reaped, i);
var pkStat: int64 := UsbIsoPacketStatus(reaped, i);
// ... Paket i auswerten ...
i := i + 1;
}
}
UsbFreeIsoUrb(urbPtr, 8);
free(buf, 8 * 192);
Wichtige Konstanten
Descriptor-Typen (usb_types):
| Konstante | Wert | Bedeutung |
|---|---|---|
USB_DT_DEVICE | 1 | Device Descriptor |
USB_DT_CONFIG | 2 | Configuration Descriptor |
USB_DT_INTERFACE | 4 | Interface Descriptor |
USB_DT_ENDPOINT | 5 | Endpoint Descriptor |
Transfer-Richtung und Typ:
| Konstante | Wert | Bedeutung |
|---|---|---|
USB_DIR_OUT | 0x00 | Host → Device |
USB_DIR_IN | 0x80 | Device → Host |
USB_TRANS_CONTROL | 0 | Control |
USB_TRANS_ISO | 1 | Isochronous |
USB_TRANS_BULK | 2 | Bulk |
USB_TRANS_INTERRUPT | 3 | Interrupt |
Timeouts:
| Konstante | Wert | Verwendung |
|---|---|---|
USB_TIMEOUT_DEFAULT | 5000 ms | Standard |
USB_TIMEOUT_SHORT | 1000 ms | Schnelle Geräte |
Transfer-Typ Auswahl
| Anwendungsfall | Transfer-Typ | Unit |
|---|---|---|
| USB-Stick, Drucker, serieller Adapter | Bulk | usb_bulk |
| Maus, Tastatur, Gamepad (HID) | Interrupt | usb_interrupt |
| USB-Audio, Webcam | Isochronous | usb_iso |
| Gerätekonfiguration, Descriptor lesen | Control | usb_control |
| Kontinuierlicher HID-Empfang | Interrupt + URB-Pool | usb_urb_pool |
Letzte Aktualisierung: 2026-06-08
