Signatur
Beschreibung
USBAlternateInterface gehört zur WebUSB API und beschreibt genau eine alternative Einstellung einer USB-Schnittstelle. Laut USB-Spezifikation kann jede Schnittstelle (USBInterface) mehrere solche Einstellungen haben. Sie unterscheiden sich meist in der Anzahl der Endpunkte, der Paketgröße oder der Bandbreite. Ein typisches Beispiel ist eine USB-Audio- oder Videoschnittstelle, deren Einstellung 0 keine Bandbreite belegt, während die Einstellungen 1…n isochrone Endpunkte mit steigender Datenrate anbieten.
Instanzen erzeugt man in der Praxis nicht selbst. Man erhält sie über USBInterface.alternates (alle Einstellungen) oder USBInterface.alternate (die gerade aktive Einstellung). Die Kette sieht so aus: USBDevice.configuration → USBConfiguration.interfaces → USBInterface.alternates → USBAlternateInterface.endpoints.
Alle Eigenschaften sind schreibgeschützt:
alternateSetting(number): Nummer dieser Einstellung, alsobAlternateSettingaus dem Deskriptor.interfaceClass,interfaceSubclass,interfaceProtocol(number): Klassencodes laut USB-IF, z. B.0xFFfür herstellerspezifisch oder0x03für HID.interfaceName(string | null): Name der Schnittstelle aus dem String-Deskriptor, falls das Gerät einen liefert.endpoints(USBEndpoint[]): die Endpunkte dieser Einstellung.
Nützlich ist die Schnittstelle vor allem, um beim Verbinden automatisch die passende Schnittstelle und Einstellung zu finden. Das geht zum Beispiel über die Klasse 0xFF oder über das Vorhandensein bestimmter Bulk-Endpunkte. Danach aktiviert man die Einstellung mit USBDevice.selectAlternateInterface() und nutzt deren Endpunktnummern für transferIn() und transferOut().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $deviceInterface Pflicht | USBInterface | Nur beim (selten genutzten) Konstruktor new USBAlternateInterface(deviceInterface, alternateSetting): die Schnittstelle, zu der die Einstellung gehört. |
|
| $alternateSetting Pflicht | number | Nur beim Konstruktor: Nummer der alternativen Einstellung, die beschrieben werden soll. Gibt es sie nicht, wird ein RangeError geworfen. |
Rückgabewert
alternateSetting, interfaceClass, interfaceSubclass, interfaceProtocol, interfaceName und endpoints.Beispiele
Alle alternativen Einstellungen eines Geräts auflisten
// Muss durch eine Nutzeraktion ausgelöst werden (z. B. Klick)
document.querySelector('#connect').addEventListener('click', async () => {
const device = await navigator.usb.requestDevice({ filters: [] });
await device.open();
if (device.configuration === null) await device.selectConfiguration(1);
for (const iface of device.configuration.interfaces) {
for (const alt of iface.alternates) {
const hex = (n) => '0x' + n.toString(16).padStart(2, '0');
console.log(
`Interface ${iface.interfaceNumber} / Alt ${alt.alternateSetting}:`,
`Klasse ${hex(alt.interfaceClass)}`,
`Subklasse ${hex(alt.interfaceSubclass)}`,
`Protokoll ${hex(alt.interfaceProtocol)}`,
`Name: ${alt.interfaceName ?? '(keiner)'}`,
`Endpunkte: ${alt.endpoints.length}`
);
}
}
});
Herstellerspezifische Schnittstelle mit Bulk-Endpunkten finden und nutzen
const findVendorInterface = (device) => {
for (const iface of device.configuration.interfaces) {
for (const alt of iface.alternates) {
if (alt.interfaceClass !== 0xff) continue;
const epIn = alt.endpoints.find((e) => e.direction === 'in' && e.type === 'bulk');
const epOut = alt.endpoints.find((e) => e.direction === 'out' && e.type === 'bulk');
if (epIn && epOut) return { iface, alt, epIn, epOut };
}
}
return null;
};
const connect = async () => {
const device = await navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] });
await device.open();
if (device.configuration === null) await device.selectConfiguration(1);
const match = findVendorInterface(device);
if (!match) throw new Error('Keine passende Schnittstelle gefunden');
const { iface, alt, epIn, epOut } = match;
await device.claimInterface(iface.interfaceNumber);
await device.selectAlternateInterface(iface.interfaceNumber, alt.alternateSetting);
await device.transferOut(epOut.endpointNumber, new TextEncoder().encode('PING'));
const result = await device.transferIn(epIn.endpointNumber, 64);
console.log(new TextDecoder().decode(result.data));
};
Aktive Einstellung prüfen und Einstellung mit höchster Paketgröße wählen
const selectBestAlternate = async (device, interfaceNumber) => {
const iface = device.configuration.interfaces
.find((i) => i.interfaceNumber === interfaceNumber);
console.log('Aktiv vorher:', iface.alternate.alternateSetting);
// Einstellung mit der größten maximalen Paketgröße suchen
const best = iface.alternates.reduce((a, b) => {
const max = (alt) => Math.max(0, ...alt.endpoints.map((e) => e.packetSize));
return max(b) > max(a) ? b : a;
});
await device.claimInterface(interfaceNumber);
await device.selectAlternateInterface(interfaceNumber, best.alternateSetting);
console.log('Aktiv nachher:', iface.alternate.alternateSetting);
};
// Wichtig · Fallstricke
- Browserunterstützung: WebUSB gibt es nur in Chromium-basierten Browsern (Chrome, Edge, Opera, Chrome für Android). Firefox und Safari unterstützen es nicht. Vorher mit
'usb' in navigatorprüfen. - Sicherer Kontext: Die API funktioniert nur über HTTPS (oder
localhost).requestDevice()braucht eine Nutzeraktion. Über eine Permissions Policy (usb) kann die API in iframes gesperrt sein. - Aktivieren nicht vergessen: Das bloße Auslesen eines
USBAlternateInterfaceaktiviert die Einstellung nicht. Dafür braucht esclaimInterface()und anschließendselectAlternateInterface(). Erst danach gelten die Endpunkte dieser Einstellung für Übertragungen. interfaceNameistnull, wenn das Gerät keinen String-Deskriptor liefert. Den Wert also nie ungeprüft verwenden.- Geschützte Klassen: Schnittstellen bestimmter Klassen (z. B. HID
0x03, Massenspeicher0x08, Audio, Video, Smartcard) blockiert Chrome für WebUSB.claimInterface()schlägt dann mitSecurityErrorfehl. Für HID ist die WebHID API gedacht. - Unter Windows muss für das Gerät meist der WinUSB-Treiber geladen sein. Hat ein Systemtreiber die Schnittstelle schon belegt, schlägt
claimInterface()fehl.