WebUSB API

커뮤니티 그룹 초안 보고서,

현재 버전:
https://wicg.github.io/webusb
이슈 추적:
GitHub
명세 내 인라인
에디터:
(Google LLC)
(Google LLC)
(Google LLC)
참여하기:
W3C 커뮤니티 그룹 가입
IRC: W3C의 #webusb (답변을 기다리세요. 시간이 걸릴 수 있습니다)
StackOverflow에서 질문하기

초록

이 문서는 웹 페이지에서 범용 직렬 버스(USB) 장치에 안전하게 접근할 수 있도록 하는 API를 설명합니다.

이 문서의 상태

이 명세는 Web Platform Incubator Community Group에서 발행하였습니다. 이는 W3C 표준도 아니며, W3C 표준 트랙에도 속하지 않습니다. W3C Community Contributor License Agreement (CLA)에 따라 제한적 옵트아웃 및 기타 조건이 적용됨을 유의하시기 바랍니다. W3C 커뮤니티 및 비즈니스 그룹에 대해 더 알아보세요.

1. 소개

이 절은 비규범적(non-normative)입니다.

범용 직렬 버스(USB)는 유선 주변기기의 사실상 표준입니다. 대부분의 USB 장치는 약 12개의 표준 "디바이스 클래스" 중 하나를 구현합니다. 이들은 장치가 지원하는 기능을 광고하고, 해당 기능 사용을 위한 명령 및 데이터 포맷을 규정합니다. 표준 디바이스 클래스에는 키보드, 마우스, 오디오, 비디오 및 저장 장치 등이 포함됩니다. 운영 체제는 이러한 장치를 OS 공급자가 제공하는 "클래스 드라이버"를 이용해 지원합니다. 하지만 표준화된 디바이스 클래스에 속하지 않는 장치도 많이 존재합니다. 이러한 장치는 하드웨어 공급업체가 자체 드라이버와 SDK를 직접 작성해야 하며, 이 네이티브 코드는 해당 장치가 웹에서 사용되는 것을 어렵게 만듭니다.

WebUSB API는 USB 장치 서비스를 웹에 안전하게 노출할 수 있는 방법을 제공합니다. 기존 네이티브 USB 라이브러리를 사용해 본 개발자들이 익숙하게 느낄 수 있는 API와 기존 명세에 정의된 디바이스 인터페이스를 노출합니다. 이 API를 사용하면 하드웨어 제조사는 자체 장치용 크로스플랫폼 JavaScript SDK를 만들 수 있습니다. 이로 인해 새로운 유형의 장치가 브라우저에 특정 API가 제공될 만큼 대중화될 때까지 기다릴 필요 없이, 웹을 위한 혁신적인 하드웨어가 처음부터 바로 개발될 수 있게 됩니다.

USB에 대한 자세한 정보는 § 10 부록: USB 간략 소개를 참고하세요.

2. 적용 동기 예시

이 절은 비규범적(non-normative)입니다.

2.1. 교육용 장치

웹의 소프트웨어 전달 모델은 플랫폼 호환성이나 관리자 권한 문제 없이 어디서든 빠르게 로드할 수 있기 때문에 교육용 애플리케이션을 가능케 하는 핵심 요소입니다. 과학 수업에서는 컴퓨터 기반 측정 및 데이터 로깅을 활용하고 있습니다. 이러한 도구들은 번들 소프트웨어를 필요로 하지만, 새로운 네이티브 앱을 추가할 때마다 IT 부서의 부담이 커지기 때문에 관리되는 컴퓨터에 설치하기 힘들 수 있습니다. 웹 기반 하드웨어 API를 사용하면 이런 장치 지원을 기존 온라인 강의 자료에 직접 빌드할 수 있어, 완전히 매끄러운 경험을 제공합니다.

다양한 마이크로컨트롤러 개발 키트로 코딩을 배우는 학생들은 온라인 개발 도구를 사용하여 코드를 작성 및 업로드할 수 있습니다. 이미 이러한 도구가 존재하지만 브라우저와 하드웨어를 연결하려면 네이티브 컴포넌트가 별도로 필요합니다. 이 네이티브 확장들은 진입 장벽을 높이고, 샌드박스 기반 웹 환경 내 코드에서는 노출되지 않는 보안 취약점에 사용자를 노출시킬 수 있습니다.

2.2. 웹 드라이버(Web Drivers)

웹의 조합성(composability)은 완전히 웹 기술만으로 새로운 하드웨어 지원 생태계를 구축할 수 있게 합니다. 3D 프린터 예시를 들면, 3D 오브젝트 디자인을 호스팅하는 사이트가 프린트 기능을 직접 통합하고 싶어할 수 있습니다. 웹은 2D 프린팅은 지원하지만 3D 프린팅용 API는 없습니다. 제조사가 WebUSB API를 사용하여 자신의 프린터에 데이터를 보내는 임베드 페이지를 제공한다면, 사이트들은 지도 삽입 기능처럼 해당 페이지를 활용하여 하드웨어 지원을 쉽게 추가할 수 있습니다.

2.3. 장치 업데이트 및 진단

블루투스와 같은 무선 프로토콜이 소비자 장치에 더 편리하게 쓰이기도 하지만, USB 포트는 전원 공급에 용이하며 장치에 문제가 있을 때 최후의 연결수단으로 계속 널리 사용되고 있습니다. 하드웨어 제조사가 지원 웹사이트에 업데이트/진단 도구를 통합하면 고객이 어떤 플랫폼에서도 도구를 사용할 수 있으며, 웹사이트를 통해 지원을 받을 때 더 나은 진단 데이터를 수집할 수 있습니다. 랜딩 페이지는 사용자가 적절한 도움을 받을 수 있도록 제조사가 해당 웹사이트의 올바른 위치로 안내할 수 있는 방법을 제공합니다.

3. 보안 및 개인정보 보호 고려사항

이 절은 비규범적(non-normative)입니다.

WebUSB API는 강력한 기능을 제공하는 동시에, 사용자가 새로운 보안·개인정보 침해 위험에 노출될 수 있습니다. 이러한 위험은 크게 세 가지로 구분되며, 아래의 절에서 다룹니다.

3.1. 장치 접근권 남용

주변기기는 다양한 용도로 사용될 수 있습니다. 플래시 드라이브처럼 데이터를 저장할 수도 있고, 카메라나 마이크처럼 외부 정보를 수집할 수도 있으며, 프린터처럼 외부 세계에 영향을 줄 수도 있습니다. 이런 예시들은 모두 악의적인 사이트로 인한 오남용을 막기 위해 웹 플랫폼에서 고수준 API에 보안 장치를 마련하고 있습니다. 예를 들어 외장 드라이브로의 데이터 저장은 사용자가 파일을 직접 선택해야 하고, 마이크나 카메라 켜기는 사용자 허가가 필요하며, 장치 활성시 표시등이 켜질 수 있습니다. 프린트 또한 직접 사용자 조작이 필요합니다. 이 API는 기존 고수준 API들이 다루지 않는 장치에도 범용적으로 연결할 수 있도록 하지만, 악의적 웹 페이지의 오남용을 막는 범용적 보호장치 역시 필요합니다.

첫 번째 보호장치는 requestDevice() 함수입니다. UA는 이 함수 호출 시 권한 요청 페이지(프롬프트)를 표시할 수 있습니다. 이 동작은 악의적 웹페이지뿐 아니라, 사용자가 연결 가능한 장치가 있다는 사실을 미리 알지 못하는 경우에도 연결 전 허가를 받음으로써 개인정보 보호도 돕습니다. UA는 장치 연결이 활성 상태일 때 인디케이터(표시)를 표시할 수도 있습니다.

둘째로, 이 명세는 [powerful-features]에서 설명된 '보안 컨텍스트'에서만 USB 장치 접근을 허용하도록 요구합니다. 이를 통해 오리진에서 실행 중인 코드의 신뢰성과 장치에서 읽은 데이터가 전송 중에 탈취당하지 않음을 보장합니다.

끝으로, USB 장치들은 여러 주체의 요청을 구별할 수 없기 때문에 운영체제는 단일 USB 인터페이스를 동시에 하나의 사용자 공간 또는 커널 공간 드라이버만 가지도록 허용합니다. UA는 사용자 공간 드라이버 역할을 하므로, 한 번에 하나의 실행 컨텍스트만 USB 인터페이스를 점유할 수 있습니다. claimInterface() 함수는 여러 실행 컨텍스트가 동시에 인터페이스를 점유하려 할 경우 실패합니다.

3.2. 장치 공격

과거에는 높은 보안을 요구하는 경우가 아니라면 대부분의 USB 장치는 자신이 연결된 호스트를 신뢰하도록 설계됐으며, 호스트가 전통적으로 장치가 제공하는 기능에 대한 접근을 통제해왔습니다. 이 명세를 개발하며 두 가지 대안을 고려했습니다. 첫째, UA가 요청 발생 오리진을 장치에 알릴 수 있습니다. 이는 HTTP 요청에 첨부되는 Referrer 헤더와 유사합니다. 문제는 이 방식이 접근 통제의 부담을 장치에 부과한다는 점입니다. 많은 장치가 매우 제한된 처리능력/저장공간만 제공하므로, 장치가 담당해야 할 작업량은 최대한 줄이려고 했습니다.

이 명세 초안 단계에서 채택된 초기 방법은 UA가 [CORS]와 비슷한 메커니즘을 통해 접근을 제어하도록 요구하는 것이었습니다. 장치는 자신이 허용하는 오리진 리스트를 UA에 정적 데이터 구조로 제공할 수 있는 기능을 갖추거나, 기존 장치 지원을 위해 퍼블릭 레지스트리에 제공할 수도 있다고 제안되었습니다.

이 방법의 문제점은 두 가지입니다. 첫째, 제조사는 WebUSB를 고려해 신제품을 만들어야 하거나, 명확히 명세하기 힘든 퍼블릭 레지스트리 시스템에 의존해야 합니다. 제품 개발 주기는 길고, 에디터 초안 단계인 현 명세는 시장에 영향을 미칠만한 권위를 갖지 않습니다. 둘째, 서드파티 개발자가 이 API로 장치를 사용할 방법이 없으므로 혁신과 개발자 수 모두 한계가 있었습니다.

이 옵션들을 모두 검토한 끝에, 작성자들은 requestDevice() 방식으로 권한 프롬프트를 제공하고 § 8.1 권한 정책(Permissions Policy)과 연동하는 현 구조면 충분한 장치 보안 대책이 된다고 결론지었습니다.

3.3. 호스트 공격

만약 장치가 해킹된다면, 그 자체 기능 오남용 외에도 장치를 통해 연결된 호스트(혹은 향후 연결될 모든 호스트)를 다시 공격받을 수 있습니다. 위의 방법들은 이 명세가 이런 공격 경로를 최소화하려는 방식들입니다. 그러나 일단 장치가 공격자에게 넘어가면(예, 악의적 펌웨어 이미지를 업로드), UA가 추가 피해를 막을 방법은 없습니다.

따라서 이 명세는 디바이스 제조사가 방어적 설계를 채택할 것을 권장합니다. 예를 들어 서명된 펌웨어 업데이트만 적용하거나, 일부 설정 변경에는 물리적 접근을 요구하는 식입니다.

4. WebUSB 디스크립터 및 요청

이 명세는 UA가 API 구현시 장치에 대해 얻을 수 있는 정보를 위해 사용할 수 있는 디스크립터와 명령을 정의합니다.

4.1. WebUSB 플랫폼 기능 디스크립터(Platform Capability Descriptor)

장치는 다음 Platform Descriptor를 자신의 Binary Object Store에 포함시켜 WebUSB 명령 세트 지원을 알립니다:

Offset Field Size Value 설명
0 bLength 1 Number 이 디스크립터의 크기. 반드시 24로 설정해야 합니다.
1 bDescriptorType 1 Constant DEVICE CAPABILITY 디스크립터 타입 ([USB31] Table 9-6).
2 bDevCapabilityType 1 Constant PLATFORM capability 타입 ([USB31] Table 9-14).
3 bReserved 1 Number 예약 필드, 반드시 0이어야 합니다.
4 PlatformCapabilityUUID 16 UUID {3408b638-09a9-47a0-8bfd-a0768815b665}로 설정되어야 함.
20 bcdVersion 2 BCD 지원 프로토콜 버전. 반드시 0x0100.
22 bVendorCode 1 Number WebUSB 요청을 수행할 때 사용하는 bRequest 값.
23 iLandingPage 1 Number 장치의 랜딩 페이지의 URL 디스크립터 인덱스.

iLandingPage 필드가 0이 아닌 경우, 이는 제조사가 사용자가 자신의 장치를 제어하기 위해 방문하길 권장하는 랜딩 페이지가 있음을 의미합니다. UA는 장치 연결 시 이 URL로 이동할 것을 사용자에게 안내할 수 있습니다.

참고: USB는 리틀 엔디안 버스이므로 [RFC4122]에 따라, 위 UUID는 전송 시 바이트 시퀀스 {0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}로 보내야 합니다.

