WebUSB API

Editor’s Draft,

This version:
https://wicg.github.io/webusb
Issue Tracking:
GitHub
Inline In Spec
Editors:
(Google Inc.)
(Google Inc.)
Participate:
Join the W3C Community Group
File Issues, send changes, through GitHub
IRC: #webusb on W3C’s IRC (Stay around for an answer, it make take a while)

Abstract

This document describes an API for securely providing access to Universal Serial Bus devices from web pages.

Status of this document

This specification was published by the Web Platform Incubator Community Group. It is not a W3C Standard nor is it on the W3C Standards Track. Please note that under the W3C Community Contributor License Agreement (CLA) there is a limited opt-out and other conditions apply. Learn more about W3C Community and Business Groups.

1. Introduction

The Universal Serial Bus (USB) is the de-facto standard for wired peripherals. Most USB devices implement one of roughly a dozen standard "device classes" which specify a way for the device to advertize the features it supports and commands and data formats for using those features. Standard device classes include keyboard, mice, audio, video and storage devices. Operating systems support such devices using the "class driver" provided by the OS vendor. There is however a long tail of devices that do not fit into one of the standardized device classes. These devices require hardware vendors to write native drivers and SDKs in order for developers to take advantage of them and this native code prevents these devices from being used by the web.

The WebUSB API provides a way to safely expose USB device services to the web. It provides an API familiar to developers who have used existing native USB libraries and exposes the device interfaces defined by existing specifications. With this API hardware manufacturers will have the ability to build cross-platform JavaScript SDKs for their devices. This will be good for the web because, instead of waiting for a new kind of device to be popular enough for browsers to provide a specific API, new and innovative hardware can be built for the web from day one.

2. Security and Privacy Considerations

The WebUSB API is a powerful feature and has the possibility to expose users to a number of new privacy and security risks. These risks can be broadly divided into three catagories that will be described in the sections below.

2.1. Abusing Access to a Device

Peripheral devices can serve a number of purposes. They may store data, as a flash drive does. They may collect information about the outside world as a camera or microphone does. They may manipulate objects in the outside world as a printer does. Each of the examples above have high-level APIs in the web platform with security features that aim to prevent their abuse by a malicious website. Storing data to or from an external drive requires the user to select the file manually. Turning on the microphone or camera requires permission from the user and may activate an indicator to let the user know data collection is in progress. Printing a document requires explicit action as well. This API provides a generic mechanism to connect to devices not covered by these existing high-level APIs and so it requires a similarly generic mechanism for preventing a malicious page from abusing a device.

The first of these protections is the requestDevice() function. The UA may display a permission prompt when this function is called. Even for a non-malicous page this action also preserves user privacy by preventing a site from connecting to a device before the user is aware that such a connection is possible. The UA may also display an indicator when a device connection is active.

Secondly, this specification requires that only secure contexts as described in [powerful-features] can access USB devices. This ensures both the authenticity of the code executing on behalf of the origin and that data read from the device may not be intercepted in transit.

2.2. Attacking a Device

Historically, unless they were created for high security applications, USB devices have been designed to trust the host they are connected to and so the host is the traditional guardian of access to the capabilities a device provides. In the development of this specification two possibilities were considered. First, the UA could notify the device of the origin from which a request originated. This would be similar to the Referrer header included in HTTP request. The difficulty of this approach is that it places the burden of access control on the device. Devices often have very limited processing and storage capabilities and so an effort was made to limit the amount of work necessary on the part of the device. The approach chosen by this specification is to instead require that the UA control access. This is done though a mechanism similiar to [CORS]. This specification defines a way for the device to provide the UA with a set of static data structures defining a set of origins that are allowed to connect to it. For devices manufactured before this specification is adopted information about allowed origins can also be provided out of band through a public device registry.

A downside of this approach is that it provides no mechanism for third-party developers to use this API with a device. We encourage device manufacturers to enable software ecosystems to develop around their devices by providing a high-level API on top of the trusted code running on their own origin. Such an API could be accessed by embedding the manufacturer’s trusted origin as an iframe within the developer’s own site. The trusted origin retains control over the device through its ability to restrict the commands the third-party site can send through it.

2.3. Attacking the Host

If a device is compromised then in addition to abusing its own capabilities the attacker may also use it to in turn attack the host to which it is connected or if the exploit is persistent any host it is connected to later. The methods above are the ways in which this specification attempts to mitigate this attack vector for once the device is under the control of an attacker (for example, by uploading a malicious firmware image) there is nothing that can be done by the UA to prevent further damage.

This specification recommends device manufacturers practice defense in depth by designing their devices to only accept signed firmware updates and/or require physical access to the device in order to apply some configuration changes.

3. WebUSB Descriptors and Requests

This specification defines descriptors and commands the UA MAY use to gather information about the device specific to implementing this API.

3.1. WebUSB Platform Capability Descriptor

A device announces support for the WebUSB command set by including the following Platform Descriptor in its Binary Object Store:

Offset Field Size Value Description
0 bLength 1 Number Size of this descriptor. Must be set to 24.
1 bDescriptorType 1 Constant DEVICE CAPABILITY descriptor type ([USB31] Table 9-6).
2 bDevCapabilityType 1 Constant PLATFORM capability type ([USB31] Table 9-14).
3 bReserved 1 Number This field is reserved and shall be set to zero.
4 PlatformCapabilityUUID 16 UUID Must be set to {3408b638-09a9-47a0-8bfd-a0768815b665}.
20 bcdVersion 2 BCD Protocol version supported. Must be set to 0x0100.
22 bVendorCode 1 Number bRequest value used for issuing WebUSB requests.
23 iLandingPage 1 Number URL descriptor index of the device’s landing page.

3.2. WebUSB Device Requests

All control transfers defined by this specification are considered to be vendor-specific requests. The bVendorCode value found in the WebUSB Platform Capability Descriptor provides the UA with the bRequest the device expects the host to use when issuing control transfers these requests. The request type is then specified in the wIndex field.

WebUSB Request Codes
Constant Value
GET_ALLOWED_ORIGINS 1
GET_URL 2

3.2.1. Get Allowed Origins

This request gets the set of origins allowed to access the device. It is analogous to the Access-Control-Allow-Origin header defined by [CORS].

The device MUST respond with data beginning with a Allowed Origins Header or stall the transfer.

