====== 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