Signatur
Beschreibung
controlTransferIn() führt einen Control-Transfer über den Default-Endpunkt 0 aus, bei dem Daten vom Gerät zum Host fließen. Mit solchen Transfers liest man Deskriptoren, fragt Status- oder Konfigurationswerte ab oder ruft herstellerspezifische Befehle auf, die eine Antwort liefern. Welche Anfrage gesendet wird, legt das Objekt im Parameter setup fest (Request-Typ, Empfänger, Request-Code, value und index). Diese Werte entsprechen dem Setup-Paket aus der USB-Spezifikation.
Das Gerät muss vorher mit open() geöffnet werden. Ist der Empfänger "interface" oder "endpoint", muss außerdem das betreffende Interface per claimInterface() beansprucht sein. Der Parameter length gibt an, wie viele Bytes höchstens erwartet werden. Das Gerät darf weniger senden.
Das zurückgegebene Promise wird mit einem USBInTransferResult erfüllt. Dessen Eigenschaft data ist ein DataView mit den empfangenen Bytes. status ist "ok", "stall" (das Gerät hat die Anfrage abgelehnt) oder "babble" (das Gerät hat mehr Daten gesendet als angefordert). Das Promise wird nur bei echten Fehlern verworfen, etwa wenn das Gerät nicht geöffnet ist oder die Übertragung fehlschlägt. Ein Stall führt dagegen nicht zum Verwerfen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $setup Pflicht | USBControlTransferParameters | Objekt, das das Setup-Paket beschreibt:
|
|
| $length Pflicht | number | Maximale Anzahl Bytes, die vom Gerät gelesen werden sollen (wLength, 0 bis 65535). |
Rückgabewert
USBInTransferResult erfüllt wird. Es enthält data (DataView, die empfangenen Bytes) und status ("ok", "stall" oder "babble").Beispiele
Geräte-Deskriptor lesen (Standard-Request GET_DESCRIPTOR)
const readDeviceDescriptor = async () => {
// Muss durch eine Nutzeraktion ausgelöst werden (z. B. Klick)
const device = await navigator.usb.requestDevice({ filters: [] });
await device.open();
const result = await device.controlTransferIn({
requestType: 'standard',
recipient: 'device',
request: 0x06, // GET_DESCRIPTOR
value: 0x0100, // Deskriptortyp 1 (Gerät), Index 0
index: 0x0000
}, 18); // Geräte-Deskriptor ist 18 Byte lang
if (result.status === 'ok') {
const view = result.data;
const vendorId = view.getUint16(8, true);
const productId = view.getUint16(10, true);
console.log(`VID: 0x${vendorId.toString(16)}, PID: 0x${productId.toString(16)}`);
}
await device.close();
};
document.querySelector('#connect').addEventListener('click', readDeviceDescriptor);
Herstellerspezifischen Status über ein Interface abfragen
const readVendorStatus = async (device) => {
await device.open();
if (device.configuration === null) {
await device.selectConfiguration(1);
}
await device.claimInterface(0);
const { status, data } = await device.controlTransferIn({
requestType: 'vendor',
recipient: 'interface',
request: 0x01, // gerätespezifischer Befehl "Status lesen"
value: 0x0000,
index: 0x0000 // Interface-Nummer 0
}, 64);
if (status === 'stall') {
console.warn('Gerät hat die Anfrage abgelehnt');
return null;
}
// Empfangene Bytes als Uint8Array auslesen
const bytes = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
console.log('Empfangen:', bytes.length, 'Bytes', bytes);
return bytes;
};
Fehlerbehandlung mit try/catch und Stall-Behandlung
const safeControlIn = async (device, setup, length) => {
try {
const result = await device.controlTransferIn(setup, length);
if (result.status === 'stall') {
// Bei Control-Transfers setzt sich der Stall mit dem nächsten Setup-Paket
// automatisch zurück, clearHalt() ist hier nicht nötig
throw new Error('Request vom Gerät nicht unterstützt (STALL)');
}
if (result.status === 'babble') {
throw new Error('Gerät hat mehr Daten gesendet als erwartet');
}
return result.data;
} catch (err) {
// z. B. InvalidStateError (Gerät nicht geöffnet) oder NetworkError
console.error(`${err.name}: ${err.message}`);
return null;
}
};
// Wichtig · Fallstricke
- Browser-Unterstützung: WebUSB gibt es nur in Chromium-Browsern (Chrome, Edge, Opera) auf dem Desktop und unter Android. Firefox und Safari unterstützen es nicht. Vor der Nutzung mit
'usb' in navigatorprüfen. - Sicherer Kontext: Die API ist nur über HTTPS (oder
localhost) verfügbar.requestDevice()braucht eine Nutzeraktion. In iframes muss die Permissions-Policyusberlaubt sein. - Stall führt nicht zum Verwerfen: Lehnt das Gerät die Anfrage ab, wird das Promise trotzdem erfüllt, nur mit
status: "stall". Den Status also immer prüfen. - Interface beanspruchen: Beim Empfänger
"interface"oder"endpoint"ohne vorherigesclaimInterface()wird das Promise mitInvalidStateErrorverworfen. - Gesperrte Geräteklassen: Geräte, die das Betriebssystem bereits beansprucht (z. B. HID-Tastaturen, Massenspeicher), und Geräte auf der Sperrliste sind nicht zugänglich. Unter Windows ist oft der WinUSB-Treiber nötig.
- Byte-Reihenfolge: USB-Deskriptoren sind Little-Endian, daher bei
DataView.getUint16()das zweite Argumenttrueübergeben.data.byteLengthkann kleiner sein alslength.