A URL descriptor referenced by the response MUST be interpreted as an origin (as defined by [RFC6454]) and so content beyond the scheme/host/port triple MUST be ignored.

If the UA chooses to enforce this policy then an origin is allowed to access a device if it matches one of the origins in the top-level Allowed Origins Header or any descriptors following it. An origin is allowed to access a configuration if it matches one of the origins in the corresponding Configuration Subset Header or in the top-level Allowed Origins Header. An origin is allowed to access an interface if it matches one of the origins in the corresponding Function Subset Header, the Configuration Subset Header containing it or the top-level Allowed Origins Header.

bmRequestType bRequest wValue wIndex wLength Data
11000000B bVendorCode Zero GET_ALLOWED_ORIGINS Descriptor Length Descriptor

3.2.2. Get URL

This request fetches the URL descriptor with the given index.

The device MUST respond with the URL Descriptor at the given index or stall the transfer if the index is invalid.

bmRequestType bRequest wValue wIndex wLength Data
11000000B bVendorCode Descriptor Index GET_URL Descriptor Length Descriptor

3.3. WebUSB Descriptors

These descriptor types are returned by requests defined in this specification.

WebUSB Descriptor Types
Constant Value
WEBUSB_DESCRIPTOR_SET_HEADER 0
WEBUSB_CONFIGURATION_SUBSET_HEADER 1
WEBUSB_FUNCTION_SUBSET_HEADER 2
WEBUSB_URL 3

3.3.1. Allowed Origins Header

This header lists the set of origins allowed to access the entire USB device. It MUST be followed by bNumConfigurations configuration subset headers that control permission to access particular configurations.

This descriptor MUST be the beginning of the response to the Get Allowed Origins request. wTotalLength MUST be the total length of the response.

Offset Field Size Value Description
0 bLength 1 Number Size of this descriptor. Must be set to N + 5.
1 bDescriptorType 1 Constant WEBUSB_DESCRIPTOR_SET_HEADER.
2 wTotalLength 2 Number Total size of this and all following descriptors.
4 bNumConfigurations 1 Number Number of configuration subset headers following this descriptor.
5 iOrigin[N] N × 1 Number Set of bLength - 5 URL descriptor indicies.

3.3.2. Configuration Subset Header

This header lists the set of origins allowed to access the USB device configuration described by the configuration descriptor with the given bConfigurationValue. It MUST be followed by bNumFunctions function subset headers that control permission to access functions within this configuration.

This descriptor MUST follow a Allowed Origins Header.

Offset Field Size Value Description
0 bLength 1 Number Size of this descriptor. Must be set to N + 4.
1 bDescriptorType 1 Constant WEBUSB_CONFIGURATION_SUBSET_HEADER.
2 bConfigurationValue 1 Number Configuration to which this section applies.
3 bNumFunctions 1 Number Number of function subset headers following this descriptor.
4 iOrigin[N] N × 1 Number Set of bLength - 4 URL descriptor indicies.

3.3.3. Function Subset Header

This header lists the set of origins allowed to access the USB device interface described by the interface descriptor with a bInterfaceNumber equal to bFirstInterfaceNumber or the set of interfaces defined as a function by an interface association descriptor with an equal bFirstInterfaceNumber.

This descriptor MUST follow a Configuration Subset Header.

WebUSB Function Subset Header
Offset Field Size Value Description
0 bLength 1 Number Size of this descriptor. Must be set to N + 3.
1 bDescriptorType 1 Constant WEBUSB_FUNCTION_SUBSET_HEADER.
2 bFirstInterfaceNumber 1 Number First interface of the function to which this section applies.
3 iOrigin[N] N × 1 Number Set of bLength - 3 URL descriptor indicies.

3.3.4. URL Descriptor

This descriptor contains a single URL and is returned by the Get URL request.

Offset Field Size Value Description
0 bLength 1 Number Size of this descriptor.
1 bDescriptorType 1 Constant WEBUSB_URL.
2 bScheme 1 Number URL scheme prefix.
3 URL Variable String UTF-8 encoded URL (excluding the scheme prefix).

The bScheme field MUST be one of these values:

URL Prefixes
Value Prefix
0 "http://"
1 "https://"

3.4. Public Device Registry

The WebUSB Platform Capability Descriptor and descriptors returned by the requests defined above can be elided by publishing this information in a public registry of supported USB devices. This will allow device manufacturers to support WebUSB on existing devices.

4. Device Enumeration

dictionary USBDeviceFilter {
  unsigned short vendorId;
  unsigned short productId;
  octet classCode;
  octet subclassCode;
  octet protocolCode;
  DOMString serialNumber;
};

dictionary USBDeviceRequestOptions {
  required sequence<USBDeviceFilter> filters;
};

interface USB : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  Promise<sequence<USBDevice>> getDevices();
  Promise<USBDevice> requestDevice(USBDeviceRequestOptions options);
};

[SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute USB usb;
};

A USB device device matches a filter filter if the following steps return match:

  1. Let deviceDesc be device’s device descriptor.

  2. If filter.vendorId is present then, if deviceDesc.idVendor does not equal filter.vendorId, return mismatch.

  3. If filter.productId is present then, if deviceDesc.idProduct does not equal filter.productId, return mismatch.

  4. If filter.classCode is present then, for each of device’s interface’s interface run the following steps:

    1. Let desc be interface’s interface descriptor.

    2. If filter.classCode is present then, if desc.bInterfaceClass is not equal to filter.classCode, continue to the next interface.

    3. If filter.subclassCode is present then, if desc.bInterfaceSubClass is not equal to filter.subclassCode, continue to the next interface.

    4. If filter.protocolCode is present then, if desc.bInterfaceProtocol is not equal to filter.protocolCode, continue to the next interface.

    5. Return match.

  5. If filter.classCode is present then, if deviceDesc.bDeviceClass is not equal to filter.classCode, return mismatch.

  6. If filter.subclassCode is present then, if deviceDesc.bDeviceSubClass is not equal to filter.subclassCode, return mismatch.

  7. If filter.protocolCode is present then, if deviceDesc.bDeviceProtocol is not equal to filter.protocolCode, return mismatch.

  8. If filter.serialNumber is present then, let serialNumber be the string descriptor with index deviceDesc.iSerialNumber. If device returns an error when requesting serialNumber or serialNumber is not equal to filter.serialNumber, return mismatch.

  9. Return match.

