====== 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.
→ [[lyx_-_programmiersprache:guides|Guides]] · [[lyx_-_programmiersprache:units:hardware:usb|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 ioctl
* **''USBDEVFS_BULK''** — Bulk-Transfer via ioctl
* **''USBDEVFS_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