4.2. WebUSB 장치 요청

이 명세가 정의한 모든 제어 전송(control transfer) 은 벤더 전용 요청으로 간주합니다. WebUSB 플랫폼 기능 디스크립터bVendorCode 값은 호스트가 장치에 제어 전송 을 할 때 사용할 bRequest 값을 제공합니다. 요청 타입은 wIndex 필드에 정의됩니다.

WebUSB 요청 코드
Constant Value
(Reserved) 1
GET_URL 2

4.2.1. URL 얻기(Get URL)

이 요청은 지정된 인덱스의 URL 디스크립터를 가져옵니다.

장치는 해당 인덱스의 URL 디스크립터를 반환하거나, 인덱스가 유효하지 않으면 전송을 스톨(stall)해야 합니다.

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

4.3. WebUSB 디스크립터

이 명세서에 정의된 요청에 의해 반환되는 디스크립터 타입입니다.

WebUSB 디스크립터 타입
Constant Value
(Reserved) 0-2
WEBUSB_URL 3

4.3.1. URL 디스크립터

이 디스크립터는 한 개의 URL을 포함하며, Get URL 요청에 의해 반환됩니다.

Offset Field Size Value 설명
0 bLength 1 Number 이 디스크립터의 크기.
1 bDescriptorType 1 Constant WEBUSB_URL.
2 bScheme 1 Number URL scheme prefix.
3 URL Variable String UTF-8로 인코딩된 URL(스킴 프리픽스 제외).

bScheme 필드는 다음 값 중 하나여야 합니다:

URL 접두사(Prefix)
Value Prefix
0 "http://"
1 "https://"
255 ""

특수 값 255는 scheme을 포함한 전체 URL이 URL 필드에 인코딩되어 있음을 나타냅니다.

5. 장치 나열

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

dictionary USBDeviceRequestOptions {
  required sequence<USBDeviceFilter> filters;
  sequence<USBDeviceFilter> exclusionFilters = [];
};

[Exposed=(Worker,Window), SecureContext]
interface USB : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  Promise<sequence<USBDevice>> getDevices();
  [Exposed=Window] Promise<USBDevice> requestDevice(USBDeviceRequestOptions options);
};

[Exposed=Window, SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute USB usb;
};

[Exposed=Worker, SecureContext]
partial interface WorkerNavigator {
  [SameObject] readonly attribute USB usb;
};
이 예제에서는 UI에 포함될 몇몇 장치를 검색합니다. 페이지가 처음 로드될 때, getDevices()를 호출하여 이미 연결된 장치에 대한 접근 권한이 있는지 확인해야 합니다.
document.addEventListener('DOMContentLoaded', async () => {
  let devices = await navigator.usb.getDevices();
  devices.forEach(device => {
    // |device|를 UI에 추가합니다.
  });
});

페이지가 로드된 후에도 사용자가 장치를 연결하거나 연결 해제할 수 있으므로, UI 상태를 최신으로 유지하려면 스크립트에서도 이러한 이벤트를 등록해야 합니다.

navigator.usb.addEventListener('connect', event => {
  // |event.device|를 UI에 추가합니다.
});

navigator.usb.addEventListener('disconnect', event => {
  // |event.device|를 UI에서 제거합니다.
});

사용자가 페이지를 처음 방문한 경우에는 어떤 장치에도 접근 권한이 없으므로, 관련 전역 객체일시적 활성화가 있을 때, 먼저 requestDevice()를 호출해야 합니다. 이 예제에서는 vendor가 0xABCD이고 vendor-specific subclass 0x01을 가진 장치를 지원합니다.

let button = document.getElementById('request-device');
button.addEventListener('click', async () => {
  let device;
  try {
    device = await navigator.usb.requestDevice({ filters: [{
        vendorId: 0xABCD,
        classCode: 0xFF, // vendor-specific
        protocolCode: 0x01
    }]});
  } catch (err) {
    // 장치가 선택되지 않았습니다.
  }

  if (device !== undefined) {
    // |device|를 UI에 추가합니다.
  }
});

이 명세에서 정의된 메서드는 일반적으로 비동기로 완료되며, USB 작업 소스(task source)에 작업을 큐잉합니다.

USB 장치 device디바이스 필터에 일치한다 filter 는 아래 단계가 match를 반환하면 참입니다:

  1. deviceDescdevice디바이스 디스크립터로 둡니다.

  2. filter.vendorId 가 있으면 deviceDesc.idVendor와 같지 않으면 mismatch를 반환합니다.

  3. filter.productId 가 있으면 deviceDesc.idProduct와 같지 않으면 mismatch를 반환합니다.

  4. filter.serialNumber 가 있으면, serialNumber문자열 디스크립터deviceDesc.iSerialNumber로 둡니다. deviceserialNumber 요청에 오류를 반환하거나, serialNumberfilter.serialNumber와 다르면 mismatch를 반환합니다.

  5. filter.classCode 가 있으면, device 내 어디든 interfacefilter인터페이스 필터 일치라면 match를 반환합니다.

  6. filter.classCode 가 있으면 deviceDesc.bDeviceClass가 다르면 mismatch를 반환합니다.

  7. filter.subclassCode 가 있으면 deviceDesc.bDeviceSubClass가 다르면 mismatch를 반환합니다.

  8. filter.protocolCode 가 있으면 deviceDesc.bDeviceProtocol가 다르면 mismatch를 반환합니다.

  9. match를 반환합니다.

참고: 위 단계는 디바이스 디스크립터bDeviceClass, bDeviceSubClass, bDeviceProtocol 필드를 인터페이스 디스크립터에서 비교하는 것처럼 처리합니다.

USB 인터페이스 interface인터페이스 필터에 일치 filter 이려면 다음 단계가 match를 반환해야 합니다:

  1. descinterface인터페이스 디스크립터로 둡니다.

  2. filter.classCode 가 있으면 desc.bInterfaceClass가 다르면 mismatch를 반환합니다.

  3. filter.subclassCode 가 있으면 desc.bInterfaceSubClass가 다르면 mismatch를 반환합니다.

  4. filter.protocolCode 가 있으면 desc.bInterfaceProtocol가 다르면 mismatch를 반환합니다.

  5. match를 반환합니다.

USBDeviceFilter filter유효한 필터이다가 아래 단계가 valid를 반환하면 참입니다:

  1. filter.productId 가 있으나, filter.vendorId 가 없다면 invalid 반환.

  2. filter.subclassCode 가 있으나, filter.classCode 가 없다면 invalid 반환.

  3. filter.protocolCode 가 있으나, filter.subclassCode 가 없다면 invalid 반환.

  4. valid 반환.

UA는 시스템에 연결된 모든 장치를 나열할 수 있어야 합니다. 단, 알고리즘마다 매번 나열을 수행할 필요는 없습니다. UA는 최초 나열 결과를 캐싱한 뒤 장치 연결/해제 이벤트를 모니터링하고, 연결된 장치는 캐시된 나열에 추가하고, 해제된 장치는 제거할 수 있습니다. 이런 동작 방식이 OS 호출 횟수와 버스 트래픽을 줄이기 때문에 바람직합니다. 이 모드는 getDevices()requestDevice() 호출로 발생하는 시스템 부하를 낮춥니다.

onconnect 속성은 connect 이벤트 타입의 이벤트 핸들러 IDL 속성입니다.

ondisconnect 속성은 disconnect 이벤트 타입의 이벤트 핸들러 IDL 속성입니다.

getDevices() 메서드는 호출될 때 반드시 다음 단계를 실행해야 합니다:
  1. globalthis관련 전역 객체로 둡니다.

  2. documentglobal연결된 Document로 하고, 연결된 Document가 없으면 null로 둡니다.

  3. storage를 다음과 같이 둡니다:

    1. 연관된 service worker client의 스크립트 실행 환경에 있는 USBPermissionStorage 객체, 만약 globalServiceWorkerGlobalScope라면.

    2. 그 외의 경우, 현재 스크립트 실행 환경의 USBPermissionStorage 객체.

  4. promise새로운 promise로 둡니다.

  5. 다음 단계를 병렬로 실행합니다:

    1. 시스템에 연결된 모든 장치 나열을 수행하고, 이 결과를 enumerationResult로 둡니다.

    2. devices를 새로운 빈 Array로 둡니다.

    3. enumerationResult의 각 device에 대해 다음을 수행:

      1. devicedocument에 대해 차단 목록(blocklisted)이면, continue합니다.

      2. 이 메서드의 첫 번째 호출이라면 storage로 device의 권한 확인을 수행합니다.

      3. storage.allowedDevices 중에서 deviceallowedDevice.[[devices]]에 존재하는 요소를 탐색하고, 없다면 다음 device로 넘어갑니다.

    4. device를 나타내는 USBDevice 객체를 devices에 추가합니다.

    5. 글로벌 작업을 대기열에 추가하여 USB 작업 소스global과 함께 promisedevicesresolve 하도록 합니다.

  6. promise를 반환합니다.

requestDevice(options) 메서드는 호출될 때 다음 단계를 반드시 실행해야 합니다:
  1. 다음 기술(descriptor)에 대한 권한을 요청합니다.

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

    permissionResult를 결과로 나오는 Promise로 둡니다.

  2. permissionResultresult와 함께 이행(fulfillment)될 때 다음 단계를 실행합니다:

    1. result.devices 가 비어 있다면, "NotFoundError" DOMException 을 발생시키고, 이 단계를 중단합니다.

    2. result.devices[0]을 반환합니다.

"usb" permission 요청 시, Document document, USBPermissionStorage storage, USBPermissionDescriptor optionsUSBPermissionResult status가 주어지면 UA는 반드시 다음 단계를 실행해야 합니다:
  1. globalstorage관련 전역 객체로 둡니다.

  2. filter에 대해 options.filters 내에 filter유효하지 않은 필터라면, 거부된 promiseTypeError와 함께 반환합니다.

  3. exclusionFilter에 대해 options.exclusionFilters 내에 exclusionFilter유효하지 않은 필터라면, 거부된 promiseTypeError와 함께 반환합니다.

  4. 이 알고리즘이 global일시적 활성화(transient activation) 상태에서 트리거됐는지 확인합니다. 아니라면, 거부된 promise를 "SecurityError" DOMException과 함께 반환합니다.

  5. promise새로운 promise로 둡니다.

  6. 다음 단계를 병렬로 실행합니다.

    1. 시스템에 연결된 모든 장치 나열을 수행하고, 결과를 enumerationResult로 둡니다.

    2. document에 대해 차단 목록에 포함된 장치는 enumerationResult에서 제거합니다.

    3. options.filters 중 어느 필터에도 일치하지 않는 장치는 enumerationResult에서 제거합니다.

    4. options.exclusionFilters 중 어느 필터에 일치하는 장치는 enumerationResult에서 제거합니다.

    5. 사용자에게 enumerationResult에서 장치를 선택하라는 안내 프롬프트를 표시합니다. UA는 각 장치의 사람이 알아볼 수 있는 이름을 표시해야 합니다.

    6. 사용자가 device를 선택하거나 프롬프트를 취소할 때까지 대기합니다.

    7. 글로벌 작업을 대기열에 USB 작업 소스 global에 추가하여 다음 단계를 실행:

      1. status.state 값을 "ask"로 설정합니다.

      2. 사용자가 프롬프트를 취소했다면 status.devices 를 빈 FrozenArray로 하고, resolve promise with undefined하여 이 단계를 중단합니다.

      3. devicestorage에 추가합니다.

      4. deviceObjdevice를 나타내는 USBDevice 객체로 둡니다.

      5. status.devices 값을 deviceObj 하나만 들어있는 새로운 FrozenArray로 설정합니다.

      6. resolve promise with undefined합니다.

  7. promise를 반환합니다.

허용된 USB deviceUSBPermissionStorage storage에 추가하려면, UA는 반드시 다음 단계를 실행해야 합니다:

  1. storage.allowedDevices 중에서 deviceallowedDevice@[[devices]]에 포함된 요소를 찾고, 존재한다면 이 단계를 중단합니다.

  2. vendorIdproductIddevicevendor IDproduct ID로 둡니다.

  3. serialNumberdeviceserial number로, 없다면 undefined로 둡니다.

  4. { vendorId: vendorId, productId: productId, serialNumber: serialNumber }[[devices]] 슬롯에 device만 포함된 상태로 storage.allowedDevices에 추가합니다.

새로운 USB device의 권한 확인 시, USBPermissionStorage storage가 주어진 경우, UA는 반드시 다음 단계를 실행해야 합니다:

  1. vendorIdproductIddevicevendor IDproduct ID로 둡니다.

  2. serialNumberdevice에 있으면 그 값으로, 없으면 undefined로 둡니다.

  3. storage.allowedDevices 내에서 다음 조건을 모두 만족하는 allowedDevice를 찾습니다:

    • allowedDevice.vendorId 값이 vendorId와 같습니다.

    • allowedDevice.productId 값이 productId와 같습니다.

    • allowedDevice.serialNumber 값이 serialNumber와 같습니다.

  4. 이런 요소가 없으면 null을 반환합니다.

  5. deviceallowedDevice@[[devices]]에 추가합니다.

  6. allowedDevice를 반환합니다.