A USBDeviceFilter filter is valid if the following steps return valid:

  1. If filter.protocolCode is present then, if filter.subclassCode is not present, return invalid.

  2. If filter.subclassCode is present then, if filter.classCode is not present, return invalid.

  3. Return valid.

The UA MUST be able to enumerate all devices attached to the system. It is, however NOT required to perform this work each time an algorithm requests an enumeration. The UA MAY cache the result of the first enumeration it performs and then begin monitoring for device connection and disconnection events, adding connected devices to its cached enumeration and removing disconnected devices. This mode of operation is preferred as it reduces the number of operating system calls made and amount of bus traffic generated by the getDevices() and requestDevice() methods.

The onconnect attribute is an Event handler IDL attribute for the connect event type.

The ondisconnect attribute is an Event handler IDL attribute for the disconnect event type.

The getDevices() method, when invoked, MUST return a new Promise and run the following steps in parallel:

  1. Enumerate all devices attached to the system. Let this result be enumerationResult.

  2. Let devices be a new empty Array.

  3. For each device in enumerationResult:

    1. If this is the first call to this method, check permissions for device.

    2. Search for an element allowedDevice in storage.allowedDevices where device is in allowedDevice@[[devices]]. If no such element exists, continue to the next device.

    3. Add the USBDevice object representing device to devices.

  4. Resolve promise with devices.

The requestDevice() method, when invoked, MUST run the following steps:

  1. Request a permission, passing:

    {
      name: "usb"
      filters: options.filters
    }
    

    Let permissionResult be the resulting Promise.

  2. Return the result of transforming permissionResult with a fullfillment handler that takes an argument result and runs the following steps:

    1. If result.devices is empty, throw a NotFoundError and abort these steps.

    2. Return result.devices[0].

To request the "usb" permission, given a USBPermissionStorage storage, a USBPermissionDescriptor options and a USBPermissionResult status, the UA MUST return a new Promise promise and run the following steps in parallel:

  1. For each filter in options.filters if filter is not a valid filter reject promise with a TypeError and abort these steps.

  2. If the algorithm is not allowed to show a popup, reject promise with a SecurityError and abort these steps.

  3. Set status.state to storage.state.

  4. If status.state is "denied", set status.devices to an empty FrozenArray, resolve promise with undefined, and abort these steps.

  5. Enumerate all devices attached to the system. Let this result be enumerationResult.

  6. Remove devices from enumerationResult if they do not match a filter in options.filters.

    The UA MAY apply additional origin-based filtering of available devices by consulting an authoritative list of device-origin mappings or referring to the origin list returned by the Get Allowed Origins request.

    The UA MAY provide additional mechanisms for blacklisting or whitelisting specific devices for arbitrary origins.

  7. Display a prompt to the user requesting they select a device from enumerationResult. The UA SHOULD show a human-readable name for each device.

  8. Wait for the user to have selected a device or cancelled the prompt.

  9. If the user cancels the prompt, set status.devices to an empty FrozenArray, resolve promise with undefined, and abort these steps.

  10. Add device to storage.

  11. Let deviceObj be the USBDevice object representing device.

  12. Set status.devices to a new FrozenArray containing deviceObj as its only element.

  13. Resolve promise with undefined.

To add an allowed USB device device to USBPermissionStorage storage, the UA MUST run the following steps:

  1. Search for an element allowedDevice in storage.allowedDevices where device is in allowedDevice@[[devices]]. If one is found, abort these steps.

  2. Let vendorId and productId be device’s vendor ID and product ID.

  3. Let serialNumber be device’s serial number if it has one, otherwise undefined.

  4. Append { vendorId: vendorId, productId: productId, serialNumber: serialNumber }, with a [[devices]] internal slot containing a single entry device to storage.allowedDevices.

To check permissions for a new USB device device, given a USBPermissionStorage storage, the UA MUST run the following steps:

  1. Let vendorId and productId be device’s vendor ID and product ID.

  2. Let serialNumber be device’s if it has one, otherwise undefined.

  3. Search for an element allowedDevice in storage.allowedDevices where:

  4. If no such element exists, return null.

  5. Add device to allowedDevice@[[devices]].

  6. Return allowedDevice.

4.1. Events

dictionary USBConnectionEventInit : EventInit {
    required USBDevice device;
};

[Constructor(DOMString type, USBConnectionEventInit eventInitDict)]
interface USBConnectionEvent : Event {
  [SameObject] readonly attribute USBDevice device;
};

When the UA detects a new USB device device connected to the host it MUST perform the following steps for each script execution environment:

  1. Let storage be the USBPermissionStorage object in the current script execution environment.

  2. Check permissions for device with storage and let allowedDevice be the result.

  3. If allowedDevice is null, abort these steps.

  4. Let deviceObj be the USBDevice object representing device.

  5. Let event be a new USBConnectionEvent, with the device attribute set to deviceObj.

  6. Fire an event named connect on usb, using event as the event object.

When the UA detects a USB device device has been disconnected from the host it MUST perform the following steps for each script execution environment:

  1. Let storage be the USBPermissionStorage object in the current script execution environment.

  2. Search for an element allowedDevice in storage.allowedDevices where device is in allowedDevice@[[devices]], if no such element exists, abort these steps.

  3. Remove device from allowedDevice@[[devices]].

  4. If allowedDevice.serialNumber is undefined and allowedDevice@[[devices]] is empty remove allowedDevice from storage.allowedDevices.

  5. Let device be the USBDevice object representing device.

  6. Let event be a new USBConnectionEvent, with the device attribute set to device.

  7. Fire an event named disconnect on usb, using event as the event object.

4.2. Permission API Integration

The [permissions] API provides a uniform way for websites to request permissions from users and query which permissions they have.

The "usb" permission is defined as follows:

permission descriptor type
dictionary USBPermissionDescriptor : PermissionDescriptor {
  sequence<USBDeviceFilter> filters;
};
permission storage type
USBPermissionStorage, defined as:
dictionary AllowedUSBDevice {
  required octet vendorId;
  required octet productId;
  DOMString serialNumber;
};

dictionary USBPermissionStorage : PermissionStorage {
  required sequence<AllowedUSBDevice> allowedDevices = [];
};

AllowedUSBDevice instances have an internal slot [[devices]] that holds an array of USB devices.

permission result type
interface USBPermissionResult : PermissionStatus {
  attribute FrozenArray<USBDevice> devices;
};
permission query algorithm
To query the "usb" permission with a USBPermissionDescriptor desc, a USBPermissionStorage storage, and a USBPermissionResult status, the UA must:
  1. If desc.filters is set then, for each filter in desc.filters if filter is not a valid filter then raise a TypeError and abort these steps.

  2. Set status.state to "prompt".

  3. Let matchingDevices be a new Array.

  4. For each allowedDevice in storage.allowedDevices and for each device in allowedDevice@[[devices]], run the following substeps:

    1. If desc.filters is set and device does not match a filter in desc.filters, continue to the next device.

    2. Get the USBDevice representing device and add it to matchingDevices.

  5. Set status.devices to a new FrozenArray whose contents are matchingDevices.

permission request algorithm
Request the "usb" permission.

5. Device Usage

interface USBDevice {
  readonly attribute octet usbVersionMajor;
  readonly attribute octet usbVersionMinor;
  readonly attribute octet usbVersionSubminor;
  readonly attribute octet deviceClass;
  readonly attribute octet deviceSubclass;
  readonly attribute octet deviceProtocol;
  readonly attribute unsigned short vendorId;
  readonly attribute unsigned short productId;
  readonly attribute octet deviceVersionMajor;
  readonly attribute octet deviceVersionMinor;
  readonly attribute octet deviceVersionSubminor;
  readonly attribute DOMString? manufacturerName;
  readonly attribute DOMString? productName;
  readonly attribute DOMString? serialNumber;
  readonly attribute USBConfiguration? configuration;
  readonly attribute FrozenArray<USBConfiguration> configurations;
  readonly attribute boolean opened;
  Promise<void> open();
  Promise<void> close();
  Promise<void> selectConfiguration(octet configurationValue);
  Promise<void> claimInterface(octet interfaceNumber);
  Promise<void> releaseInterface(octet interfaceNumber);
  Promise<void> selectAlternateInterface(octet interfaceNumber, octet alternateSetting);
  Promise<USBInTransferResult> controlTransferIn(USBControlTransferParameters setup, unsigned short length);
  Promise<USBOutTransferResult> controlTransferOut(USBControlTransferParameters setup, optional BufferSource data);
  Promise<void> clearHalt(USBDirection direction, octet endpointNumber);
  Promise<USBInTransferResult> transferIn(octet endpointNumber, unsigned long length);
  Promise<USBOutTransferResult> transferOut(octet endpointNumber, BufferSource data);
  Promise<USBIsochronousInTransferResult> isochronousTransferIn(octet endpointNumber, sequence<unsigned long> packetLengths);
  Promise<USBIsochronousOutTransferResult> isochronousTransferOut(octet endpointNumber, BufferSource data, sequence<unsigned long> packetLengths);
  Promise<void> reset();
};

The usbVersionMajor, usbVersionMinor and usbVersionSubminor attributes declare the USB protocol version supported by the device. They SHALL correspond to the value of the bcdUSB field of the device descriptor such that a value of 0xJJMN has major version JJ, minor version M and subminor version N.

The deviceClass, deviceSubclass and deviceProtocol attributes declare the communication interface supported by the device. They MUST correspond respectively to the values of the bDeviceClass, bDeviceSubClass and bDeviceProtocol fields of the device descriptor.

The vendorId and productId MUST be equal to the device’s vendor ID and product ID.

The deviceVersionMajor, deviceVersionMinor and deviceVersionSubminor attributes declare the device release number as defined by the device manufacturer. It SHALL correspond to the value of the bcdDevice field of the device descriptor such that a value of 0xJJMN has major version JJ, minor version M and subminor version N.

The manufacturerName, productName and serialNumber attributes SHOULD contain the values of the string descriptors indexed by the iManufacturer, iProduct and iSerialNumber fields of the device descriptor if each is defined.

The configuration attribute contains the currently selected configuration for the device and SHALL be one of the configurations listed in configurations. It MAY be null if the device is in an unconfigured state and MUST be updated by selectConfiguration().

The configurations attribute contains a list of configurations supported by the device. These configurations SHALL be populated from the configuration descriptors reported by the device and the number of elements in this list SHALL match the value of the bNumConfigurations field of the device descriptor.

The opened attribute SHALL be set to true when the device is opened by the current execution context and SHALL be set to false otherwise.

The open() method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. Let device be the target USBDevice object.

  2. If device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  3. If device.opened is true resolve promise and abort these steps.

  4. Perform the necessary platform-specific steps to begin a session with the device. If these fail for any reason reject promise with a NetworkError and abort these steps.

  5. Set device.opened to true and resolve promise.

The close() method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. Let device be the target USBDevice object.

  2. If device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  3. If device.opened is false resolve promise and abort these steps.

  4. Abort all other algorithms currently running against this device and reject their associated promises with an AbortError.

  5. Perform the necessary platform-specific steps to release any claimed interfaces as if releaseInterface(interfaceNumber) had been called for each claimed interface.

  6. Perform the necessary platform-specific steps to end the session with the device.

  7. Set device.opened to false and resolve promise.

The selectConfiguration(configurationValue) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. Let device be the target USBDevice object.

  2. If device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  3. Let configuration be the device configuration with bConfigurationValue equal to configurationValue. If no such configuration exists, reject promise with a NotFoundError and abort these steps.

  4. If device.opened is not equal to true reject promise with an InvalidStateError and abort these steps.

  5. The UA MAY check that the caller is allowed to access configuration, and if not reject promise with a SecurityError and abort these steps.

  6. Abort all transfers currently scheduled on endpoints other than the default control pipe and reject their associated promises with a AbortError.

  7. Issue a SET_CONFIGURATION control transfer to the device to set configurationValue as its active configuration. If this step fails reject promise with a NetworkError and abort these steps.

  8. Set device.configuration to configuration and resolve promise.

The claimInterface(interfaceNumber) method, when invoked, MUST return a new Promise and run the following steps in parallel:

  1. If the device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  2. Let interface be the interface in the active configuration with bInterfaceNumber equal to interfaceNumber. If no such interface exists, reject promise with a NotFoundError and abort these steps.

  3. If device.opened is not true and interface.claimed is not false, reject promise with an InvalidStateError and abort these steps.

  4. The UA MAY check that the caller is allowed to access interface, and if not reject promise with a SecurityError and abort these steps.

  5. Perform the necessary platform-specific steps to request exclusive control over interface. If this fails, reject promise with a NetworkError and abort these steps.

  6. Set interface.claimed to true and resolve promise.