허용된 USB device를 제거하려면, USBPermissionStorage storage가 주어진 경우 UA는 반드시 다음 단계를 실행해야 합니다:

  1. storage.allowedDevices 내에서 deviceallowedDevice@[[devices]] 에 포함된 요소를 찾고, 없으면 중단합니다.

  2. storage.allowedDevices에서 allowedDevice를 제거합니다.

5.1. 이벤트

dictionary USBConnectionEventInit : EventInit {
    required USBDevice device;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBConnectionEvent : Event {
  constructor(DOMString type, USBConnectionEventInit eventInitDict);
  [SameObject] readonly attribute USBDevice device;
};

참고: Workers는 connectdisconnect 이벤트에 대한 이벤트 리스너를 등록할 수 있으나, 워커가 활성 상태가 아니면 이벤트 리스너는 호출되지 않습니다.

UA가 호스트에 새 USB 장치 device가 연결된 것을 감지하면 각 스크립트 실행 환경에 대해 다음 단계를 수행해야 합니다:

  1. 현재 스크립트 실행 환경에서 storageUSBPermissionStorage 객체로 둡니다.

  2. device에 대한 권한을 확인하고 결과를 allowedDevice로 둡니다.

  3. 만약 allowedDevicenull이면, 이 단계를 중단합니다.

  4. deviceObjdevice를 나타내는 USBDevice 객체로 둡니다.

  5. 이벤트 발생을 사용하여 connect라는 이름의 이벤트를 device관련 전역 객체Navigator 객체의 usb에서 발생시키되, USBConnectionEvent를 사용하고, device 속성을 deviceObj로 설정합니다.

UA가 호스트에서 USB 장치 device가 분리된 것을 감지하면 각 스크립트 실행 환경에 대해 다음 단계를 수행해야 합니다:

  1. 현재 스크립트 실행 환경에서 storageUSBPermissionStorage 객체로 둡니다.

  2. storage.allowedDevices에서 deviceallowedDevice의 [[devices]]에 있는 요소 allowedDevice를 검색합니다. 그런 요소가 없으면 이 단계를 중단합니다.

  3. deviceallowedDevice의 [[devices]]에서 제거합니다.

  4. 만약 allowedDeviceserialNumberundefined이고 allowedDevice의 [[devices]]가 비어있다면, allowedDevicestorage.allowedDevices에서 제거합니다.

  5. devicedevice를 나타내는 USBDevice 객체로 둡니다.

  6. 이벤트 발생을 사용하여 disconnect라는 이름의 이벤트를 device관련 전역 객체Navigator 객체의 usb에서 발생시키되, USBConnectionEvent를 사용하고, device 속성을 device로 설정합니다.

6. 장치 사용법

이 예에서는 최대 8개의 16비트 데이터 채널을 지원하는 데이터 로깅 장치를 가정합니다. 단일 구성(bConfigurationValue 1) 과 단일 인터페이스(bInterfaceNumber 1), 단일 벌크 엔드포인트(bEndpointAddress 0x81 — 즉 endpoint 1 이고 IN 엔드포인트)을 가집니다. 데이터가 샘플링되면 이 엔드포인트에서 이용할 수 있습니다. 이 엔드포인트의 최대 패킷 크기는 모든 8개 채널이 동시에 활성화되는 것을 지원하기 위해 16바이트입니다. 그러나 버스 대역폭을 절약하기 위해 임의의 채널 조합을 활성화하거나 비활성화할 수 있습니다. 패킷은 전송된 데이터에 필요한 길이만큼만 전송됩니다.

시작하려면 장치를 열고, 첫 번째 구성을 선택(장치에는 하나뿐이지만 운영 체제가 열거과정에서 이미 선택했을 수 있음)하고 데이터 로깅 인터페이스를 클레임합니다,

await device.open();
if (device.configuration === null)
  await device.selectConfiguration(1);
await device.claimInterface(1);

이 애플리케이션에서는 채널 1, 2, 5에서 읽는 것이 필요하므로 이 채널들을 활성화하기 위해 control transfer를 실행합니다,

await device.controlTransferOut({
    requestType: 'vendor',
    recipient: 'interface',
    request: 0x01,  // vendor-specific request: enable channels
    value: 0x0013,  // 0b00010011 (channels 1, 2 and 5)
    index: 0x0001   // Interface 1 is the recipient
});

이제 애플리케이션은 장치로부터 데이터를 폴링하기 시작할 수 있습니다. 우리는 3개 채널에서만 데이터를 기대하므로 6바이트 버퍼를 요청합니다. 완전한 버퍼를 받는 한 캡처된 값들(빅 엔디안으로 전송됨)은 콘솔 로그에 출력됩니다. 장치가 오류를 만나 엔드포인트를 스톨한 경우에는 계속하기 전에 오류를 클리어합니다,

while (true) {
  let result = await device.transferIn(1, 6);

  if (result.data && result.data.byteLength === 6) {
    console.log('Channel 1: ' + result.data.getUint16(0));
    console.log('Channel 2: ' + result.data.getUint16(2));
    console.log('Channel 5: ' + result.data.getUint16(4));
  }

  if (result.status === 'stall') {
    console.warn('Endpoint stalled. Clearing.');
    await device.clearHalt(1);
  }
}

6.1. USBDevice 인터페이스

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

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBInTransferResult {
  constructor(USBTransferStatus status, optional DataView? data);
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBOutTransferResult {
  constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0);
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousInTransferPacket {
  constructor(USBTransferStatus status, optional DataView? data);
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousInTransferResult {
  constructor(sequence<USBIsochronousInTransferPacket> packets, optional DataView? data);
  readonly attribute DataView? data;
  readonly attribute FrozenArray<USBIsochronousInTransferPacket> packets;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousOutTransferPacket {
  constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0);
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousOutTransferResult {
  constructor(sequence<USBIsochronousOutTransferPacket> packets);
  readonly attribute FrozenArray<USBIsochronousOutTransferPacket> packets;
};

[Exposed=(Worker,Window), SecureContext]
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<undefined> open();
  Promise<undefined> close();
  Promise<undefined> forget();
  Promise<undefined> selectConfiguration(octet configurationValue);
  Promise<undefined> claimInterface(octet interfaceNumber);
  Promise<undefined> releaseInterface(octet interfaceNumber);
  Promise<undefined> selectAlternateInterface(octet interfaceNumber, octet alternateSetting);
  Promise<USBInTransferResult> controlTransferIn(USBControlTransferParameters setup, unsigned short length);
  Promise<USBOutTransferResult> controlTransferOut(USBControlTransferParameters setup, optional BufferSource data);
  Promise<undefined> 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<undefined> reset();
};

다음 표에 설명된 내부 슬롯들로 USBDevice의 인스턴스가 생성됩니다:

Internal Slot 초기값 설명(비규범)
[[configurations]] sequence of USBConfiguration 이 장치가 지원하는 모든 구성들.
[[configurationValue]] <문서에서 항상 설정됨> 장치의 현재 구성 값.
[[selectedAlternateSetting]] 정수 리스트 현재 구성에서 각 인터페이스의 현재 대체 설정.
[[claimedInterface]] 불리언 리스트 현재 구성에서 각 인터페이스의 클레임 상태.
To 연결된 USB 장치의 디바이스 디스크립터를 찾기, 다음 단계를 수행하세요:
  1. 연결된 장치의 deviceDescriptordevice descriptor로 두되, Get Descriptor를 수행하여 DescriptorTypeDEVICE로 설정합니다.

  2. deviceDescriptor를 반환합니다.

To 연결된 USB 장치에 대한 구성 디스크립터 목록 찾기, 다음 단계를 수행하세요:
  1. deviceDescriptor를 연결된 USB 장치의 디바이스 디스크립터를 찾는 결과로 둡니다.

  2. numConfigurationsdeviceDescriptorbNumConfigurations로 둡니다.

  3. configurationIndex를 0으로 둡니다.

  4. configurationDescriptors를 빈 리스트로 둡니다.

  5. 만약 configurationIndexnumConfigurations보다 작을 동안:

    1. Get Descriptor를 DescriptorType=CONFIGURATION, DescriptorIndex=configurationIndex로 수행하여 descriptors를 얻습니다.

    2. 만약 descriptors[0]의 bDescriptorTypeCONFIGURATION와 같다면, descriptors[0]을 configurationDescriptors에 추가합니다.

    3. configurationIndex를 1씩 증가시킵니다.

  6. configurationDescriptors를 반환합니다.

To 인터페이스에 현재 스케줄된 전송을 중단하기 주어진 interface 객체에 대해, 다음 단계를 수행하세요:
  1. globalinterface의 관련 전역 객체로 둡니다.

  2. configurationinterface.[[configuration]]로 둡니다.

  3. deviceconfiguration.[[device]]로 둡니다.

  4. 만약 configurationdevice로 현재 구성을 찾은 결과와 같지 않다면, 반환합니다.

  5. 만약 인터페이스가 클레임되었는지 찾은 결과가 true가 아니라면, 반환합니다.

  6. currAlternateInterface를 현재 대체 설정에 대한 대체 인터페이스를 찾은 결과로 둡니다.

  7. endpoint에 대해 currAlternateInterface.[[endpoints]]에 대해:

    1. endpoint에 현재 스케줄된 모든 전송을 중단합니다.

    2. 관련된 프로미스들을 거부하기 위해 주어진 global에 대한 USB 작업 소스에서 전역 태스크를 큐에 추가하고, 연관된 프로미스들을 "AbortError" AbortError DOMException로 거부합니다.

To 인터페이스 인덱스 찾기 주어진 interfaceNumberconfiguration 객체에 대해, 다음 단계를 수행하세요:
  1. interfaceIndex를 0으로 둡니다.

  2. interfaceIndexconfiguration.[[interfaces]]의 크기보다 작을 동안:

    1. 만약 configuration.[[interfaces]][interfaceIndex].[[interfaceNumber]]가 interfaceNumber와 같다면, interfaceIndex를 반환합니다.

    2. interfaceIndex를 1 증가시킵니다.

  3. -1를 반환합니다.

To 대체 인덱스 찾기 주어진 alternateSettinginterface 객체에 대해, 다음 단계를 수행하세요:
  1. alternateIndex를 0으로 둡니다.

  2. alternateIndexinterface.[[alternates]]의 크기보다 작을 동안:

    1. 만약 interface.[[alternates]][alternateIndex].[[alternateSetting]]가 alternateSetting와 같다면, alternateIndex를 반환합니다.

    2. alternateIndex를 1 증가시킵니다.

  3. -1를 반환합니다.

To 현재 구성 찾기 주어진 device 객체에 대해, 다음 단계를 수행하세요:
  1. configuration에 대해 device.[[configurations]]를 반복합니다:

    1. 만약 configuration.[[configurationValue]]가 device.[[configurationValue]]와 같다면, configuration를 반환합니다.

  2. null을 반환합니다.

To 엔드포인트 찾기 주어진 endpointAddressdevice 객체에 대해, 다음 단계를 수행하세요:
  1. configurationdevice로 현재 구성을 찾은 결과로 둡니다.

  2. 만약 configurationnull이면, null을 반환합니다.

  3. interface에 대해 configuration.[[interfaces]]를 반복합니다:

    1. 만약 인터페이스가 클레임되었는지 찾은 결과가 true가 아니라면, 계속합니다.

    2. alternate를 현재 대체 설정에 대한 대체 인터페이스를 찾은 결과로 둡니다.

    3. endpoint에 대해 alternate.[[endpoints]]를 반복합니다:

      1. 만약 endpoint.[[endpointAddress]]가 endpointAddress와 같다면, endpoint를 반환합니다.

  4. null을 반환합니다.

To 장치가 구성되어 있는지 확인하기 주어진 device에 대해, 다음 단계를 수행하세요:
  1. 만약 device가 더 이상 시스템에 연결되어 있지 않다면, "NotFoundError" DOMException으로 거부된 프로미스를 반환합니다.

  2. 만약 device.openedtrue가 아니라면, "InvalidStateError" DOMException로 거부된 프로미스를 반환합니다.

  3. 만약 device.[[configurationValue]]가 0과 같다면, "InvalidStateError" DOMException로 거부된 프로미스를 반환합니다.

  4. undefined를 반환합니다.

장치가 연결되고 감지되면, 새로운 USBDevice 객체가 다음 단계를 수행하여 생성됩니다:
  1. this.[[configurationValue]]를 Get Configuration의 반환값으로 설정합니다.

  2. configurationDescriptors를 연결된 USB 장치에 대한 구성 디스크립터 목록을 찾은 결과로 둡니다.

  3. configurationDescriptor에 대해 다음을 수행합니다:

    1. configuration새로운 USBConfiguration 객체로 만들되, USBConfiguration(device,configurationValue)를 사용하고, devicethis로, configurationValueconfigurationDescriptorbConfigurationValue로 설정합니다.

    2. Append configuration를 this.[[configurations]]에 추가합니다.

    3. 만약 configurationDescriptorbConfigurationValue가 this.[[configurationValue]]와 같다면:

      1. numInterfacesconfiguration.[[interfaces]]의 크기로 둡니다.

      2. this.[[selectedAlternateSetting]]의 크기를 numInterfaces로 조정합니다.

      3. this.[[selectedAlternateSetting]]를 0으로 채웁니다.

      4. this.[[claimedInterface]]의 크기를 numInterfaces로 조정합니다.

      5. this.[[claimedInterface]]를 false로 채웁니다.

모든 USB 장치는 default control pipeendpointNumber 0을 반드시 가져야 합니다.

6.1.1. 속성

usbVersionMajor, 유형 octet, 읽기 전용
usbVersionMinor, 유형 octet, 읽기 전용
usbVersionSubminor, 유형 octet, 읽기 전용

usbVersionMajor, usbVersionMinorusbVersionSubminor 속성은 장치가 지원하는 USB 프로토콜 버전을 선언합니다. 이들은 bcdUSB 필드의 값과 일치해야 하며, 0xJJMN 값은 major 버전 JJ, minor 버전 M, subminor 버전 N을 나타냅니다.

deviceClass, 유형 octet, 읽기 전용
deviceSubclass, 유형 octet, 읽기 전용
deviceProtocol, 유형 octet, 읽기 전용

deviceClass, deviceSubclassdeviceProtocol 속성은 장치가 지원하는 통신 인터페이스를 선언합니다. 이들은 각각 device descriptorbDeviceClass, bDeviceSubClassbDeviceProtocol 필드 값과 일치해야 합니다.

vendorId, 유형 unsigned short, 읽기 전용
productId, 유형 unsigned short, 읽기 전용

vendorIdproductId 는 장치의 vendor IDproduct ID와 동일해야 합니다.

deviceVersionMajor, 유형 octet, 읽기 전용
deviceVersionMinor, 유형 octet, 읽기 전용
deviceVersionSubminor, 유형 octet, 읽기 전용

deviceVersionMajor, deviceVersionMinordeviceVersionSubminor 속성은 제조업체가 정의한 장치 릴리스 번호를 선언합니다. 이는 device descriptorbcdDevice 필드 값과 일치해야 하며, 0xJJMN 값은 major 버전 JJ, minor 버전 M, subminor 버전 N을 나타냅니다.

manufacturerName, 유형 DOMString, 읽기 전용, nullable
productName, 유형 DOMString, 읽기 전용, nullable
serialNumber, 유형 DOMString, 읽기 전용, nullable

manufacturerName, productNameserialNumber 속성은 각 device descriptoriManufacturer, iProduct, iSerialNumber 필드로 인덱스된 string descriptors의 값을 포함해야 합니다(정의되어 있는 경우).

configuration, 유형 USBConfiguration, 읽기 전용, nullable

configuration 속성은 장치에 대해 현재 선택된 구성을 포함하며, 반드시 USBConfiguration 객체 중 하나이어야 하며, 이는 configurations에 포함되어야 합니다.

configuration 게터 동작은 다음과 같습니다:

  1. 현재 구성 찾기를 이 this에 대해 실행한 결과를 반환합니다.

configurations, 유형 FrozenArray<USBConfiguration>, 읽기 전용

configurations 속성은 장치가 지원하는 구성들을 나타내는 sequence를 포함합니다.

configurations 게터 동작은 다음과 같습니다:

  1. this.[[configurations]]를 반환합니다.

opened, 유형 boolean, 읽기 전용

opened 속성은 현재 실행 컨텍스트에 의해 장치가 열려 있으면 true로 설정되어야 하며, 그렇지 않으면 false로 설정되어야 합니다.

6.1.2. 메서드

open() 메서드는 호출 시 다음 단계를 반드시 실행해야 합니다:
  1. globalthis의 관련 전역 객체로 둡니다.

  2. 만약 this가 더 이상 시스템에 연결되어 있지 않다면, "NotFoundError" DOMException으로 거부된 프로미스를 반환합니다.

  3. 만약 this.openedtrue이면 undefined로 해결된 프로미스를 반환합니다.

  4. 다음 단계를 병렬로 실행합니다.

    1. 장치와의 세션을 시작하기 위한 플랫폼 특유의 필요한 단계를 수행합니다.

    2. USB 작업 소스에 대하여 global에 대한 전역 태스크를 큐에 추가하여 다음 단계를 실행합니다:

      1. 만약 위 플랫폼 단계가 실패하면 reject하여 promise를 "NetworkError" DOMException로 거부하고 이 단계를 중단합니다.

      2. this.openedtrue로 설정합니다.

      3. Resolve하여 promiseundefined로 해결합니다.

  5. promise를 반환합니다.

close() 메서드는 호출 시 다음 단계를 반드시 실행해야 합니다:
  1. globalthis의 관련 전역 객체로 둡니다.

  2. 만약 this가 더 이상 시스템에 연결되어 있지 않다면, "NotFoundError" DOMException으로 거부된 프로미스를 반환합니다.

  3. 만약 this.openedfalse이면 undefined로 해결된 프로미스를 반환합니다.

  4. 이 장치에 대해 현재 실행 중인 다른 모든 알고리즘을 중단하고 관련 프로미스들을 "AbortError" DOMException으로 reject합니다.

  5. 다음 단계를 병렬로 실행합니다.

    1. 각 클레임된 인터페이스에 대해 releaseInterface(interfaceNumber)가 호출된 것처럼 플랫폼 특유의 필요한 단계를 수행하여 모든 클레임된 인터페이스를 해제합니다.

    2. 장치와의 세션을 종료하기 위한 플랫폼 특유의 필요한 단계를 수행합니다.

    3. USB 작업 소스에 대하여 global에 대한 전역 태스크를 큐에 추가하여 다음 단계를 실행합니다:

      1. this.openedfalse로 설정합니다.

      2. Resolve하여 promiseundefined로 해결합니다.

  6. promise를 반환합니다.

참고: 더 이상 어떤 [ECMAScript] 코드도 USBDevice device 인스턴스를 관찰할 수 없게 되면, UA는 device.close()를 실행할 것을 권장합니다.

forget() 메서드는 호출 시 다음 단계를 반드시 실행해야 합니다:
  1. devicethis로 둡니다.

  2. storage를 현재 스크립트 실행 환경의 USBPermissionStorage 객체로 둡니다.

  3. Remove device from storagestorage로 실행합니다.

  4. undefined로 해결된 프로미스를 반환합니다.

참고: 사용자 에이전트는 예를 들어 WebHID와 WebUSB 장치 접근을 통합된 저레벨 장치 접근 권한으로 추적하는 등 API 간 권한을 결합하도록 결정할 수 있습니다. 이 때문에 이 메서드는 향후 추가적인(아직 명시되지 않은) 권한도 취소할 수 있습니다.

selectConfiguration(configurationValue) 메서드는 호출 시 다음 단계를 반드시 실행해야 합니다:
  1. globalthis의 관련 전역 객체로 둡니다.

  2. 만약 this가 더 이상 시스템에 연결되어 있지 않다면, "NotFoundError" DOMException으로 거부된 프로미스를 반환합니다.

  3. selectedConfigurationnull로 둡니다.

  4. this.[[configurations]]의 각 configuration에 대해 반복하며:

    1. 만약 configuration.[[configurationValue]]가 configurationValue와 같다면 selectedConfigurationconfiguration으로 설정하고 반복을 중단합니다.

  5. 만약 selectedConfigurationnull이면, "NotFoundError" DOMException으로 거부된 프로미스를 반환합니다.

  6. 만약 this.openedtrue가 아니면, "InvalidStateError" DOMException으로 거부된 프로미스를 반환합니다.

  7. activeConfiguration현재 구성 찾기를 이 this에 대해 실행한 결과로 둡니다.

  8. 다음 단계를 병렬로 실행합니다.

    1. 만약 activeConfigurationnull이 아니라면:

      1. interface에 대해 activeConfiguration.[[interfaces]]를 반복하며, 해당 인터페이스에서 현재 스케줄된 전송을 중단합니다.

    2. SET_CONFIGURATION 제어 전송을 실행하여 configurationValue를 지정된 configurationValue로 설정합니다.

    3. USB 작업 소스에 대하여 global의 전역 태스크를 큐에 추가하여 다음 단계를 실행합니다:

      1. 만약 위의 제어 전송이 실패하면 reject하여 promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단합니다.

      2. numInterfacesselectedConfiguration.[[interfaces]]의 크기로 둡니다.

      3. this.[[selectedAlternateSetting]]의 크기를 numInterfaces로 조정합니다.

      4. [[selectedAlternateSetting]]을 0으로 채웁니다.

      5. [[claimedInterface]]의 크기를 numInterfaces로 조정합니다.

      6. [[claimedInterface]]를 false로 채웁니다.

      7. this.[[configurationValue]]를 configurationValue로 설정합니다.

      8. Resolve하여 promiseundefined로 해결합니다.

  9. promise를 반환합니다.

claimInterface(interfaceNumber) 메서드는 호출 시 다음 단계를 반드시 실행해야 합니다:
  1. globalthis의 관련 전역 객체로 둡니다.

  2. 만약 장치가 구성되어 있는지 확인가 이 this에 대해 Promise를 반환하면, 그 값을 반환합니다.

  3. activeConfiguration현재 구성 찾기를 이 this에 대해 실행한 결과로 둡니다.

  4. interfacesactiveConfiguration.[[interfaces]]로 둡니다.

  5. interfaceIndex인터페이스 인덱스 찾기로 구한 값(인수: interfaceNumber, activeConfiguration)으로 둡니다.

  6. 만약 interfaceIndex-1이면, "NotFoundError" DOMException으로 거부된 프로미스를 반환합니다.

  7. 만약 this.[[claimedInterface]][interfaceIndex]가 true이면 undefined로 해결된 프로미스를 반환합니다.

  8. unrestrictedfalse로 둡니다.

  9. documentglobal의 연관 Document로 두거나, 연관 Document가 없으면 null로 둡니다.

  10. 만약 documentnull이 아니고, document가 정책 제어 기능 "usb-unrestricted"을 사용할 수 있도록 허용되어 있다면 unrestrictedtrue로 설정합니다.

  11. 만약 interfaces[interfaceIndex].[[isProtectedClass]]가 true이고 unrestrictedfalse이면 "SecurityError" DOMException으로 거부된 프로미스를 반환합니다.

  12. promise를 새 프로미스로 둡니다.

  13. 다음 단계를 병렬로 실행합니다.

    1. 현재 실행 컨텍스트에 대해 interfaces[interfaceIndex]에 대한 단독 제어를 요청하는 플랫폼 특유의 단계를 수행합니다.

    2. USB 작업 소스에 대하여 global의 전역 태스크를 큐에 추가하여 다음 단계를 실행합니다:

      1. 만약 위 플랫폼 단계가 실패하면 reject하여 promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단합니다.

      2. this.[[claimedInterface]][interfaceIndex]를 true로 설정합니다.

      3. Resolve하여 promiseundefined로 해결합니다.

  14. promise를 반환합니다.

releaseInterface(interfaceNumber) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. activeConfigurationfinding the current configurationthis와 함께 수행한 결과로 한다.

  4. interfacesactiveConfiguration.[[interfaces]]로 한다.

  5. interfaceIndexfinding the interface indexinterfaceNumberactiveConfiguration과 함께 수행한 결과로 한다.

  6. interfaceIndex-1과 같다면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  7. this.[[claimedInterface]][interfaceIndex] 가 false이면, undefined로 이행된 promise를 반환한다.

  8. promise새로운 promise로 한다.

  9. 다음 단계를 병렬로 수행한다.

    1. interfaces[interfaceIndex]에 대한 독점 제어를 포기하기 위해 필요한 플랫폼별 단계를 수행한다.

    2. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. this.[[selectedAlternateSetting]][interfaceIndex] 를 0으로 설정한다.

      2. this.[[claimedInterface]][interfaceIndex] 를 false로 설정한다.

      3. Resolve promiseundefined로 이행한다.

  10. promise를 반환한다.

selectAlternateInterface(interfaceNumber, alternateSetting) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. activeConfigurationfinding the current configurationthis와 함께 수행한 결과로 한다.

  4. interfacesactiveConfiguration.[[interfaces]]로 한다.

  5. interfaceIndexfinding the interface indexinterfaceNumberactiveConfiguration으로 수행한 결과로 한다.

  6. interfaceIndex-1과 같다면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  7. this.[[claimedInterface]][interfaceIndex] 가 false라면, 다음으로 거부된 promise를 반환한다: "InvalidStateError" DOMException.

  8. interfaceinterfaces[interfaceIndex]로 한다.

  9. alternateIndexfinding the alternate indexalternateSettinginterface로 수행한 결과로 한다.

  10. alternateIndex-1과 같다면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  11. promise새로운 promise로 한다.

  12. 다음 단계를 병렬로 수행한다.

    1. 인터페이스에 현재 예약된 전송 중단interface로 수행한다.

    2. SET_INTERFACE control transfer를 발행하되, interfaceNumberinterfaceNumber로, alternateSettingalternateSetting으로 설정한다.

    3. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. 위의 control transfer가 실패했다면 reject promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단한다.

      2. this.[[selectedAlternateSetting]][interfaceIndex] 를 alternateSetting으로 설정한다.

      3. Resolve promiseundefined로 이행한다.

  13. promise를 반환한다.

controlTransferIn(setup, length) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. check the validity of the control transfer parametersthissetup으로 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  4. promise새로운 promise로 한다.

  5. 다음 단계를 병렬로 수행한다.

    1. length가 0보다 크면, bufferlength 바이트를 담을 공간을 가진 호스트 버퍼로 한다.

    2. control transferthis에 대해 발행하되, setup packet 매개변수는 setup에서 제공된 값을 사용하고, bmRequestType의 데이터 전송 방향은 "device to host"로, wLengthlength로 설정한다. 정의되어 있다면, 이 전송에 대한 응답으로 수신된 데이터를 기록할 대상로서 buffer도 제공한다.

    3. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. bytesTransferredbuffer에 기록된 바이트 수로 한다.

      2. result새로운 USBInTransferResult로 한다.

      3. 장치로부터 데이터를 수신했다면, 새로운 ArrayBuffer를 생성하되 buffer의 처음 bytesTransferred 바이트를 포함하도록 하고, result.data를 그 위에 구성한 새로운 DataView로 설정한다.

      4. result.status를 다음과 같이 설정한다.

      5. 그 밖의 이유로 전송이 실패했다면 reject promise를 "NetworkError"로 거부하고 이 단계를 중단한다.

      6. Resolve promiseresult로 이행한다.

  6. promise를 반환한다.

controlTransferOut(setup, data) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. check the validity of the control transfer parametersthissetup으로 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  4. 버퍼 소스의 복사본을 가져와 data에서 결과를 bytes로 한다.

  5. promise새로운 promise로 한다.

  6. 다음 단계를 병렬로 수행한다.

    1. control transferthis에 대해 발행하되, setup packet 매개변수는 setup에서 제공된 값을 사용하고, bmRequestType의 데이터 전송 방향은 "host to device"로, wLengthbytes의 길이로 설정한다.

    2. 전송의 data stage에서 bytes를 전송한다.

    3. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. result새로운 USBOutTransferResult로 한다.

      2. result.status를 다음과 같이 설정한다.

      3. 그 밖의 이유로 전송이 실패하면 reject promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단한다.

      4. Resolve promiseresult로 이행한다.

  7. promise를 반환한다.

clearHalt(direction, endpointNumber) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. endpointAddressdirection"in"과 같으면 endpointNumber | 0x80으로, 그렇지 않으면 endpointNumber로 한다.

  4. endpointfinding the endpointendpointAddressthis로 수행한 결과로 한다.

  5. endpointnull이면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  6. promise새로운 promise로 한다.

  7. 다음 단계를 병렬로 수행한다.

    1. ClearFeature(ENDPOINT_HALT) control transfer를 장치에 발행해 endpoint의 중지(halts) 상태를 해제한다.

    2. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. 위의 control transfer가 실패했다면, reject promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단한다.

      2. Resolve promiseundefined로 이행한다.

  8. promise를 반환한다.

transferIn(endpointNumber, length) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. endpointAddressendpointNumber | 0x80(즉, "in" 방향)으로 한다.

  4. endpointfinding the endpointendpointAddressthis로 수행한 결과로 한다.

  5. endpointnull이면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  6. endpoint.type"bulk" 또는 "interrupt"와 같지 않다면, 다음으로 거부된 promise를 반환한다: "InvalidAccessError" DOMException.

  7. promise새로운 promise로 한다.

  8. 다음 단계를 병렬로 수행한다.

    1. bufferlength 바이트를 담을 공간을 가진 호스트 버퍼로 한다.

    2. endpoint에 적합한 방식으로 bulk 또는 interrupt IN transfer를 수행하여 장치로부터 length 바이트의 데이터를 buffer로 수신한다.

    3. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. bytesTransferredbuffer에 기록된 바이트 수로 한다.

      2. result새로운 USBInTransferResult로 한다.

      3. 장치로부터 데이터를 수신했다면, 새로운 ArrayBuffer를 생성하되 buffer의 처음 bytesTransferred 바이트를 포함하도록 하고, result.data를 그 위에 구성한 새로운 DataView로 설정한다.

      4. result.status를 다음과 같이 설정한다.

        • "stall": 장치가 endpoint를 정지시켜 응답한 경우.

        • "babble": 장치가 length 바이트보다 많은 데이터를 응답한 경우.

        • "ok": 그 외의 경우.

      5. 그 밖의 이유로 전송이 실패했다면 reject promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단한다.

      6. Resolve promiseresult로 이행한다.

  9. promise를 반환한다.

transferOut(endpointNumber, data) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. endpointAddressendpointNumber(즉, "out" 방향)로 한다.

  4. endpointfinding the endpointendpointAddressthis로 수행한 결과로 한다.

  5. endpointnull이면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  6. endpoint.type"bulk" 또는 "interrupt"와 같지 않다면, 다음으로 거부된 promise를 반환한다: "InvalidAccessError" DOMException.

  7. 버퍼 소스의 복사본을 가져와 data에서 결과를 bytes로 한다.

  8. promise새로운 promise로 한다.

  9. 다음 단계를 병렬로 수행한다.

    1. endpoint에 적합한 방식으로 bulk 또는 interrupt OUT transfer를 수행하여 bytes를 장치로 전송한다.

    2. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. result새로운 USBOutTransferResult로 한다.

      2. result.bytesWritten을 장치로 성공적으로 전송된 데이터의 양으로 설정한다.

      3. result.status를 다음과 같이 설정한다.

        • "stall": 장치가 endpoint를 정지시켜 응답한 경우.

        • "ok": 그 외의 경우.

      4. 그 밖의 이유로 전송이 실패했다면 reject promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단한다.

      5. Resolve promiseresult로 이행한다.

  10. promise를 반환한다.

isochronousTransferIn(endpointNumber, packetLengths) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. endpointAddressendpointNumber | 0x80(즉, "in" 방향)으로 한다.

  4. endpointfinding the endpointendpointAddressthis로 수행한 결과로 한다.

  5. endpointnull이면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  6. endpoint.type"isochronous"와 같지 않다면, 다음으로 거부된 promise를 반환한다: "InvalidAccessError" DOMException.

  7. promise새로운 promise로 한다.

  8. 다음 단계를 병렬로 수행한다.

    1. totalLengthpacketLengths의 모든 요소의 합으로 한다.

    2. buffertotalLength 바이트를 담을 공간을 가진 호스트 버퍼로 한다.

    3. isochronous IN transferendpoint에서 수행하여 장치로부터 패킷을 buffer로 읽되, packetLengths에 따라 진행한다.

    4. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. result새로운 USBIsochronousInTransferResult로 한다.

      2. data새로운 ArrayBuffer로 하되 buffer로 초기화한다.

      3. result.data새로운 DataView로 설정하되, buffer 위에 구성한다.

      4. 각 패킷 i에 대해 0부터 packetLengths.length - 1까지 반복한다:

        1. packet새로운 USBIsochronousInTransferPacket으로 한다.

        2. view새로운 DataView로 하되 이 패킷에 대해 장치에서 수신된 데이터를 포함하는 data의 일부분 위에 구성하고, packet.dataview로 설정한다.

        3. packet.status를 다음과 같이 설정한다.

          • "stall": 이 패킷을 수신하기 전에 장치가 endpoint를 정지시켜 전송이 종료된 경우.

          • "babble": 이 패킷에 대해 장치가 packetLengths[i] 바이트보다 많은 데이터를 보내 전송이 종료된 경우.

          • "ok": 그 외의 경우.

        4. 그 밖의 이유로 전송이 실패하면 reject promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단한다.

        5. result.packets[i] 를 packet으로 설정한다.

      5. Resolve promiseresult로 이행한다.

  9. promise를 반환한다.

isochronousTransferOut(endpointNumber, data, packetLengths) 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. endpointAddressendpointNumber(즉, "out" 방향)로 한다.

  4. endpointfinding the endpointendpointAddressthis로 수행한 결과로 한다.

  5. endpointnull이면, 다음으로 거부된 promise를 반환한다: "NotFoundError" DOMException.

  6. endpoint.type"isochronous"와 같지 않다면, 다음으로 거부된 promise를 반환한다: "InvalidAccessError" DOMException.

  7. 버퍼 소스의 복사본을 가져와 data에서 결과를 bytes로 한다.

  8. promise새로운 promise로 한다.

  9. 다음 단계를 병렬로 수행한다.

    1. isochronous OUT transferendpoint에서 수행하여 bytes를 장치에 기록하되, packetLengths.length개의 패킷으로 나누고 각 패킷의 크기는 packetLengths[i] 바이트(i0부터 packetLengths.length - 1까지)로 한다.

    2. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. result새로운 USBIsochronousOutTransferResult로 한다.

      2. 각 패킷 i에 대해 0부터 packetLengths.length - 1까지 반복한다:

        1. packet새로운 USBIsochronousOutTransferPacket으로 한다.

        2. packet.bytesWritten을 이 패킷의 일부로 장치에 성공적으로 전송된 데이터의 양으로 설정한다.

        3. packet.status를 다음과 같이 설정한다.

          • "stall": 이 패킷이 확인되기 전에 장치가 endpoint를 정지시켜 전송이 종료된 경우.

          • "ok": 그 외의 경우.

        4. 그 밖의 이유로 전송이 실패하면 reject promise를 "NetworkError" DOMException으로 거부하고 이 단계를 중단한다.

        5. result.packets[i] 를 packet으로 설정한다.

      3. Resolve promiseresult로 이행한다.

  10. promise를 반환한다.

reset() 메서드는, 호출되면 다음 단계를 반드시 수행해야 한다:
  1. globalthis관련 전역 객체로 한다.

  2. check if the device is configuredthis와 함께 수행한 결과가 Promise를 반환하면, 그 값을 반환한다.

  3. promise새로운 promise로 한다.

  4. 다음 단계를 병렬로 수행한다.

    1. 장치의 모든 작업을 중단하고, 그들의 연결된 promise를 "글로벌 태스크를 큐에 넣어 USB 작업 소스에서 reject 하되 "AbortError" DOMException으로 거부하도록 한다.

    2. 장치를 소프트 리셋하기 위한 필요한 플랫폼별 작업을 수행한다.

    3. 글로벌 태스크를 큐에 넣어 USB 작업 소스에서 global을 주어 다음 단계를 실행한다:

      1. 소프트 리셋이 실패했다면, reject promise를 "NetworkError"로 거부하고 이 단계를 중단한다.

      2. Resolve promiseundefined로 이행한다.

  5. promise를 반환한다.

장치를 재설정한 후에는 어떤 구성 상태인가? [Issue #36]

6.2. USBControlTransferParameters 사전

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

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

dictionary USBControlTransferParameters {
  required USBRequestType requestType;
  required USBRecipient recipient;
  required octet request;
  required unsigned short value;
  required unsigned short index;
};
USBDevice deviceUSBControlTransferParameters setup이 주어졌을 때, 제어 전송 매개변수의 유효성을 확인하려면 다음 단계를 수행한다:
  1. configurationdevice현재 구성을 찾기의 결과로 한다.

  2. configurationnull이면, undefined를 반환한다.

  3. setup.recipient"interface"이면, 다음 단계를 수행한다:

    1. interfaceNumbersetup.index의 하위 8비트로 한다.

    2. interfaceIndexinterfaceNumberconfiguration으로 인터페이스 인덱스를 찾기의 결과로 한다.

    3. interfaceIndex-1과 같다면, a promise rejected with "NotFoundError" DOMException을 반환한다.

    4. interfaceconfiguration.[[interfaces]][interfaceIndex]로 한다.

    5. interface해당 인터페이스가 클레임되었는지 찾기의 결과가 true가 아니면, a promise rejected with "InvalidStateError" DOMException을 반환한다.

  4. setup.recipient"endpoint"이면, 다음 단계를 수행한다:

    1. endpointAddresssetup.index로 한다.

    2. endpointendpointAddressdevice엔드포인트 찾기의 결과로 한다.

    3. endpointnull이면, a promise rejected with "NotFoundError" DOMException을 반환한다.

    4. alternateendpoint.[[alternateInterface]]로 한다.

    5. interfacealternate.[[interface]]로 한다.

    6. interface해당 인터페이스가 클레임되었는지 찾기의 결과가 false이면, a promise rejected with "InvalidStateError" DOMException을 반환한다.

  5. undefined를 반환한다.

6.2.1. 멤버

requestType, 유형 USBRequestType

requestType 속성은 setup packetbmRequestType 필드의 일부를 채워 이 요청이 USB 표준의 일부인지, 특정 USB 장치 클래스 명세의 일부인지, 또는 벤더 전용 프로토콜인지 나타낸다.

recipient, 유형 USBRecipient

recipient 속성은 setup packetbmRequestType 필드의 일부를 채워 control transfer가 전체 장치를 대상으로 하는지, 특정 인터페이스 또는 엔드포인트를 대상으로 하는지 나타낸다.

request, 유형 octet

request 속성은 setup packetbRequest 필드를 채운다. 유효한 요청은 USB 표준, USB 장치 클래스 명세 또는 장치 벤더에 의해 정의된다.

value, 유형 unsigned short

valueindex 속성은 각각 setup packetwValuewIndex 필드를 채운다. 이 필드들의 의미는 수행되는 요청에 따라 달라진다.

6.3. USBConfiguration 인터페이스

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBConfiguration {
  constructor(USBDevice device, octet configurationValue);
  readonly attribute octet configurationValue;
  readonly attribute DOMString? configurationName;
  readonly attribute FrozenArray<USBInterface> interfaces;
};

USBConfiguration 인스턴스는 다음 표에 설명된 내부 슬롯과 함께 생성된다:

내부 슬롯 초깃값 설명(비규범)
[[device]] <항상 본문에서 설정> USBDevice 객체로서, this가 속한다.
[[interfaces]] sequence (비어 있음) of USBInterface 이 구성에서 지원되는 모든 인터페이스.
[[configurationValue]] <항상 본문에서 설정> 이 구성의 구성 값.
주어진 configurationValue구성에 대한 디스크립터 목록을 찾기 위해, 다음 단계를 수행한다:
  1. deviceDescriptor연결된 USB 장치의 디바이스 디스크립터 찾기의 결과로 한다.

  2. numConfigurationsdeviceDescriptorbNumConfigurations로 한다.

  3. configurationIndex를 0으로 한다.

  4. configurationIndexnumConfigurations보다 작은 동안:

    1. descriptorsdescriptors의 목록으로 하되, Get Descriptor를 수행하면서 DescriptorTypeCONFIGURATION으로, DescriptorIndexconfigurationIndex로 설정한다.

    2. descriptors[0]의 bDescriptorTypeCONFIGURATION과 같고, descriptorsbConfigurationValueconfigurationValue와 같다면, descriptors를 반환한다.

    3. configurationIndex를 1 증가시킨다.

  5. empty 결과를 반환한다.

6.3.1. 생성자

USBConfiguration(device, configurationValue) 생성자는 호출될 때 MUST 다음 단계를 수행한다:
  1. this.[[device]]device로 설정한다.

  2. this.[[configurationValue]]configurationValue로 설정한다.

  3. descriptorsconfigurationValueconfigurationValue로 설정하여 구성에 대한 디스크립터 목록을 찾기의 결과로 한다.

  4. seen을 비어 있는 ordered set으로 한다.

  5. descriptorsdescriptor에 대해:

    1. descriptorbDescriptorTypeINTERFACE와 같지 않다면, 다음으로 진행한다.

    2. descriptorbInterfaceNumberseen 안에 있으면, 다음으로 진행한다.

    3. interface새로운 USBInterface 객체로 하되, USBInterface(configuration,interfaceNumber)를 사용하고 configurationthis로, interfaceNumberdescriptorbInterfaceNumber로 설정한다.

    4. Append interfacethis.[[interfaces]]에 추가한다.

    5. Append descriptorbInterfaceNumberseen에 추가한다.

6.3.2. 속성

configurationValue, 유형 octet, readonly

각 장치 구성은 이를 정의하는 configuration descriptorbConfigurationValue 필드와 일치하는 고유한 configurationValue를 가져야 한다(SHALL).

configurationName, 유형 DOMString, readonly, nullable

configurationName 속성은, 정의되어 있다면, configuration descriptoriConfiguration 필드가 참조하는 string descriptor의 값을 포함해야 한다(SHOULD).

interfaces, 유형 FrozenArray<USBInterface>, readonly

interfaces 속성은 이 장치 구성에 의해 노출되는 인터페이스 목록을 포함해야 한다(SHALL).

interfaces 게터 단계는 다음과 같다:

  1. this.[[interfaces]]를 반환한다.

장치 구성에 대한 비규범 정보를 일부 포함한다. [Issue #46]

6.4. USBInterface 인터페이스

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBInterface {
  constructor(USBConfiguration configuration, octet interfaceNumber);
  readonly attribute octet interfaceNumber;
  readonly attribute USBAlternateInterface alternate;
  readonly attribute FrozenArray<USBAlternateInterface> alternates;
  readonly attribute boolean claimed;
};

USBInterface 인스턴스는 다음 표에 설명된 내부 슬롯과 함께 생성된다:

내부 슬롯 초깃값 설명(비규범)
[[configuration]] <항상 본문에서 설정> USBConfiguration 객체로서, this가 속한다.
[[interfaceNumber]] <항상 본문에서 설정> 이 인터페이스의 인터페이스 번호.
[[alternates]] sequence (비어 있음) of USBAlternateInterface 이 인터페이스가 지원하는 모든 대체 설정.
[[isProtectedClass]] false 이 인터페이스가 보호된 클래스에 속하는 어떤 대체 설정이라도 가지는지 여부.

interface descriptor interface는 그리고 오직 그때에만 interfacebInterfaceClass가 다음 값 중 하나와 같을 때 보호된 인터페이스 클래스를 가진다.

보호된 인터페이스 클래스
코드 설명
0x01 Audio
0x03 HID (Human Interface Device)
0x08 Mass Storage
0x0B Smart Card
0x0E Video
0x10 Audio/Video Devices
0xE0 Wireless Controller

Note: 본 명세는 가능한 한 많은 장치를 지원하면서도 민감한 장치에 대한 접근을 제한하여 악성 콘텐츠로부터 사용자를 보호하는 균형을 추구한다. 서론에서 언급했듯이 이 API의 목표는 다른, 더 높은 수준의 API로 다루어지지 않는 장치를 지원하는 것이다. 위 목록에는 그러한 높은 수준의 API가 존재하여 사용자 프라이버시와 보안을 이 API를 통한 저수준 접근보다 더 잘 보호해주는 인터페이스 클래스들이 포함되어 있다.

주어진 interface에 대해 인터페이스가 클레임되었는지 찾기 위해, 다음 단계를 수행한다:
  1. configurationinterface.[[configuration]]로 한다.

  2. deviceconfiguration.[[device]]로 한다.

  3. configurationdevice현재 구성을 찾기의 결과와 동일하지 않다면, false를 반환한다.

  4. interfaceIndexinterface.[[interfaceNumber]]configuration으로 인터페이스 인덱스를 찾기의 결과로 한다.

  5. Assert: interfaceIndex-1이 아니다.

  6. device.[[claimedInterface]][interfaceIndex]를 반환한다.

주어진 interface에 대해 현재 대체 설정에 대한 대체 인터페이스 찾기를 수행하려면, 다음 단계를 따른다:
  1. configurationinterface.[[configuration]]로 한다.

  2. deviceconfiguration.[[device]]로 한다.

  3. alternateIndex를 0으로 한다.

  4. interface인터페이스가 클레임되었는지 찾기의 결과가 true이면:

    1. interfaceIndexinterface.[[interfaceNumber]]configuration으로 인터페이스 인덱스를 찾기의 결과로 한다.

    2. Assert: interfaceIndex-1이 아니다.

    3. alternateIndexdevice.[[selectedAlternateSetting]][interfaceIndex] 및 interface대체 인덱스를 찾기의 결과로 설정한다.

  5. Assert: alternateIndex-1이 아니다.

  6. interface.alternates[alternateIndex]를 반환한다.

Note: Interface Descriptor [USB31]에 따르면 인터페이스에는 적어도 하나의 대체 설정이 있어야 한다. 즉, Interface Descriptor에는 최소한 하나의 대체 설정이 존재해야 한다.

6.4.1. 생성자

USBInterface(configuration, interfaceNumber) 생성자는 호출될 때 MUST 다음 단계를 수행한다:
  1. this.[[configuration]]configuration로 설정한다.

  2. this.[[interfaceNumber]]interfaceNumber로 설정한다.

  3. descriptorsconfigurationValueconfiguration.[[configurationValue]]로 설정하여 구성에 대한 디스크립터 목록을 찾기의 결과로 한다.

  4. descriptorsdescriptor에 대하여:

    1. descriptorbDescriptorTypeINTERFACE와 같지 않다면, 계속한다.

    2. descriptorbInterfaceNumberinterfaceNumber와 같지 않다면, 계속한다.

    3. descriptor보호된 인터페이스 클래스를 가진다면, this.[[isProtectedClass]]true로 설정한다.

    4. alternate새로운 USBAlternateInterface 객체로 하되, USBAlternateInterface(deviceInterface,alternateSetting)를 사용하고 deviceInterfacethis로, alternateSettingdescriptorbAlternateSetting으로 설정한다.

    5. Append alternatethis.[[alternates]]에 추가한다.

6.4.2. 속성

interfaceNumber, 유형 octet, readonly

각 인터페이스는 interface descriptors에 있는 단일 bInterfaceNumber 필드로 식별되는 alternates 모음을 제공한다. interfaceNumber 속성은 이 필드와 일치해야 한다(MUST).

interfaceNumber 게터 단계는 다음과 같다.

  1. this.[[interfaceNumber]]를 반환한다.

alternate, 유형 USBAlternateInterface, readonly

alternate 속성은 이 인터페이스에 대해 현재 선택된 USBAlternateInterface로 설정되어야 하며(SHALL), 기본값으로는 bAlternateSetting0인 인터페이스여야 한다(SHALL).

alternate 게터 단계는 다음과 같다:

  1. 현재 대체 설정에 대한 대체 인터페이스 찾기의 결과를 this로 반환한다.

alternates, 유형 FrozenArray<USBAlternateInterface>, readonly

alternates 메서드는 단일 bInterfaceNumber 필드로 식별되는 USBAlternateInterface 객체들의 모음을 제공합니다. 이 bInterfaceNumber는 각 객체의 interface descriptor에서 찾을 수 있습니다.

alternates 게터 단계는 다음과 같다:

  1. this.[[alternates]]를 반환한다.

claimed, 유형 boolean, readonly

claimed 속성은 현재 실행 컨텍스트에 의해 인터페이스가 클레임되었을 때 true로, 그렇지 않으면 false로 설정되어야 한다(SHALL).

claimed 게터 단계는 다음과 같다:

  1. 인터페이스가 클레임되었는지 찾기의 결과를 this로 반환한다.

6.5. USBAlternateInterface 인터페이스

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBAlternateInterface {
  constructor(USBInterface deviceInterface, octet alternateSetting);
  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;
};

USBAlternateInterface 인스턴스는 다음 표에 설명된 내부 슬롯과 함께 생성된다:

내부 슬롯 초깃값 설명(비규범)
[[interface]] <항상 본문에서 설정> USBInterface 객체로서, this가 속한다.
[[endpoints]] sequence (비어 있음) of USBEndpoint 이 인터페이스가 노출하는 모든 엔드포인트.
[[alternateSetting]] <항상 본문에서 설정> 이 인터페이스의 대체 설정 값.
주어진 alternateSetting, interfaceNumber, configurationValue에 대해 엔드포인트 디스크립터 목록을 찾기 위해, 다음 단계를 수행한다:
  1. descriptorsconfigurationValue구성에 대한 디스크립터 목록을 찾기의 결과로 한다.

  2. endpointDescriptors를 비어 있는 list로 한다.

  3. index를 0으로 한다.

  4. indexdescriptorssize보다 작은 동안:

    1. descriptordescriptors[index]로 한다.

    2. descriptorbDescriptorTypeINTERFACE와 같지 않다면, index를 1 증가시키고 계속한다.

    3. descriptorbInterfaceNumberinterfaceNumber와 같지 않다면, index를 1 증가시키고 계속한다.

    4. descriptorbAlternateSettingalternateSetting과 같지 않다면, index를 1 증가시키고 계속한다.

    5. bNumEndpoints가 0과 같다면, endpointDescriptors를 반환한다.

    6. numEndpointsdescriptorbNumEndpoints로 한다.

    7. indexEndpoints를 0으로 한다.

    8. offsetindex + 1로 한다

    9. indexEndpointsnumEndpoints보다 작은 동안:

      1. Append descriptors[indexEndpoints + offset]를 endpointDescriptors에 추가한다.

      2. indexEndpoints를 1 증가시킨다.

    10. endpointDescriptors를 반환한다.

6.5.1. 생성자

USBAlternateInterface(deviceInterface, alternateSetting) 생성자는 호출될 때 MUST 다음 단계를 수행한다:
  1. this.[[interface]]deviceInterface로 설정한다.

  2. this.[[alternateSetting]]alternateSetting으로 설정한다.

  3. descriptorsinterfaceNumberdeviceInterface.interfaceNumber로, alternateSettingalternateSetting으로, configurationValuedeviceInterface.[[configuration]].[[configurationValue]]로 설정하여 엔드포인트 디스크립터 목록을 찾기의 결과로 한다.

  4. descriptorsdescriptor에 대하여:

    1. descriptorbmAttributes가 Control Transfer Type(즉, endpoint descriptor에 따라 비트 0..100)을 나타내면, 계속한다.

    2. endpointAddressdescriptorbEndpointAddress로 한다.

    3. endpointAddress & 0x800이면 dir"out"으로, 그렇지 않으면 "in"으로 한다.

    4. endpoint새로운 USBEndpoint 객체로 하되, USBEndpoint(alternate,endpointNumber,direction)를 사용하고 alternatethis로, endpointNumberendpointAddress & 0xF로, directiondir로 설정한다.

    5. Append endpointthis.[[endpoints]]에 추가한다.

6.5.2. 속성

alternateSetting, 유형 octet, readonly

각 대체 인터페이스 구성은 주어진 인터페이스 내에서 고유한 alternateSetting을 가져야 하며(SHALL), 이는 이를 정의하는 interface descriptorbAlternateSetting 필드와 일치해야 한다.

interfaceClass, 유형 octet, readonly
interfaceSubclass, 유형 octet, readonly
interfaceProtocol, 유형 octet, readonly

interfaceClass, interfaceSubclass, interfaceProtocol 속성은 인터페이스가 지원하는 통신 인터페이스를 선언한다. 이 값들은 각각 interface descriptorbInterfaceClass, bInterfaceSubClass, bInterfaceProtocol 필드 값과 일치해야 한다(MUST).

interfaceName, 유형 DOMString, readonly, nullable

interfaceName 속성은, 정의되어 있다면, interface descriptoriInterface 필드로 색인되는 string descriptor의 값을 포함해야 한다(SHOULD).

endpoints, 유형 FrozenArray<USBEndpoint>, readonly

endpoints 속성은 이 인터페이스에 의해 노출되는 엔드포인트 목록을 포함해야 한다(SHALL). 이 엔드포인트들은 이 interface descriptor에 포함된 endpoint descriptors로부터 채워져야 하며(SHALL), 이 시퀀스의 원소 수는 interface descriptorbNumEndpoints 값과 일치해야 한다(SHALL).

endpoints 게터 단계는 다음과 같다:

  1. this.[[endpoints]]를 반환한다.

6.6. USBEndpoint 인터페이스

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

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

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBEndpoint {
  constructor(USBAlternateInterface alternate, octet endpointNumber, USBDirection direction);
  readonly attribute octet endpointNumber;
  readonly attribute USBDirection direction;
  readonly attribute USBEndpointType type;
  readonly attribute unsigned long packetSize;
};

USBEndpoint 인스턴스는 다음 표에 설명된 내부 슬롯을 가지고 생성됩니다:

내부 슬롯 초기값 설명 (비규범)
[[alternateInterface]] <항상 본문에서 설정> USBAlternateInterface 객체. this가 속함.
[[endpointAddress]] <항상 본문에서 설정> 이 엔드포인트의 엔드포인트 주소.
주어진 endpoint 객체에 대해 엔드포인트 디스크립터 찾기를 수행하려면, 다음 단계를 수행한다:
  1. alternateInterfaceendpoint.[[alternateInterface]]로 한다.

  2. interfacealternateInterface.[[interface]]로 한다.

  3. configurationinterface.[[configuration]]로 한다.

  4. endpointDescriptorsalternateSettingalternateInterface.alternateSetting으로, interfaceNumberinterface.interfaceNumber로, configurationValueconfiguration.configurationValue로 설정하여 엔드포인트 디스크립터 목록 찾기의 결과로 한다.

  5. endpointDescriptor (of endpointDescriptors)에 대하여:

    1. endpoint.[[endpointAddress]]endpointDescriptorbEndpointAddress와 같으면, endpointDescriptor를 반환한다.

6.6.1. 생성자

endpointNumber(alternate, endpointNumber, direction) 생성자는 호출될 때 반드시 다음 단계를 수행해야 한다:
  1. this.[[alternateInterface]]alternate로 설정한다.

  2. endpointAddressdirection"in"이면 endpointNumber | 0x80으로, 그렇지 않으면 endpointNumber로 한다.

  3. this.[[endpointAddress]]endpointAddress로 설정한다.

6.6.2. 속성

endpointNumber, 유형 octet, readonly
direction, 유형 USBDirection, readonly

특정 장치 구성 내부의 각 엔드포인트는 endpointNumberdirection 의 조합이 고유해야 한다.
endpointNumber 는 해당 엔드포인트를 정의하는 엔드포인트 디스크립터bEndpointAddress 필드의 하위 4비트와 반드시 같아야 한다(MUST).

direction 속성은 해당 엔드포인트가 지원하는 데이터 전송 방향을 나타내며, bEndpointAddress의 최상위 비트가 설정되어 있으면 "in"이고, 아니면 "out"이다. 엔드포인트는 데이터를 IN 방향(장치→호스트) 또는 OUT 방향(호스트→장치) 중 하나로만 전송할 수 있다.

type, 유형 USBEndpointType, readonly

type 속성은 해당 엔드포인트가 지원하는 데이터 전송 타입을 나타낸다.

type 게터의 단계는 다음과 같다:

  1. endpointDescriptor엔드포인트 디스크립터 찾기의 결과로 this를 인자로 한다.

  2. attrendpointDescriptorbmAttributes로 한다.

  3. typeBitsattr & 0x3로 한다.

  4. typeBitsb01이면 "isochronous"를 반환한다.

  5. typeBitsb10이면 "bulk"를 반환한다.

  6. typeBitsb11이면 "interrupt"를 반환한다.

참고: USBAlternateInterface(deviceInterface,alternateSetting)의 단계에 따라 Control Transfer Type에 속하는 엔드포인트 객체는 생성되지 않는다.

packetSize, 유형 unsigned long, readonly

packetSize 속성은 해당 엔드포인트에서 사용되는 패킷 크기를 나타내며, 해당 엔드포인트를 정의하는 엔드포인트 디스크립터wMaxPacketSize 값과 반드시 같아야 한다(MUST). 하이스피드, 고대역폭 엔드포인트의 경우 이 값은 마이크로프레임당 여러 트랜잭션에 따른 곱하기 계수를 포함한다. 슈퍼스피드 장치의 경우 이 값은 슈퍼스피드 엔드포인트 동반 디스크립터의 bMaxBurst 필드에 의한 곱하기 계수를 포함한다.

packetSize 게터의 단계는 다음과 같다:

  1. endpointDescriptor엔드포인트 디스크립터 찾기의 결과로 this를 인자로 한다.

  2. endpointDescriptorwMaxPacketSize를 반환한다.

7. USB 차단 목록

// USBBlocklistEntry는 외부에 노출되지 않습니다.
dictionary USBBlocklistEntry {
  required unsigned short idVendor;
  required unsigned short idProduct;
  required unsigned short bcdDevice;
};

이 명세는 이 저장소의 blocklist.txt 파일에 의존하여, 웹사이트가 접근할 수 있는 디바이스의 범위를 제한합니다.

차단 목록 해석의 결과는 URL url에서 다음과 같은 알고리즘을 통해 생성되는 list 형식의 USBBlocklistEntry 객체들입니다:

  1. url을 가져오고, 그 body를 UTF-8로 디코딩하여 contents로 한다.

  2. linesstrictly splitting을 이용해 contents'\n' 코드 포인트 기준으로 분할한 결과로 한다.

  3. blocklist를 빈 list로 한다.

  4. linesline에 대해:

    1. line을, 시작부터 '#'가 아닌 모든 코드 포인트 시퀀스 추출로 변환한다.

    2. line에서 앞뒤의 ASCII 공백 문자 제거 결과로 다시 line을 설정한다.

    3. componentsstrictly splitting을 사용하여 line':' 기준으로 분할한 결과로 한다.

    4. componentssize가 2나 3이 아니면 continue한다.

    5. idVendorcomponents[0]을 16진수로 해석한 값으로 한다.

    6. idProductcomponents[1]을 16진수로 해석한 값으로 한다.

    7. bcdDevice0xFFFF로 한다.

    8. componentssize가 3이면, bcdDevicecomponents[2]의 16진수 해석값으로 한다.

    9. 새로운 USBBlocklistEntryidVendor, idProduct, bcdDevice와 함께 생성해 blocklist에 추가한다.

  5. blocklist를 반환한다.

USB 차단 목록차단 목록 해석 결과로, https://raw.githubusercontent.com/WICG/webusb/main/blocklist.txt에서 불러온 목록이다. UA는 이 리스트를 주기적으로 다시 받아야 하지만, 얼마나 자주 받아야 하는지는 명시되지 않는다.

USBDevice deviceDocument document에 대해 차단됨(blocklisted) 상태인지는, 아래 단계가 "blocked"를 반환할 때 그렇다:

  1. documentnull이 아니고, document"usb-unrestricted"라는 정책 제어 기능을 사용할 수 있다면, "not blocked"를 반환한다.

  2. entryUSB 차단 목록의 원소로 하여:

    1. device.vendorIdentry.idVendor와 다르면, 계속.

    2. device.productIdentry.idProduct와 다르면, 계속.

    3. bcdDevicedevice.deviceVersionMajor << 8 + device.deviceVersionMinor << 4 + device.deviceVersionSubminor로 한다.

    4. bcdDeviceentry.bcdDevice보다 작거나 같으면, "blocked"를 반환한다.

  3. "not blocked"를 반환한다.

8. 통합

8.1. 권한 정책

이 명세는 정책 제어 기능을 정의합니다. 이 기능은 "usb" 토큰으로 식별되며, usb 속성이 Navigator 객체에 노출될지 여부를 제어합니다.

이 기능의 기본 허용 목록["self"] 입니다.

이 명세는 두 번째 정책 제어 기능도 정의합니다. "usb-unrestricted" 토큰으로 식별되는 이 기능은 차단 목록에 있는 USB 디바이스와 보호된 클래스를 가진 디바이스 인터페이스에 대한 접근을 제어합니다. 이 기능은 Isolated Web Apps에서 Web Application Manifest [APPMANIFEST]에 해당 기능을 선언한 경우에만 활성화되어야 합니다(MUST).

이 기능의 기본 허용 목록["self"]입니다. 일반적으로 이 기능은 최상위 탐색 컨텍스트Document에 허용되어야 한다고 볼 수 있습니다. 그러나 매니페스트에 이 기능이 선언된 Isolated Web Apps에서만 기능이 활성화되어야 하므로, 실질적 기본 허용 목록은 ["none"]입니다.

8.2. 권한 API

[permissions] API는 웹사이트가 사용자에게 권한을 요청하고 현재 권한 상태를 질의할 수 있도록 하는 일관된 방법을 제공합니다.

"usb" 강력 기능(powerful feature)은 아래와 같이 정의됩니다:

permission descriptor type
dictionary USBPermissionDescriptor : PermissionDescriptor {
  sequence<USBDeviceFilter> filters;
  sequence<USBDeviceFilter> exclusionFilters;
};
extra permission data type
USBPermissionStorage, 정의:
dictionary AllowedUSBDevice {
  required octet vendorId;
  required octet productId;
  DOMString serialNumber;
};

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

AllowedUSBDevice 인스턴스들은 내부 슬롯 [[devices]]을 갖는데, 이 슬롯에는 USB 디바이스의 배열이 저장됩니다.

permission result type
[Exposed=(Worker,Window)]
interface USBPermissionResult : PermissionStatus {
  attribute FrozenArray<USBDevice> devices;
};
permission query algorithm
"usb" 권한 쿼리USBPermissionDescriptor desc, USBPermissionStorage storage, USBPermissionResult status로 수행할 때, UA는 다음을 따라야 한다:
  1. desc.filters 값이 설정되어 있으면, desc.filters의 각 filter에 대해 filter유효한 필터가 아니면 TypeError를 발생시키고, 단계를 중단한다.

  2. desc.exclusionFilters 값이 설정되어 있으면, desc.exclusionFilters의 각 exclusionFilter에 대해 exclusionFilter유효한 필터가 아니면 TypeError를 발생시키고, 단계를 중단한다.

  3. status.state 값을 "prompt"로 설정한다.

  4. matchingDevices를 새로운 Array로 한다.

  5. storage.allowedDevices의 각 allowedDeviceallowedDevice@[[devices]]의 각 device에 대해 다음 하위단계를 실행한다:

    1. desc.filters이 설정되어 있고, devicedesc.filters의 어떤 디바이스 필터와 일치하지 않는다면, 다음 device로 넘어간다.

    2. desc.exclusionFilters 이 설정되어 있고, devicedesc.exclusionFilters디바이스 필터에 일치한다면, 다음 device로 넘어간다.

    3. device를 대표하는 USBDevice를 가져와 matchingDevices에 추가한다.

  6. status.devices 값을 matchingDevices로 구성된 새로운 FrozenArray로 설정한다.

permission request algorithm
"usb" 권한 요청(Request the "usb" permission).

9. 용어 정의

이 명세서는 [USB31]에서 가져온 여러 용어를 사용합니다. Universal Serial Bus 3.1 버전을 참조하고 있으나, 이러한 개념들은 이전 버전에도 존재합니다. 이 명세에 영향을 미치는 USB 버전 간의 중요한 차이점들은 별도로 명시합니다.

디스크립터(Descriptor)는 디바이스로부터 읽을 수 있으며 장치의 속성과 기능을 설명하는 이진 데이터 구조입니다:

Binary Object Store (BOS)는 표준 디바이스 디스크립터보다 더 자유롭게 정의되는 추가 디스크립터 모음입니다. 플랫폼 디스크립터(Platform Descriptor) 타입이 주목할 만하며, 타사(이 명세 등)가 자체 디스크립터 타입을 선언하도록 허용합니다. 각각은 UUID로 식별됩니다. Binary Object Store는 [USB31]의 9.6.2절에서 설명합니다.

USB 디바이스는 하나의 디바이스 디스크립터를 가지고, 이는 하나 이상의 구성 디스크립터와 연결됩니다. 벤더 ID(vendor ID)는 USB-IF가 장치 제조업체에 할당하며, 디바이스 디스크립터idVendor 필드에 저장됩니다. 제품 ID(product ID)는 제조사가 할당하며 디바이스 디스크립터idProduct 필드에 저장됩니다. 시리얼 번호(serial number)는 선택적 속성으로, 디바이스 디스크립터iSerialNumber 필드가 0이 아닐 때 정의되며, 그 인덱스로 참조되는 문자열 디스크립터(string descriptor) 값입니다.

Get Configuration은 현재 장치의 구성값을 요청하는 명령이며, [USB31]의 9.4.2절에 설명됩니다.

Set Descriptor는 지정된 디스크립터 타입 및 디스크립터 인덱스 값을 가진 디스크립터를 얻는 요청으로, [USB31]의 9.4.8절에 설명되어 있습니다.

Get Descriptor는 지정된 디스크립터 타입 및 인덱스 값을 가진 디스크립터를 얻는 요청으로, [USB31]의 9.4.3절에서 설명합니다.

참고: 장치의 디스크립터는 대부분의 경우 변하지 않으며(가끔 Set Descriptor에 의해 변경될 수 있음), 불필요한 Get Descriptor 트래픽을 줄이기 위해 디스크립터 정보를 저장하는 읽기/쓰기 캐시 계층을 구현할 수 있습니다.

컨트롤 전송(control transfer)은 디바이스를 구성할 때 가장 자주 사용되는 USB 트래픽의 특별한 유형입니다. 3단계( 세트업, 데이터, 상태 )로 이루어집니다. 세트업 단계(setup stage)에서 세트업 패킷(setup packet)을 장치로 전송하며, 이때 전송 방향 및 데이터 크기 등 요청 파라미터가 포함됩니다. 데이터 단계(data stage)는 데이터를 장치에 보내거나 장치로부터 수신하며, 상태 단계(status stage)에서 요청이 성공적으로 처리된 경우 이를 확인하거나, 실패가 발생하면 신호합니다.

10. 부록: USB 간략 소개

이 섹션은 비규범적입니다.

USB는 네트워크이지만, 전통적인 TCP/IP 네트워크와 매우 다릅니다. 실제로는 RPC 시스템에 더 가깝습니다. 모든 트래픽은 호스트(즉, 여러분의 컴퓨터)가 지시합니다. 일부 스마트폰과 같은 장치는 USB 호스트와 USB 클라이언트 역할을 모두 할 수 있지만 한 번에 한 역할만 수행합니다.

10.1. 디스크립터

USB 장치는 디스크립터라고 불리는 일련의 이진 구조를 호스트에 제공하여 자신을 식별합니다. 호스트가 처음 읽는 것은 디바이스 디스크립터로, 벤더 및 제조사에 의해 할당된 벤더/제품 ID, 제조사 등 기본 정보를 담고 있습니다. 그 다음으로 호스트는 해당 장치의 구성 디스크립터를 읽을 수 있는데, 이 디스크립터는 인터페이스와 엔드포인트 등 장치의 기능을 설명합니다. 클래스는 장치 레벨이나 각 인터페이스에 선언할 수 있습니다. 여러 기능을 제공하는 여러 인터페이스를 가진 장치는 복합 장치(composite device)로 알려져 있습니다.

10.2. 전송(Transfers)

데이터가 호스트에서 장치로 이동하든 반대로 이동하든, 전송은 항상 호스트가 시작합니다. OUT 전송은 데이터를 호스트에서 장치로 보내며, 장치가 데이터 수신을 확인할 때까지 대기할 수 있습니다. IN 전송은 데이터를 장치에서 호스트로 보내며, 장치가 보낼 데이터를 준비할 때까지 기다릴 수 있습니다. 전송은 장치의 엔드포인트 중 하나를 대상으로 수행되며, 전송되는 트래픽 종류에 따라 여러 종류가 존재합니다.

적합성(Conformance)

문서 규약(Document conventions)

적합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 본 문서의 규범적인 부분에서 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", "OPTIONAL" 과 같은 키워드는 RFC 2119에서 설명한 대로 해석되어야 합니다. 하지만 가독성을 위해, 이런 단어들은 본 명세서에서 모두 대문자로 나타나지는 않습니다.

이 명세의 모든 텍스트는 명시적으로 비규범(nonnormative) 또는 예시, 주석으로 표시된 부분을 제외하고 규범적입니다. [RFC2119]

이 명세서의 예시는 "예를 들어(for example)"라는 단어와 함께 소개되거나, class="example"로 구분되어, 다음과 같이 표시됩니다:

이것은 정보성 예시의 한 예입니다.

정보성 주석은 "참고(Note)"라는 단어로 시작하며, class="note"로 구분되어, 다음과 같이 표시됩니다:

참고, 이것은 정보성 주석입니다.

색인(Index)

이 명세서에서 정의된 용어(Terms defined by this specification)

참조로 정의된 용어

참고문헌

규범적 참고문헌

[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS Values and Units Module Level 4. URL: https://drafts.csswg.org/css-values-4/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMAScript]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[HTML]
Anne van Kesteren; 외. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres; Mike Taylor. Permissions. URL: https://w3c.github.io/permissions/
[PERMISSIONS-POLICY-1]
Ian Clelland. Permissions Policy. URL: https://w3c.github.io/webappsec-permissions-policy/
[PERMISSIONS-REQUEST]
Requesting Permissions. Draft Community Group Report. URL: https://wicg.github.io/permissions-request/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997년 3월. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SERVICE-WORKERS]
Jake Archibald; Marijn Kruisselbrink. Service Workers. URL: https://w3c.github.io/ServiceWorker/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[USB31]
Universal Serial Bus 3.1 Specification. 2013년 7월 26일. URL: http://www.usb.org/developers/docs/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

정보성 참고문헌

[APPMANIFEST]
Marcos Caceres; 외. Web Application Manifest. URL: https://w3c.github.io/manifest/
[CORS]
Anne van Kesteren. Cross-Origin Resource Sharing. 2020년 6월 2일. REC. URL: https://www.w3.org/TR/cors/
[POWERFUL-FEATURES]
Mike West. Secure Contexts. URL: https://w3c.github.io/webappsec-secure-contexts/
[RFC4122]
A Universally Unique IDentifier (UUID) URN Namespace. 2005년 7월. URL: https://tools.ietf.org/html/rfc4122

IDL 색인

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

dictionary USBDeviceRequestOptions {
  required sequence<USBDeviceFilter> filters;
  sequence<USBDeviceFilter> exclusionFilters = [];
};

[Exposed=(Worker,Window), SecureContext]
interface USB : EventTarget {
  attribute EventHandler onconnect;
  attribute EventHandler ondisconnect;
  Promise<sequence<USBDevice>> getDevices();
  [Exposed=Window] Promise<USBDevice> requestDevice(USBDeviceRequestOptions options);
};

[Exposed=Window, SecureContext]
partial interface Navigator {
  [SameObject] readonly attribute USB usb;
};

[Exposed=Worker, SecureContext]
partial interface WorkerNavigator {
  [SameObject] readonly attribute USB usb;
};

dictionary USBConnectionEventInit : EventInit {
    required USBDevice device;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBConnectionEvent : Event {
  constructor(DOMString type, USBConnectionEventInit eventInitDict);
  [SameObject] readonly attribute USBDevice device;
};

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

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBInTransferResult {
  constructor(USBTransferStatus status, optional DataView? data);
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBOutTransferResult {
  constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0);
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousInTransferPacket {
  constructor(USBTransferStatus status, optional DataView? data);
  readonly attribute DataView? data;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousInTransferResult {
  constructor(sequence<USBIsochronousInTransferPacket> packets, optional DataView? data);
  readonly attribute DataView? data;
  readonly attribute FrozenArray<USBIsochronousInTransferPacket> packets;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousOutTransferPacket {
  constructor(USBTransferStatus status, optional unsigned long bytesWritten = 0);
  readonly attribute unsigned long bytesWritten;
  readonly attribute USBTransferStatus status;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBIsochronousOutTransferResult {
  constructor(sequence<USBIsochronousOutTransferPacket> packets);
  readonly attribute FrozenArray<USBIsochronousOutTransferPacket> packets;
};

[Exposed=(Worker,Window), SecureContext]
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<undefined> open();
  Promise<undefined> close();
  Promise<undefined> forget();
  Promise<undefined> selectConfiguration(octet configurationValue);
  Promise<undefined> claimInterface(octet interfaceNumber);
  Promise<undefined> releaseInterface(octet interfaceNumber);
  Promise<undefined> selectAlternateInterface(octet interfaceNumber, octet alternateSetting);
  Promise<USBInTransferResult> controlTransferIn(USBControlTransferParameters setup, unsigned short length);
  Promise<USBOutTransferResult> controlTransferOut(USBControlTransferParameters setup, optional BufferSource data);
  Promise<undefined> 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<undefined> reset();
};

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

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

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

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBConfiguration {
  constructor(USBDevice device, octet configurationValue);
  readonly attribute octet configurationValue;
  readonly attribute DOMString? configurationName;
  readonly attribute FrozenArray<USBInterface> interfaces;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBInterface {
  constructor(USBConfiguration configuration, octet interfaceNumber);
  readonly attribute octet interfaceNumber;
  readonly attribute USBAlternateInterface alternate;
  readonly attribute FrozenArray<USBAlternateInterface> alternates;
  readonly attribute boolean claimed;
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBAlternateInterface {
  constructor(USBInterface deviceInterface, octet alternateSetting);
  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"
};

[
  Exposed=(Worker,Window),
  SecureContext
]
interface USBEndpoint {
  constructor(USBAlternateInterface alternate, octet endpointNumber, USBDirection direction);
  readonly attribute octet endpointNumber;
  readonly attribute USBDirection direction;
  readonly attribute USBEndpointType type;
  readonly attribute unsigned long packetSize;
};

// USBBlocklistEntry is never exposed.
dictionary USBBlocklistEntry {
  required unsigned short idVendor;
  required unsigned short idProduct;
  required unsigned short bcdDevice;
};

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

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

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

[Exposed=(Worker,Window)]
interface USBPermissionResult : PermissionStatus {
  attribute FrozenArray<USBDevice> devices;
};

이슈 색인

장치가 리셋된 후 어떤 구성(configuration)에 있습니까? [이슈 #36]
장치 구성에 대한 비규범(non-normative) 정보를 일부 포함하세요. [이슈 #46]
MDN

USB/connect_event

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USB/connect_event

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USB/disconnect_event

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USB/disconnect_event

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USB/getDevices

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USB/requestDevice

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USB

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBAlternateInterface

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConfiguration/USBConfiguration

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConfiguration/configurationName

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConfiguration/configurationValue

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConfiguration/interfaces

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConfiguration

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConnectionEvent/USBConnectionEvent

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConnectionEvent/device

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBConnectionEvent

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/claimInterface

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/clearHalt

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/close

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/configuration

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/configurations

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/controlTransferIn

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/controlTransferOut

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/deviceClass

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/deviceProtocol

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/deviceSubclass

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/deviceVersionMajor

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/deviceVersionMinor

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/deviceVersionSubminor

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/forget

In only one current engine.

FirefoxNoneSafariNoneChrome101+
Opera?Edge101+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/isochronousTransferIn

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/isochronousTransferOut

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/manufacturerName

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/open

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/opened

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/productId

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/productName

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/releaseInterface

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/reset

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/selectAlternateInterface

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/selectConfiguration

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/serialNumber

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/transferIn

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/transferOut

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/usbVersionMajor

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/usbVersionMinor

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/usbVersionSubminor

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice/vendorId

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBDevice

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBEndpoint

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBInTransferResult

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBInterface

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBIsochronousInTransferPacket

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBIsochronousInTransferResult

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBIsochronousOutTransferPacket

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBIsochronousOutTransferResult

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

USBOutTransferResult

In only one current engine.

FirefoxNoneSafariNoneChrome61+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?
MDN

Headers/Feature-Policy/usb

In only one current engine.

FirefoxNoneSafariNoneChrome60+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?

Headers/Permissions-Policy/usb

In only one current engine.

FirefoxNoneSafariNoneChrome88+
Opera?Edge88+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebViewNoneSamsung Internet?Opera Mobile?