The releaseInterface(interfaceNumber) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. Let device be the target USBDevice object.

  2. If device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  3. Let interface be the interface in the active configuration with bInterfaceNumber equal to interfaceNumber. If no such interface exists, reject promise with a NotFoundError and abort these steps.

  4. If device.opened or interface.claimed is not true, reject promise with an InvalidStateError and abort these steps.

  5. Perform the necessary platform-specific steps to reliquish exclusive control over interface.

  6. Set interface.claimed to false and resolve promise.

The selectAlternateInterface(interfaceNumber, alternateSetting) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. Let device be the target USBDevice object.

  2. If device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  3. Let interface be the interface in the active configuration with bInterfaceNumber equal to interfaceNumber. If no such interface exists, reject promise with a NotFoundError and abort these steps.

  4. If device.opened or interface.claimed is not true, reject promise with an InvalidStateError and abort these steps.

  5. Abort all transfers currently scheduled on endpoints associated with the previously selected alternate setting of interface and reject their associated promises with a AbortError.

  6. Issue a SET_INTERFACE control transfer to the device to set alternateSetting as the current configuration of interface. If this step fails reject promise with a NetworkError and abort these steps.

  7. Resolve promise.

The controlTransferIn(setup, length) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. Let device be the target USBDevice object.

  2. If device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  3. If device.opened is not equal to true reject promise with an InvalidStateError and abort these steps.

  4. Check the validity of the control transfer parameters and abort these steps if promise is rejected.

  5. If length is greater than the wMaxPacketSize0 field of the device’s device descriptor, reject promise with a TypeError and abort these steps.

  6. Let result be a new USBInTransferResult and let buffer be a new ArrayBuffer of length bytes.

  7. Issue a control transfer with the setup packet parameters provided in setup and the data transfer direction in bmRequestType set to "device to host" and wLength set to length.

  8. If the device responds with data, store the first length bytes of this data in buffer and set result.data to a new DataView constructed over buffer.

  9. If the device responds by stalling the default control pipe set result.status to "stall".

  10. If more than length bytes are received set result.status to "babble" and otherwise set it to "ok".

  11. If the transfer fails for any other reason reject promise with a NetworkError and abort these steps.

  12. Resolve promise with result.

The controlTransferOut(setup, data) method, when invoked, must return a new Promise promise and run the following steps in parallel:

  1. If the device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  2. If device.opened is not equal to true reject promise with an InvalidStateError and abort these steps.

  3. Check the validity of the control transfer parameters and abort these steps if promise is rejected.

  4. If data.length is greater than the wMaxPacketSize0 field of the device’s device descriptor, reject promise with a TypeError and abort these steps.

  5. Issue a control transfer with the setup packet populated by setup and the data transfer direction in bmRequestType set to "host to device" and wLength set to data.length. Transmit data in the data stage of the transfer.

  6. Let result be a new USBOutTransferResult.

  7. If the device responds by stalling the default control pipe set result.status to "stall".

  8. If the device acknowledges the transfer set result.status to "ok" and result.{{USBOutTransferResult/bytesWritten to data.length.

  9. If the transfer fails for any other reason reject promise with a NetworkError and abort these steps.

  10. Resolve promise with result.

The clearHalt(direction, endpointNumber) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. If the device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  2. Let endpoint be the endpoint in the active configuration with bEndpointAddress corresponding to direction and endpointNumber. If no such endpoint exists reject promise and abort these steps.

  3. If device.opened or interface.claimed is not true, reject promise with an InvalidStateError and abort these steps.

  4. Issue a CLEAR_FEATURE control transfer to the device to clear the stall condition on endpoint.

  5. On failure reject promise with a NetworkError, otherwise resolve promise.

The transferIn(endpointNumber, length) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. If the device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  2. Let endpoint be the IN endpoint in the active configuration with bEndpointAddress corresponding to endpointNumber. If there is no such endpoint reject promise with a NotFoundError and abort these steps.

  3. If endpoint is not a bulk or interrupt endpoint reject promise with an InvalidAccessError and abort these steps.

  4. If device.opened or interface.claimed is not true, reject promise with an InvalidStateError and abort these steps.

  5. As appropriate for endpoint enqueue a bulk or interrupt IN transfer on endpoint with a buffer sufficient to receive length bytes of data from the device.

  6. Let result be a new USBInTransferResult.

  7. If data is returned as part of this transfer let buffer be a new ArrayBuffer of exactly the length of the data received and set result.data to a new DataView constructed over buffer.

  8. If the device responds with more than length bytes of data set result.status to "babble".

  9. If the transfer ends because endpoint is stalled set result.status to "stall".

  10. If the device acknowledges the complete transfer set result.status to "ok".

  11. If the transfer fails for any other reason reject promise with a NetworkError and abort these steps.

  12. Resolve promise with result.

The transferOut(endpointNumber, data) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. If the device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  2. Let endpoint be the OUT endpoint in the active configuration with bEndpointAddress corresponding to endpointNumber. If there is no such endpoint reject promise with a NotFoundError and abort these steps.

  3. If endpoint is not a bulk or interrupt endpoint reject promise with an InvalidAccessError and abort these steps.

  4. If device.opened or interface.claimed is not true, reject promise with an InvalidStateError and abort these steps.

  5. As appropriate for endpoint enqueue a bulk or interrupt OUT transfer on endpoint to transmit data to the device.

  6. Let result be a new USBOutTransferResult.

  7. Set result.bytesWritten to the amount of data successfully sent to the device.

  8. If the endpoint is stalled set result.status to "stall".

  9. If the device acknowledges the complete transfer set result.status to "ok".

  10. If the transfer fails for any other reason reject promise with a NetworkError and abort these steps.

  11. Resolve promise with result.

The isochronousTransferIn(endpointNumber, packetLengths) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. If the device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  2. Let endpoint be the IN endpoint in the active configuration with bEndpointAddress corresponding to endpointNumber. If there is no such endpoint reject promise with a NotFoundError and abort these steps.

  3. If endpoint is not an isochronous endpoint reject promise with an InvalidAccessError and abort these steps.

  4. If device.opened or interface.claimed is not true, reject promise with an InvalidStateError and abort these steps.

  5. Let length be the sum of the elements of packetLengths.

  6. Let buffer be a new ArrayBuffer of length bytes.

  7. Let result be a new USBIsochronousInTransferResult and set result.data to a new DataView constructed over buffer.

  8. Enqueue an isochronous IN transfer on endpoint that will write up to length bytes of data from the device into buffer.

  9. For each packet i from 0 to packetLengths.length - 1:

    1. Let packet be a new USBIsochronousInTransferPacket and set result.packets[i] to packet.

    2. Let view be a new DataView over the portion of buffer containing the data received from the device for this packet and set packet.data to view.

    3. If the device responds with more than packetLengths[i] bytes of data set packet.status to "babble".

    4. If the transfer ends because endpoint is stalled set packet.status to "stall".

    5. If the device acknowledges the complete transfer set packet.status to "ok".

    6. If the transfer fails for any other reason reject promise with a NetworkError and abort these steps.

  10. Resolve promise with result.

The isochronousTransferOut(endpointNumber, data, packetLengths) method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. If the device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  2. Let endpoint be the OUT endpoint in the active configuration with bEndpointAddress corresponding to endpointNumber. If there is no such endpoint reject promise with a NotFoundError and abort these steps.

  3. If endpoint is not an isochronous endpoint reject promise with an InvalidAccessError and abort these steps.

  4. If device.opened or interface.claimed is not true, reject promise with an InvalidStateError and abort these steps.

  5. Let length be the sum of the elements of packetLengths.

  6. Let result be a new USBIsochronousOutTransferResult.

  7. Enqueue an isochronous OUT transfer on endpoint that will write buffer to the device, divided into packetLength.length packets of packetLength[i] bytes (for packets i from 0 to packetLengths.length - 1).

  8. For each packet i from 0 to packetLengths.length - 1 the host attempts to send to the device:

    1. Let packet be a new USBIsochronousOutTransferPacket and set result.packets[i] to packet.

    2. Let packet.bytesWritten be the amount of data successfully sent to the device as part of this packet.

    3. If the transfer ends because endpoint is stalled set packet.status to "stall".

    4. If the device acknowledges the complete transfer set packet.status to "ok".

    5. If the transfer fails for any other reason reject promise with a NetworkError and abort these steps.

  9. Resolve promise with result.

The reset() method, when invoked, MUST return a new Promise promise and run the following steps in parallel:

  1. Let device be the target USBDevice object.

  2. If device is no longer connected to the system, reject promise with a NotFoundError and abort these steps.

  3. If device.opened is not equal to true reject promise with an InvalidStateError and abort these steps.

  4. Abort all operations on the device and reject their associated promises with an AbortError.

  5. Perform the necessary platform-specific operation to soft reset the device.

  6. On failure reject promise with a NetworkError, otherwise resolve promise.

What configuration is the device in after it resets? <https://github.com/wicg/webusb/issues/36>

5.1. Transfers

enum USBRequestType {
  "standard",
  "class",
  "vendor"
};

enum USBRecipient {
  "device",
  "interface",
  "endpoint",
  "other"
};

enum USBTransferStatus {
  "ok",
  "stall",
  "babble"
};

dictionary USBControlTransferParameters {
  required USBRequestType requestType;
  required USBRecipient recipient;
  required octet request;
  required unsigned short value;
  required unsigned short index;
};

[Constructor(USBTransferStatus status, optional DataView? data)]
interface USBInTransferResult {
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[Constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0)]
interface USBOutTransferResult {
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[Constructor(USBTransferStatus status, optional DataView? data)]
interface USBIsochronousInTransferPacket {
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[Constructor(sequence<USBIsochronousInTransferPacket> packets, optional DataView? data)]
interface USBIsochronousInTransferResult {
  readonly attribute DataView? data;
  readonly attribute FrozenArray<USBIsochronousInTransferPacket> packets;
};

[Constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0)]
interface USBIsochronousOutTransferPacket {
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[Constructor(sequence<USBIsochronousOutTransferPacket> packets)]
interface USBIsochronousOutTransferResult {
  readonly attribute FrozenArray<USBIsochronousOutTransferPacket> packets;
};

A control transfer is a special class of USB traffic most commonly used for configuring a device. It consists of three stages: setup, data and status. In the setup stage a setup packet is transmitted to the device containing request parameters including the transfer direction and size of the data to follow. In the data stage that data is either sent to or received from the device. In the status stage successful handling of the request is acknowledged or a failure is signaled.

All USB devices MUST have a default control pipe which is endpointNumber 0.

The requestType attribute populates part of the bmRequestType field of the setup packet to indicate whether this request is part of the USB standard, a particular USB device class specification or a vendor-specific protocol.

The recipient attribute populates part of the bmRequestType field of the setup packet to indicate whether the control transfer is addressed to the entire device, or a specific interface or endpoint.

The request attribute populates the bRequest field of the setup packet. Valid requests are defined by the USB standard, USB device class specifications or the device vendor.

The value and index attributes populate the wValue and wIndex fields of the setup packet respectively. The meaning of these fields depends on the request being made.

To check the validity of the control transfer parameters perform the following steps:

  1. Let setup be the USBControlTransferParameters created for the transfer.

  2. Let promise be the promise created for the transfer.

  3. Let configuration be the active configuration. If the device is not configured abort these steps.

  4. If setup.recipient is "device" or "other" the UA MAY check that the caller is allowed to access configuration, and if not reject promise with a SecurityError and abort these steps.

  5. If setup.recipient is "interface", perform the following steps:

    1. Let interfaceNumber be the lower 8 bits of setup.index.

    2. Let interface be the interface in the | configuration| with bInterfaceNumber equal to interfaceNumber. If no such interface exists, reject promise with a NotFoundError and abort these steps.

    3. If interface.claimed is not equal to true, reject promise with an InvalidStateError and abort these steps.

    4. The UA MAY check that the caller is allowed to access interface, and if not reject promise with a SecurityError.

  6. If setup.recipient is "endpoint", run the following steps:

    1. Let endpointNumber be defined as the lower 4 bits of setup.index.

    2. Let direction be defined as "in" if the 8th bit of setup.index is 1 and "out" otherwise.

    3. Let endpoint be the endpoint in the active configuration with bEndpointAddress corresponding to direction and endpointNumber. If no such endpoint exists, reject promise with a NotFoundError and abort these steps.

    4. Let interface be the interface in which endpoint is defined. If interface.claimed is not equal to true, reject promise with an InvalidStateError.

    5. The UA MAY check that the caller is allowed to access interface, and if not reject promise with a SecurityError.

5.2. Configurations

[Constructor(USBDevice device, octet configurationValue)]
interface USBConfiguration {
  readonly attribute octet configurationValue;
  readonly attribute DOMString? configurationName;
  readonly attribute FrozenArray<USBInterface> interfaces;
};

Each device configuration SHALL have a unique configurationValue that matches the bConfigurationValue fields of the configuration descriptor that defines it.

The configurationName attribute SHOULD contain the value of the string descriptor referenced by the iConfiguration field of the configuration descriptor, if defined.

The interfaces attribute SHALL contain a list of interfaces exposed by this device configuration. These interfaces SHALL by populated by collecting the interface descriptors contained within this configuration descriptor and organizing them by bInterfaceNumber.

Include some non-normative information about device configurations. <https://github.com/wicg/webusb/issues/46>

5.3. Interfaces

[Constructor(USBConfiguration configuration, octet interfaceNumber)]
interface USBInterface {
  readonly attribute octet interfaceNumber;
  readonly attribute USBAlternateInterface alternate;
  readonly attribute FrozenArray<USBAlternateInterface> alternates;
  readonly attribute boolean claimed;
};

[Constructor(USBInterface deviceInterface, octet alternateSetting)]
interface USBAlternateInterface {
  readonly attribute octet alternateSetting;
  readonly attribute octet interfaceClass;
  readonly attribute octet interfaceSubclass;
  readonly attribute octet interfaceProtocol;
  readonly attribute DOMString? interfaceName;
  readonly attribute FrozenArray<USBEndpoint> endpoints;
};

Each interface provides a collection of alternates identified by a single bInterfaceNumber field found in their interface descriptors. The interfaceNumber attribute MUST match this field.

The alternate attribute SHALL be set to the USBAlternateInterface that is currently selected for this interface, which by default SHALL be the one with bAlternateSetting equal to 0.

The alternates attribute SHALL contain a list of all alternate interface configurations available for this interface.

The claimed attribute SHALL be set to true when the interface is claimed by the current execution context and SHALL be set to false otherwise.

Each alternative interface configuration SHALL have a unique alternateSetting within a given interface that matches the bAlternateSetting field of the interface descriptor that defines it.

The interfaceClass, interfaceSubclass and interfaceProtocol attributes declare the communication interface supported by the interface. They MUST correspond respectively to the values of the bInterfaceClass, bInterfaceSubClass and bInterfaceProtocol fields of the interface descriptor.

The interfaceName attribute SHOULD contain the value of the string descriptor indexed by the iInterface field of the interface descriptor, if defined.

The endpoints attribute SHALL contain a list of endpoints exposed by this interface. These endpoints SHALL by populated from the endpoint descriptors contained within this interface descriptor and the number of elements in this sequence SHALL match the value of the bNumEndpoints field of the interface descriptor.

A device’s active configuration is the combination of the USBConfiguration selected by calling selectConfiguration(configurationValue) and the set of USBAlternateInterfaces selected by calling selectAlternateInterface(interfaceNumber, alternateSetting). A device MAY, by default, be left in an unconfigured state, referred to as configuration 0 or may automatically be set to whatever configuration has bConfigurationValue equal to 1. When a configuration is set all interfaces within that configuration automatically have the USBAlternateInterface with bAlternateSetting equal to 0 selected by default. It is therefore unnecessary to call selectAlternateInterface(interfaceNumber, alternateSetting) with alternateSetting equal to 0 for each interface when opening a device.

5.4. Endpoints

enum USBDirection {
  "in",
  "out"
};

enum USBEndpointType {
  "bulk",
  "interrupt",
  "isochronous"
};

[Constructor(USBAlternateInterface alternate, octet endpointNumber, USBDirection direction)]
interface USBEndpoint {
  readonly attribute octet endpointNumber;
  readonly attribute USBDirection direction;
  readonly attribute USBEndpointType type;
  readonly attribute unsigned long packetSize;
};

Each endpoint within a particular device configuration SHALL have a unique combination of endpointNumber and direction. The endpointNumber MUST equal the 4 least significant bits of the bEndpointAddress field of the endpoint descriptor defining the endpoint.

The direction attribute declares the transfer direction supported by this endpoint and is equal to "in" if the most significant bit of the bEndpointAddress is set and "out" otherwise. An endpoint may either carry data IN from the device to host or OUT from host to device.

The type attribute declares the type of data transfer supported by this endpoint.

The packetSize attribute declares the packet size employed by this endpoint and MUST be equal to the value of the wMaxPacketSize of the endpoint descriptor defining it. In a High-Speed, High-Bandwidth endpoint this value will include the multiplication factor provided by issuing multiple transactions per microframe. In a SuperSpeed device this value will include the multiplication factor provided by the bMaxBurst field of the SuperSpeed Endpoint Companion descriptor.

6. Terminology

This specification uses several terms taken from [USB31]. While reference is made to version 3.1 of the Universal Serial Bus many of these concepts exist in previous versions as well. Significant differences between USB versions that have bearing on this specification will be called out explicitly.

Descriptors are binary data structures that can be read from a device and describe its properties and function:

The Binary Object Store (BOS) is an additional set of descriptors that are more free-form than the standard device descriptors. Of note is the Platform Descriptor type which allows third parties (such as this specification) to declare their own types of descriptors. Each of these is identified by a UUID. The Binary Object Store is described in section 9.6.2 of [USB31].

A USB device has a single device descriptor which links to one or more configuration descriptors. It’s vendor ID is assigned to the device manufacturer by the USB-IF and is stored in the idVendor field of the device descriptor. It’s product ID is assigned by the manufacturer and is stored in the idProduct field of the device descriptor. It’s serial number is an optional property that is defined if the iSerialNumber field of the device descriptor is not equal to 0 and is the string descriptor referred to by that index.

Conformance

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.github.io/ecma262/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[PERMISSIONS]
Mounir Lamouri; Marcos Caceres. The Permissions API. URL: https://w3c.github.io/permissions/
[PROMISES-GUIDE]
Domenic Denicola. Writing Promise-Using Specifications. 16 February 2016. Finding of the W3C TAG. URL: https://www.w3.org/2001/tag/doc/promises-guide
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[WebIDL]
Cameron McCormack; Boris Zbarsky; Tobie Langel. Web IDL. URL: https://heycam.github.io/webidl/
[WHATWG-DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/

Informative References

[CORS]
Anne van Kesteren. Cross-Origin Resource Sharing. 16 January 2014. REC. URL: https://www.w3.org/TR/cors/
[POWERFUL-FEATURES]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[RFC6454]
A. Barth. The Web Origin Concept. December 2011. Proposed Standard. URL: https://tools.ietf.org/html/rfc6454
[USB31]
Universal Serial Bus 3.1 Specification. 26 July 2013. URL: http://www.usb.org/developers/docs/

IDL Index

dictionary USBDeviceFilter {
  unsigned short vendorId;
  unsigned short productId;
  octet classCode;
  octet subclassCode;
  octet protocolCode;
  DOMString serialNumber;
};

dictionary USBDeviceRequestOptions {
  required sequence<USBDeviceFilter> filters;
};

interface USB : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  Promise<sequence<USBDevice>> getDevices();
  Promise<USBDevice> requestDevice(USBDeviceRequestOptions options);
};

[SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute USB usb;
};

dictionary USBConnectionEventInit : EventInit {
    required USBDevice device;
};

[Constructor(DOMString type, USBConnectionEventInit eventInitDict)]
interface USBConnectionEvent : Event {
  [SameObject] readonly attribute USBDevice device;
};

dictionary USBPermissionDescriptor : PermissionDescriptor {
  sequence<USBDeviceFilter> filters;
};

dictionary AllowedUSBDevice {
  required octet vendorId;
  required octet productId;
  DOMString serialNumber;
};

dictionary USBPermissionStorage : PermissionStorage {
  required sequence<AllowedUSBDevice> allowedDevices = [];
};

interface USBPermissionResult : PermissionStatus {
  attribute FrozenArray<USBDevice> devices;
};

interface USBDevice {
  readonly attribute octet usbVersionMajor;
  readonly attribute octet usbVersionMinor;
  readonly attribute octet usbVersionSubminor;
  readonly attribute octet deviceClass;
  readonly attribute octet deviceSubclass;
  readonly attribute octet deviceProtocol;
  readonly attribute unsigned short vendorId;
  readonly attribute unsigned short productId;
  readonly attribute octet deviceVersionMajor;
  readonly attribute octet deviceVersionMinor;
  readonly attribute octet deviceVersionSubminor;
  readonly attribute DOMString? manufacturerName;
  readonly attribute DOMString? productName;
  readonly attribute DOMString? serialNumber;
  readonly attribute USBConfiguration? configuration;
  readonly attribute FrozenArray<USBConfiguration> configurations;
  readonly attribute boolean opened;
  Promise<void> open();
  Promise<void> close();
  Promise<void> selectConfiguration(octet configurationValue);
  Promise<void> claimInterface(octet interfaceNumber);
  Promise<void> releaseInterface(octet interfaceNumber);
  Promise<void> selectAlternateInterface(octet interfaceNumber, octet alternateSetting);
  Promise<USBInTransferResult> controlTransferIn(USBControlTransferParameters setup, unsigned short length);
  Promise<USBOutTransferResult> controlTransferOut(USBControlTransferParameters setup, optional BufferSource data);
  Promise<void> clearHalt(USBDirection direction, octet endpointNumber);
  Promise<USBInTransferResult> transferIn(octet endpointNumber, unsigned long length);
  Promise<USBOutTransferResult> transferOut(octet endpointNumber, BufferSource data);
  Promise<USBIsochronousInTransferResult> isochronousTransferIn(octet endpointNumber, sequence<unsigned long> packetLengths);
  Promise<USBIsochronousOutTransferResult> isochronousTransferOut(octet endpointNumber, BufferSource data, sequence<unsigned long> packetLengths);
  Promise<void> reset();
};

enum USBRequestType {
  "standard",
  "class",
  "vendor"
};

enum USBRecipient {
  "device",
  "interface",
  "endpoint",
  "other"
};

enum USBTransferStatus {
  "ok",
  "stall",
  "babble"
};

dictionary USBControlTransferParameters {
  required USBRequestType requestType;
  required USBRecipient recipient;
  required octet request;
  required unsigned short value;
  required unsigned short index;
};

[Constructor(USBTransferStatus status, optional DataView? data)]
interface USBInTransferResult {
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[Constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0)]
interface USBOutTransferResult {
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[Constructor(USBTransferStatus status, optional DataView? data)]
interface USBIsochronousInTransferPacket {
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[Constructor(sequence<USBIsochronousInTransferPacket> packets, optional DataView? data)]
interface USBIsochronousInTransferResult {
  readonly attribute DataView? data;
  readonly attribute FrozenArray<USBIsochronousInTransferPacket> packets;
};

[Constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0)]
interface USBIsochronousOutTransferPacket {
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[Constructor(sequence<USBIsochronousOutTransferPacket> packets)]
interface USBIsochronousOutTransferResult {
  readonly attribute FrozenArray<USBIsochronousOutTransferPacket> packets;
};

[Constructor(USBDevice device, octet configurationValue)]
interface USBConfiguration {
  readonly attribute octet configurationValue;
  readonly attribute DOMString? configurationName;
  readonly attribute FrozenArray<USBInterface> interfaces;
};

[Constructor(USBConfiguration configuration, octet interfaceNumber)]
interface USBInterface {
  readonly attribute octet interfaceNumber;
  readonly attribute USBAlternateInterface alternate;
  readonly attribute FrozenArray<USBAlternateInterface> alternates;
  readonly attribute boolean claimed;
};

[Constructor(USBInterface deviceInterface, octet alternateSetting)]
interface USBAlternateInterface {
  readonly attribute octet alternateSetting;
  readonly attribute octet interfaceClass;
  readonly attribute octet interfaceSubclass;
  readonly attribute octet interfaceProtocol;
  readonly attribute DOMString? interfaceName;
  readonly attribute FrozenArray<USBEndpoint> endpoints;
};

enum USBDirection {
  "in",
  "out"
};

enum USBEndpointType {
  "bulk",
  "interrupt",
  "isochronous"
};

[Constructor(USBAlternateInterface alternate, octet endpointNumber, USBDirection direction)]
interface USBEndpoint {
  readonly attribute octet endpointNumber;
  readonly attribute USBDirection direction;
  readonly attribute USBEndpointType type;
  readonly attribute unsigned long packetSize;
};

Issues Index

What configuration is the device in after it resets? <https://github.com/wicg/webusb/issues/36>
Include some non-normative information about device configurations. <https://github.com/wicg/webusb/issues/46>