WebUSB API

コミュニティ・グループ草案,

この版:
https://wicg.github.io/webusb
課題追跡:
GitHub
仕様内のインライン
編集者:
(Google LLC)
(Google LLC)
(Google LLC)
参加方法:
W3C コミュニティ・グループに参加
IRC: W3C の IRC 上の #webusb(回答があるまでお待ちください。時間がかかる場合があります)
StackOverflow で質問する

概要

本書は、ウェブページからユニバーサル・シリアル・バス(USB)デバイスへのアクセスを安全に提供するAPIについて説明します。

この文書のステータス

この仕様は Web Platform Incubator Community Group により公開されました。 これはW3C標準ではなく、またW3C標準化トラックにもありません。 W3C Community Contributor License Agreement (CLA) の下では、限定的なオプトアウトや他の条件が適用されることにご注意ください。 W3C Community and Business Groups についてさらに詳しく知るにはこちらをご覧ください。

1. はじめに

このセクションは非規範です

ユニバーサル・シリアル・バス(USB)は、有線周辺機器における事実上の標準です。 ほとんどのUSBデバイスは、およそ十数種類の標準「デバイスクラス」のいずれかを実装しており、 これらはデバイスがサポートする機能やその機能の使い方に関するコマンド・データフォーマットを広告する方法を決定します。標準デバイスクラスにはキーボード、マウス、オーディオ、ビデオ、ストレージデバイスなどがあります。OSはこのようなデバイスを、OSベンダが提供する「クラスドライバー」でサポートします。しかし標準化されたデバイスクラスには当てはまらない膨大な種類のデバイスも存在します。こうしたデバイスでは、ハードウェアベンダが独自のネイティブドライバーやSDKを書く必要があり、開発者はそれを利用することになりますが、このネイティブコードによりウェブからデバイスを利用することができなくなります。

WebUSB APIは、USBデバイスサービスを安全にウェブに公開する方法を提供します。 これは既存のネイティブUSBライブラリに慣れ親しんだ開発者にも分かりやすいAPIとなっており、既存仕様で定義されたデバイスインターフェイスも公開します。このAPIによってハードウェアメーカは自社デバイス用のクロスプラットフォームJavaScript SDKを構築できるようになります。これにより、新種のデバイスがウェブブラウザに特化したAPIを提供するほど普及するのを待つ必要がなく、ウェブ向けの革新的なハードウェアが初日から作れるようになり、ウェブにとって大きな利点になります。

USBについての詳細は§ 10 付録: USBの簡単な紹介をご覧ください。

2. 動機付けとなるアプリケーション

このセクションは非規範です

2.1. 教育用デバイス

ウェブのソフトウェア配信モデルは教育アプリケーションにとって重要な要素です。なぜなら、それらはプラットフォーム互換性や管理者権限といった問題なく、どのコンピューターにもすぐロードできるからです。理科の授業ではコンピュータを使った計測やデータロギングが導入されています。これらのツールにはバンドルソフトウェアが必要ですが、新たなネイティブアプリが管理されたPCに増えるたび、IT部門に負担がかかりインストールも困難です。ウェブベースのハードウェアAPIにより、これらデバイスへの対応を既存のオンライン教材に直接組み込めるため、完全にシームレスな体験を実現できます。

多くのマイコン開発キットでプログラミングを学んでいる学生は、オンラインの開発ツールを使って自分のコードを書いたりアップロードできます。こうしたツールはすでに存在しますが、ブラウザとハードウェア間のインターフェイスにはネイティブコンポーネントが必要です。このネイティブな拡張は参入障壁となり、またウェブのサンドボックス環境内で動作するコードでは発生しないセキュリティ脆弱性をユーザーにさらすおそれがあります。

2.2. ウェブドライバー

ウェブの構成可能性によって、すべてウェブ技術だけで新しいハードウェアサポートのエコシステムを構築できます。3Dプリンターを例に取ると、3Dオブジェクト設計をホストしているサイトが直接印刷機能をそのページに統合したいとします。ウェブは2D印刷はサポートしていますが、3D用APIはありません。もしハードウェアベンダがWebUSB APIを使ってプリンタへデータを送る埋め込みページを公開すれば、他サイトもこうしたページを活用して、既存の地図埋め込みと同じような形でハードウェア連携機能を追加できます。

2.3. デバイスの更新と診断

Bluetoothのような無線プロトコルが消費者向けデバイスで便利な選択肢になる一方、USBポートは電源供給の容易な手段として、またデバイスが動作しない場合最後の接続手段として普及し続けています。サポートサイトに更新・診断ツールを統合することで、ハードウェアメーカーはプラットフォームを問わず顧客にツールを提供し、サポート問い合わせ時により良好な診断データを収集できます。ランディングページによって、メーカーはユーザーを自社ウェブサイトの該当サポート箇所へ誘導できます。

3. セキュリティおよびプライバシーの考慮事項

このセクションは非規範です

WebUSB APIは強力な機能を持つため、ユーザーに新たなプライバシーおよびセキュリティリスクをもたらす可能性があります。こうしたリスクは大きく三つのカテゴリーに分類でき、以降のセクションで説明します。

3.1. デバイスへのアクセスの悪用

周辺機器には様々な用途があります。たとえばフラッシュドライブのようにデータを保存したり、カメラやマイクのように外部の情報を収集したり、プリンタのように外部の物を操作することもあります。上記の例はすべて、ウェブプラットフォームには悪用防止のセキュリティ機能付き高レベルAPIが用意されています。外部ドライブへのデータ保存や取得にはユーザーが手動でファイルを選ぶ必要があり、マイクやカメラの起動にはユーザーの権限許可が必要で、データ収集中にはインジケータが点灯します。印刷も明示的な操作が必要です。このAPIは既存の高レベルAPIに含まれないデバイス用の汎用的な仕組みを提供するため、同様に汎用的な悪用防止手段が求められます。

これらの保護の第一はrequestDevice()関数です。UA(ユーザーエージェント)はこの関数が呼ばれた際に権限プロンプトを表示する場合があります。たとえ悪意のないページでも、ユーザーがそのようなデバイス接続の可能性を認識する前に自動で接続が行われるのを防止することで、ユーザーのプライバシーも守られます。またUAは、デバイス接続が有効なときにインジケータを表示することもあります。

次に、本仕様は[powerful-features]で定義されるセキュアコンテキストのみからUSBデバイスへアクセスできるよう要求します。これにより、発信元で実行されているコードの正当性が確保され、デバイスから読み取ったデータが途中で傍受されないよう保証されます。

さらに、USBデバイスは複数の依頼元からのリクエストを区別できません。そのためOSは、USBインターフェイスに単一のユーザ空間またはカーネル空間ドライバしか割り当てられないようにしています。UAはユーザ空間ドライバとして振る舞うため、同時にUSBインターフェイスを占有できるのは一つの実行コンテキストだけです。claimInterface() 関数は、複数の実行コンテキストが同じインターフェイス獲得を試みた場合失敗します。

3.2. デバイスへの攻撃

歴史的に、高セキュリティ用途で設計されたのでなければ、USBデバイスは接続先ホストを信頼する設計となっており、ホストがデバイス機能へのアクセスを守る役割を担ってきました。本仕様策定にあたり、二つの選択肢が検討されました。一つ目はUAがリクエスト元のオリジンをデバイスに通知する案です。これはHTTPリクエストに含まれるReferrerヘッダーのようなものです。しかしこの方式だとアクセス制御の負担がデバイス側にかかってしまい、デバイスの処理能力・記憶容量が制限される中で避けたいコストになります。

そこで本仕様ではCORSに似た仕組みでUA側がアクセスを制御する方法を選びました。デバイスはUAに対し、許可されたオリジンの静的データ構造を渡します。既存デバイスへの移行期間のために、許可オリジン情報を外部の公開レジストリで管理する方式も考えられました。

しかしこの案にも大きな欠点がありました。新しいデバイスはWebUSB対応を考慮して設計する必要があり、公開レジストリも仕様化が難航しました。製品開発サイクルは長く、エディタドラフト段階では製品計画に影響を与える「力」はありません。また、この仕組みではサードパーティの開発者が自作アプリでデバイスを使う術がなく、イノベーションや開発者の恩恵が大きく後退します。

これらを踏まえ、著者らはrequestDevice() メソッドが促す許可プロンプトや§ 8.1 Permissions Policyとの連携が、デバイスへの不要なアクセスを防ぐために十分であると判断しました。

3.3. ホストへの攻撃

デバイスが不正に操作されると、その機能の悪用だけでなく、接続先ホストや、もし攻撃が永続化していれば後日接続された他ホストまでも攻撃されるおそれがあります。上記の方法はこうした攻撃経路軽減を目指しています。ただし一度第三者の制御下に落ちた(例: マルウェアファームウェア書き込み)デバイスについては、UAによる抑制には限界があります。

本仕様は、デバイスメーカーに対し、署名済みファームウェアアップデートの受け入れや、重要な設定変更には物理的アクセスを要するなど、防御を多層的に設計することを推奨します。

4. WebUSBディスクリプタとリクエスト

本仕様は、このAPIの実装に必要なデバイス固有情報収集のため、UAが使ってよいディスクリプタやコマンドを定義します。

4.1. WebUSBプラットフォーム機能ディスクリプタ

デバイスは、下記のプラットフォームディスクリプタバイナリオブジェクトストアに含めることで、WebUSBコマンドセットのサポートを表明します:

オフセット フィールド サイズ 説明
0 bLength 1 数値 このディスクリプタのサイズ。24を指定すること。
1 bDescriptorType 1 定数 DEVICE CAPABILITYディスクリプタ型([USB31] 表9-6)。
2 bDevCapabilityType 1 定数 PLATFORM機能タイプ([USB31] 表9-14)。
3 bReserved 1 数値 予約済みフィールド。0に設定すること。
4 PlatformCapabilityUUID 16 UUID {3408b638-09a9-47a0-8bfd-a0768815b665}を指定する。
20 bcdVersion 2 BCD サポートするプロトコルバージョン。0x0100を指定すること。
22 bVendorCode 1 数値 WebUSBリクエストを発行する際のbRequest値。
23 iLandingPage 1 数値 デバイスのランディングページの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デバイスリクエスト

本仕様で定義される制御転送はすべてベンダー固有リクエストとみなされます。WebUSBプラットフォーム機能ディスクリプタにあるVendorCode 値は、ホストがWebUSBリクエスト発行時にデバイスが期待するbRequestです。リクエストの種類はwIndexフィールドで指定します。

WebUSBリクエストコード
定数
(予約) 1
GET_URL 2

4.2.1. URLの取得

このリクエストは指定されたインデックスのURLディスクリプタを取得します。

デバイスは、指定されたインデックスのURLディスクリプタを返すか、インデックスが不正の場合は転送を停止(stall)しなければなりません(MUST)。

bmRequestType bRequest wValue wIndex wLength データ
11000000B bVendorCode ディスクリプタインデックス GET_URL ディスクリプタ長 ディスクリプタ

4.3. WebUSBディスクリプタ

これらのディスクリプタ型は、本仕様で定義されたリクエストによって返されます。

WebUSBディスクリプタ型
定数
(予約) 0-2
WEBUSB_URL 3

4.3.1. URLディスクリプタ

このディスクリプタは単一のURLを含み、URLの取得リクエストで返されます。

オフセット フィールド サイズ 説明
0 bLength 1 数値 このディスクリプタのサイズ
1 bDescriptorType 1 定数 WEBUSB_URL
2 bScheme 1 数値 URLスキーム接頭辞
3 URL 可変 文字列 UTF-8エンコードのURL(スキーム接頭辞を除く)

bSchemeフィールドは次のいずれかの値でなければなりません(MUST):

URLプレフィックス
プレフィックス
0 "http://"
1 "https://"
255 ""(空文字列)

特別な値255は、スキームを含めた全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に追加する。
  });
});

ページがロードされた後にユーザーがデバイスを 接続 または 切断 することがあるため、インターフェースを最新の状態に保つために、スクリプトもこれらのイベントのために登録しておくべきです。

navigator.usb.addEventListener('connect', event => {
  // |event.device| をUIに追加する。
});

navigator.usb.addEventListener('disconnect', event => {
  // |event.device| をUIから削除する。
});

ユーザーがページに初めて訪れた場合は、まだどのデバイスにもアクセス権がないので、まず requestDevice()該当グローバルオブジェクト一時的なアクティベーション がある間に呼び出す必要があります。この場合、ページはベンダー 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 タスクソース に作業をキューイングします。

USBデバイス deviceデバイスフィルタにマッチする filter の場合、次の手順が match を返します:

  1. deviceDescdeviceデバイスディスクリプタ とする。

  2. filter.vendorId が存在し、deviceDesc.idVendorfilter.vendorId と等しくない場合、 mismatch を返す。

  3. filter.productId が存在し、deviceDesc.idProductfilter.productId と等しくない場合、 mismatch を返す。

  4. filter.serialNumber が存在する場合、 serialNumberストリングディスクリプタ(インデックス deviceDesc.iSerialNumber)とする。 device から serialNumber 要求時にエラーとなるか、serialNumberfilter.serialNumber と等しくない場合、 mismatch を返す。

  5. filter.classCode が存在し、 device のいずれかのインターフェイス interfacefilterインターフェイスフィルタにマッチする 場合、 match を返す。

  6. filter.classCode が存在し deviceDesc.bDeviceClassfilter.classCode と等しくない場合、mismatch を返す。

  7. filter.subclassCode が存在し deviceDesc.bDeviceSubClassfilter.subclassCode と等しくない場合、mismatch を返す。

  8. filter.protocolCode が存在し deviceDesc.bDeviceProtocolfilter.protocolCode と等しくない場合、mismatch を返す。

  9. match を返す。

注: 上記手順では bDeviceClass, bDeviceSubClass および bDeviceProtocol フィールドを デバイスディスクリプタ の一部としてだけでなく、 インターフェイスディスクリプタ の一部であるかのようにも扱い、提供されたフィルタと比較します。

USBインターフェイス interfaceインターフェイスフィルタにマッチする filter 場合、次の手順が match を返します:

  1. descinterfaceインターフェイスディスクリプタ とする。

  2. filter.classCode が存在し desc.bInterfaceClassfilter.classCode と等しくない場合、mismatch を返す。

  3. filter.subclassCode が存在し desc.bInterfaceSubClassfilter.subclassCode と等しくない場合、mismatch を返す。

  4. filter.protocolCode が存在し desc.bInterfaceProtocolfilter.protocolCode と等しくない場合、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は、システムに接続されているすべてのデバイスを列挙 できなければなりません(MUST)。 ただし、アルゴリズムが列挙を要求するたびにこれを毎回行う必要はありません。UAは最初の列挙結果をキャッシュし、その後はデバイスの接続・切断イベントを監視して、接続デバイスをキャッシュに追加し切断されたデバイスを削除することができます。この動作モードは、getDevices()requestDevice() メソッドによるOS呼び出し回数やバス通信量を減らせるため好まれます。

onconnect 属性は connect イベントタイプのためのイベントハンドラーIDL属性です。

ondisconnect 属性は disconnect イベントタイプのためのイベントハンドラーIDL属性です。

getDevices() メソッドが呼び出された場合、次の手順を実行しなければなりません(MUST):
  1. globalthis該当グローバルオブジェクトとする。

  2. documentglobal関連付けられたDocument(なければ null)とする。

  3. storage を:

    1. USBPermissionStorage オブジェクト(ServiceWorkerGlobalScopeの場合は、そのスクリプト実行環境のもの)

    2. それ以外は現在のスクリプト実行環境の USBPermissionStorage オブジェクト

  4. promise新しいPromiseとする。

  5. 次の手順を 並列で実行する:

    1. システムに接続されているすべてのデバイスを列挙 し、これを enumerationResult とする。

    2. devices を新しい空のArrayとする。

    3. enumerationResult 内の各 device について:

      1. devicedocument に対して ブロックリストに登録されている場合、次へ

      2. このメソッドへの最初の呼出しであれば デバイスの権限を確認device, storage で)。

      3. storage.allowedDevices 内で deviceallowedDevice.[[devices]] に含まれているものを探す。 なければこのdeviceは次へ進む。

    4. USBDevice オブジェクトで device を表したものを devices に追加。

    5. グローバルタスクをキューイング(USBタスクソース上、global に対して)し、promisedevicesresolve する。

  6. promise を返す。

requestDevice(options) メソッドが呼び出された場合、次の手順を実行しなければなりません(MUST):
  1. 次のディスクリプタの使用許可を要求

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

    permissionResult を、得られた Promiseとする。

  2. permissionResult が fulfillment された時は result で次の手順:

    1. result.devices が空の場合は、 "NotFoundError" DOMException を投げて処理を中止。

    2. result.devices[0] を返す。

"usb" 権限を要求する方法は、Document document, USBPermissionStorage storage, USBPermissionDescriptor options および USBPermissionResult status を引数に次の手順を実行する(MUST):
  1. globalstorage該当グローバルオブジェクトとする。

  2. filteroptions.filters について、 filter有効なフィルタではない場合は TypeError で reject された promise を返す。

  3. exclusionFilteroptions.exclusionFilters について、 exclusionFilter有効なフィルタではない場合は TypeError で reject された promise を返す。

  4. アルゴリズムが global一時的なアクティベーション がある間に呼び出されたことを確認。さもなくば、 "SecurityError" DOMException で reject された promise を返す。

  5. promise新しいPromiseとする。

  6. 次の手順を 並列で実行する。

    1. システムに接続されているすべてのデバイスを列挙する。結果を enumerationResult とする。

    2. enumerationResult から ブロックリストされたデバイスを削除。

    3. enumerationResult から options.filters のデバイスフィルタに マッチしない デバイスを削除。

    4. enumerationResult から options.exclusionFilters のデバイスフィルタに マッチする デバイスを削除。

    5. ユーザーに enumerationResult からデバイスを選択するよう促すプロンプトを表示。UAは各デバイスの人間が読める名前を表示すべき(SHOULD)。

    6. ユーザーが device を選択するかプロンプトをキャンセルするのを待つ。

    7. グローバルタスクをキューイング(USBタスクソース上、global)し、次の処理を行う:

      1. status.state"ask" に設定。

      2. ユーザーがプロンプトをキャンセルした場合は、status.devices を空の FrozenArray にし、 promise を resolveundefined で終了。

      3. device を storage に追加

      4. deviceObjUSBDevice オブジェクトで作成。

      5. status.devicesFrozenArraydeviceObj のみ含む配列に。

      6. promise を resolveundefined で終了。

  7. promise を返す。

許可された USBデバイス deviceUSBPermissionStorage storage に追加するには、 UA は次の手順を実行(MUST):

  1. storage.allowedDevices 内で、deviceallowedDevice.[[devices]] に含まれているものを探す。あれば中止。

  2. vendorIdproductIddeviceベンダーIDプロダクトID とする。

  3. serialNumber をデバイスが持つ場合は deviceシリアル番号、そうでなければ undefined

  4. { vendorId: vendorId, productId: productId, serialNumber: serialNumber } を、 [[devices]] 内部スロットに device だけを含めて storage.allowedDevices に追加。

新しい USB デバイス の権限を確認するには、 USBPermissionStorage storage で次を実行する(MUST):

  1. vendorIdproductIddeviceベンダーIDプロダクトID とする。

  2. serialNumber をデバイスが持つ場合はそれ、なければ undefined

  3. storage.allowedDevices 内で、

    • allowedDevice.vendorIdvendorId に等しい

    • allowedDevice.productIdproductId に等しい

    • allowedDevice.serialNumberserialNumber に等しい

  4. 一致する要素がなければ null を返す。

  5. deviceallowedDevice@[[devices]] に追加。

  6. allowedDevice を返す。

許可された USBデバイス の削除は、 USBPermissionStorage storage で次を実行(MUST):

  1. storage.allowedDevices 内で、deviceallowedDevice.[[devices]] に含まれているものを探し、なければ中止。

  2. allowedDevicestorage.allowedDevices から削除する。

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;
};

注: Worker は connect および disconnect イベント用のイベントリスナを登録できますが、ワーカーがアクティブでない限りそのイベントリスナは呼び出されません。

UA がホストに新しい USB device device の接続を検出したとき、スクリプト実行環境ごとに次の手順を実行しなければなりません(MUST):

  1. storage を現在のスクリプト実行環境の USBPermissionStorage オブジェクトとする。

  2. device の権限を storage で確認し、その結果を allowedDevice とする。

  3. allowedDevicenull であれば、これらの手順を中止する。

  4. deviceObjdevice を表す USBDevice オブジェクトとする。

  5. connect という名前のイベントを発火 し、device の該当グローバルオブジェクトの Navigator オブジェクトの usb 上で、USBConnectionEvent を使用し、 device 属性を deviceObj に設定して発火する。

UA がホストから USB device device の切断を検出したとき、スクリプト実行環境ごとに次の手順を実行しなければなりません(MUST):

  1. storage を現在のスクリプト実行環境の USBPermissionStorage オブジェクトとする。

  2. storage.allowedDevices 内で、deviceallowedDevice.[[devices]] に含まれる要素 allowedDevice を検索し、該当する要素がなければこれらの手順を中止する。

  3. deviceallowedDevice@[[devices]] から削除する。

  4. もし allowedDevice.serialNumberundefined であり、かつ allowedDevice@[[devices]] が空である場合は allowedDevicestorage.allowedDevices から削除する。

  5. deviceUSBDevice オブジェクトとして用意する。

  6. disconnect という名前のイベントを発火 し、device の該当グローバルオブジェクトの Navigator オブジェクトの usb 上で、USBConnectionEvent を使用し、 device 属性を device に設定して発火する。

6. デバイスの使用

ここでは、最大8チャネルの16ビットデータをサポートするデータロギングデバイスを想定します。単一のコンフィギュレーション(bConfigurationValue 1)を持ち、単一のインターフェイス(bInterfaceNumber 1)に単一のバルクエンドポイント(bEndpointAddress 0x81、つまりエンドポイント1でINエンドポイント)があります。データがサンプリングされるとこのエンドポイントで取得できます。このエンドポイントの最大パケットサイズはすべての8チャネルが同時に有効になった場合をサポートするため16バイトです。しかしバス帯域幅を節約するため、任意のチャネルの組合せを有効/無効にできます。パケットは収集されたデータを送信するのに必要な長さだけになります。

まずデバイスを開き、最初のコンフィギュレーション(デバイスは1つしか持ちませんが、OS が列挙時に既にこれを選んでいない可能性があります)を選択し、データロギングインターフェイスをクレームします。

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バイトのバッファを要求します。完全なバッファを受信した場合、キャプチャした値(ビッグエンディアンで送信)がコンソールログに出力されます。もしデバイスがエラーを検出してエンドポイントをスタール(stall)した場合は、継続する前にエラーをクリアします。

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]] 空の sequenceUSBConfiguration の) デバイスがサポートするすべての構成。
[[configurationValue]] <本文で常に設定される> デバイスの現在のコンフィギュレーション値。
[[selectedAlternateSetting]] 空の整数リスト 現在のコンフィギュレーション上の各インターフェイスの現在のオルタネート設定。
[[claimedInterface]] 空の boolean リスト 現在のコンフィギュレーション上の各インターフェイスのクレーム状態。
接続されたUSBデバイスのデバイスディスクリプタを見つけるには、次の手順を実行します:
  1. deviceDescriptor を、Get Descriptor を DEVICE に指定して取得した、接続されたデバイスのデバイスディスクリプタとする。

  2. deviceDescriptor を返す。

接続されたUSBデバイスのコンフィギュレーションディスクリプタの一覧を見つけるには、次の手順を実行します:
  1. deviceDescriptor を、接続されたUSBデバイスのデバイスディスクリプタを見つける ことによって得た結果とする。

  2. numConfigurationsdeviceDescriptorbNumConfigurations とする。

  3. configurationIndex を 0 とする。

  4. configurationDescriptors を空のリストとする。

  5. configurationIndexnumConfigurations より小さい間:

    1. descriptors を、Get Descriptor を CONFIGURATION とし、DescriptorIndex を configurationIndex にして実行して得たディスクリプタのリストとする。

    2. もし descriptors[0] の bDescriptorType が CONFIGURATION と等しければ、descriptors[0] を configurationDescriptors に追加する。

    3. configurationIndex を 1 増やす。

  6. configurationDescriptors を返す。

インターフェイス上で現在スケジュールされている転送を中止するには、与えられた interface オブジェクトに対して次の手順を実行します:
  1. globalinterface の該当グローバルオブジェクトとする。

  2. configurationinterface.[[configuration]] とする。

  3. deviceconfiguration.[[device]] とする。

  4. もし configuration現在のコンフィギュレーションの検索 の結果と等しくなければ、返す。

  5. もし インターフェイスがクレームされているかを調べる の結果が true でなければ、返す。

  6. currAlternateInterface現在のオルタネート設定のためのオルタネイトインターフェイスを見つける の結果とする。

  7. endpointcurrAlternateInterface.[[endpoints]] について:

    1. endpoint 上で現在スケジュールされているすべての転送を中止する。

    2. グローバルタスクをキューイング(USBタスクソース上、global)し、関連する promise を "AbortError"(DOMException)で reject する。

インターフェイスインデックスを見つけるには、与えられた interfaceNumberconfiguration オブジェクトに対して次の手順を実行します:
  1. interfaceIndex を 0 とする。

  2. interfaceIndexconfiguration.[[interfaces]] のサイズ未満である間:

    1. もし configuration.[[interfaces]][interfaceIndex].[[interfaceNumber]]interfaceNumber と等しければ、interfaceIndex を返す。

    2. interfaceIndex を1増やす。

  3. -1 を返す。

オルタネートインデックスを見つけるには、与えられた alternateSettinginterface オブジェクトに対して次の手順を実行します:
  1. alternateIndex を 0 とする。

  2. alternateIndexinterface.[[alternates]] のサイズ未満である間:

    1. もし interface.[[alternates]][alternateIndex].[[alternateSetting]]alternateSetting と等しければ、alternateIndex を返す。

    2. alternateIndex を1増やす。

  3. -1 を返す。

現在のコンフィギュレーションを見つけるには、与えられた device オブジェクトに対して次の手順を実行します:
  1. configurationdevice.[[configurations]] について:

    1. もし configuration.[[configurationValue]]device.[[configurationValue]] と等しければ、configuration を返す。

  2. null を返す。

エンドポイントを見つけるには、与えられた endpointAddressdevice オブジェクトに対して次の手順を実行します:
  1. configuration現在のコンフィギュレーションを見つける の結果とする。

  2. もし configurationnull であれば null を返す。

  3. interfaceconfiguration.[[interfaces]] について:

    1. もし インターフェイスがクレームされているかを調べる の結果が true でなければ、continue。

    2. alternate現在のオルタネート設定のためのオルタネートインターフェイスを見つける の結果とする。

    3. endpointalternate.[[endpoints]] について:

      1. もし endpoint.[[endpointAddress]]endpointAddress と等しければ、endpoint を返す。

  4. null を返す。

デバイスが構成済みかを確認するには、与えられた device に対して次の手順を実行します:
  1. もし device がもはやシステムに接続されていないなら、"NotFoundError"(DOMException)で reject された promise を返す。

  2. もし device.opened が true でなければ、"InvalidStateError"(DOMException)で reject された promise を返す。

  3. もし device.[[configurationValue]] が 0 と等しければ、"InvalidStateError"(DOMException)で reject された promise を返す。

  4. undefined を返す。

USB デバイスが接続され検出されたとき、新しい USBDevice オブジェクトが次の手順を実行して構築されます:
  1. this の [[configurationValue]]Get Configuration の戻り値に設定する。

  2. configurationDescriptors接続されたUSBデバイスのコンフィギュレーションディスクリプタ一覧を見つける の結果とする。

  3. configurationDescriptorconfigurationDescriptors について:

    1. configurationnewUSBConfiguration オブジェクトとして作成する(device を this に、configurationValueconfigurationDescriptorbConfigurationValue に設定して USBConfiguration(device,configurationValue) を呼ぶ)。

    2. 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 pipe を持たなければならず、それは endpointNumber 0 です。

6.1.1. 属性

usbVersionMajor, 型は octet、読み取り専用
usbVersionMinor, 型は octet、読み取り専用
usbVersionSubminor, 型は octet、読み取り専用

usbVersionMajor, usbVersionMinor および usbVersionSubminor 属性は、デバイスがサポートするUSBプロトコルのバージョンを宣言します。これらはデバイスディスクリプタの bcdUSB フィールドの値に対応し、値 0xJJMN はメジャー版が JJ、マイナー版が M、サブマイナー版が N となるものとします。

deviceClass, 型は octet、読み取り専用
deviceSubclass, 型は octet、読み取り専用
deviceProtocol, 型は octet、読み取り専用

deviceClass, deviceSubclass および deviceProtocol 属性は、デバイスがサポートする通信インターフェイスを示します。これらはそれぞれデバイスディスクリプタの bDeviceClassbDeviceSubClassbDeviceProtocol フィールドの値に対応しなければなりません。

vendorId, 型は unsigned short、読み取り専用
productId, 型は unsigned short、読み取り専用

vendorId および productId は、デバイスの vendor ID および product ID と一致している必要があります(MUST)。

deviceVersionMajor, 型は octet、読み取り専用
deviceVersionMinor, 型は octet、読み取り専用
deviceVersionSubminor, 型は octet、読み取り専用

deviceVersionMajor, deviceVersionMinor および deviceVersionSubminor 属性は、デバイスメーカーが定義するデバイスのリリース番号を示します。これはデバイスディスクリプタの bcdDevice フィールドの値に対応し、値 0xJJMN はメジャーが JJ、マイナーが M、サブマイナーが N となるものとします。

manufacturerName, 型は DOMString、読み取り専用、nullable
productName, 型は DOMString、読み取り専用、nullable
serialNumber, 型は DOMString、読み取り専用、nullable

manufacturerName, productName および serialNumber 属性は、該当する場合はデバイスディスクリプタの iManufactureriProductiSerialNumber フィールドで参照される string descriptors の値を含むべきです(SHOULD)。

configuration, 型は USBConfiguration、読み取り専用、nullable

configuration 属性は、デバイスで現在選択されている構成(configuration)を含み、その値は USBConfiguration のいずれかであり configurations に含まれていなければならない(SHALL)。

configuration の getter の手順は次の通りです:

  1. 現在のコンフィギュレーションを見つけるthis と共に実行し、その結果を返します。

configurations, 型は FrozenArray<USBConfiguration>、読み取り専用

configurations 属性は、デバイスがサポートするコンフィギュレーションを表す sequence を含みます。

configurations の getter の手順は次の通りです:

  1. このオブジェクトの内部スロット [[configurations]] を返します。

opened, 型は boolean、 読み取り専用

opened 属性は、現在の実行コンテキストによってデバイスが open されている場合 true に、そうでない場合は false に設定されるものとします(SHALL)。

6.1.2. メソッド

open() メソッドが呼び出された場合、次の手順を実行しなければなりません(MUST):
  1. globalthis の該当グローバルオブジェクトとします。

  2. もし this がもはやシステムに接続されていない場合、"NotFoundError"(DOMException)で reject された promise を返します。

  3. もし this.openedtrue であれば、undefined で解決された promise を返します。

  4. 次の手順を並列で実行します。

    1. デバイスとのセッションを開始するために必要なプラットフォーム固有の処理を行います。

    2. グローバルタスクをUSBタスクソース上にキューイングし、次の手順を実行します:

      1. 上記のプラットフォーム固有の処理が何らかの理由で失敗した場合、promise を "NetworkError"(DOMException)で reject し、処理を中止します。

      2. this.openedtrue に設定します。

      3. Resolve によって promiseundefined で解決します。

  5. promise を返します。

close() メソッドが呼び出された場合、次の手順を実行しなければなりません(MUST):
  1. globalthis の該当グローバルオブジェクトとします。

  2. もし this がもはやシステムに接続されていない場合、"NotFoundError"(DOMException)で reject された promise を返します。

  3. もし this.openedfalse であれば、undefined で解決された promise を返します。

  4. このデバイスに対して現在実行中の他のアルゴリズムをすべて中止し、それらに関連する promise を "AbortError"(DOMException)で reject します。

  5. 次の手順を並列で実行します。

    1. 各クレームされたインターフェイスについて releaseInterface(interfaceNumber) が呼ばれたかのように、クレームを解除するためのプラットフォーム固有の処理を行います。

    2. デバイスとのセッションを終了するためのプラットフォーム固有の処理を行います。

    3. グローバルタスクをUSBタスクソース上にキューイングし、次の手順を実行します:

      1. this.openedfalse に設定します。

      2. Resolve によって promiseundefined で解決します。

  6. promise を返します。

注: ECMAcript のコードが USBDevice のインスタンス device をもはや参照できない場合、UA は device.close() を実行することが推奨されます(SHOULD)。

forget() メソッドが呼び出された場合、次の手順を実行しなければなりません(MUST):
  1. devicethis とします。

  2. storage を現在のスクリプト実行環境の USBPermissionStorage オブジェクトとします。

  3. Remove device from storagestorage を使って実行します。

  4. undefined で解決された promise を返します。

注: ユーザーエージェントは複数API間の権限を統合する(例: WebHID と WebUSB のデバイスアクセスを統一された低レベルデバイスアクセス権で追跡する)ことを選択する場合があります。そのため、このメソッドは将来的に追加の(未指定の)権限も取り消す可能性があります。

selectConfiguration(configurationValue) メソッドが呼び出された場合、次の手順を実行しなければなりません(MUST):
  1. globalthis の該当グローバルオブジェクトとします。

  2. もし this がもはやシステムに接続されていない場合、"NotFoundError"(DOMException)で reject された promise を返します。

  3. selectedConfigurationnull に設定します。

  4. configurationthis.[[configurations]] について:

    1. もし configuration.[[configurationValue]]configurationValue と等しければ、selectedConfigurationconfiguration を設定してループを抜けます。

  5. もし selectedConfigurationnull のままであれば、"NotFoundError"(DOMException)で reject された promise を返します。

  6. もし this.openedtrue でない場合、"InvalidStateError"(DOMException)で reject された promise を返します。

  7. activeConfiguration現在のコンフィギュレーションを見つける を this で実行した結果とします。

  8. 次の手順を並列で実行します。

    1. もし activeConfigurationnull でない場合:

      1. interfaceactiveConfiguration.[[interfaces]] について、abort transfers currently scheduled on an interface を実行します。

    2. SET_CONFIGURATION コントロール転送を発行し、configurationValue を指定します。

    3. グローバルタスクをUSBタスクソース上にキューイングし、次の手順を実行します:

      1. 上記のコントロール転送が失敗した場合、promise を "NetworkError"(DOMException)で reject し、処理を中止します。

      2. numInterfacesselectedConfiguration.[[interfaces]] のサイズとします。

      3. this の [[selectedAlternateSetting]]numInterfaces にリサイズします。

      4. this の [[selectedAlternateSetting]] を 0 で埋めます。

      5. this の [[claimedInterface]]numInterfaces にリサイズします。

      6. this の [[claimedInterface]]false で埋めます。

      7. this の内部スロット [[configurationValue]]configurationValue に設定します。

      8. Resolve によって promiseundefined で解決します。

  9. promise を返します。

claimInterface(interfaceNumber) メソッドが呼び出された場合、次の手順を実行しなければなりません(MUST):
  1. globalthis の該当グローバルオブジェクトとします。

  2. もし check if the device is configured を this で実行して Promise が返される場合、その値を返します。

  3. activeConfiguration現在のコンフィギュレーションを見つける を this で実行した結果とします。

  4. interfacesactiveConfiguration.[[interfaces]] とします。

  5. interfaceIndexfind the interface indexinterfaceNumberactiveConfiguration で実行した結果とします。

  6. もし interfaceIndex-1 であれば、"NotFoundError"(DOMException)で reject された promise を返します。

  7. もし this.[[claimedInterface]][interfaceIndex] が true であれば、undefined で解決された promise を返します。

  8. unrestrictedfalse に設定します。

  9. documentglobal の関連ドキュメント(存在しなければ null)とします。

  10. もし documentnull でなく、かつ document がポリシー制御機能 "usb-unrestricted" の使用を許可されている場合、unrestrictedtrue にします。

  11. もし interfaces[interfaceIndex].[[isProtectedClass]]true であり、かつ unrestrictedfalse であれば、"SecurityError"(DOMException)で reject された promise を返します。

  12. promise を新しい Promise として作成します。

  13. 次の手順を並列で実行します。

    1. 現在の実行コンテキストのために interfaces[interfaceIndex] に対する排他制御を要求するためのプラットフォーム固有の処理を行います。

    2. グローバルタスクをUSBタスクソース上にキューイングし、次の手順を実行します:

      1. 上記のプラットフォーム固有の処理が失敗した場合、promise を "NetworkError"(DOMException)で reject し、処理を中止します。

      2. this.[[claimedInterface]][interfaceIndex] を true に設定します。

      3. Resolve によって promiseundefined で解決します。

  14. promise を返します。

releaseInterface(interfaceNumber) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. activeConfiguration を、現在の構成を見つけるthis に対して行った結果とする。

  4. interfacesactiveConfiguration.[[interfaces]] とする。

  5. interfaceIndex を、インターフェイスのインデックスを見つけるinterfaceNumberactiveConfiguration を用いて行った結果とする。

  6. もし interfaceIndex-1 と等しければ、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  7. もし this.[[claimedInterface]][interfaceIndex] が false なら、 次の値で解決される promise を返す: undefined

  8. promise新たな promise とする。

  9. 次の手順を 並列に実行する。

    1. interfaces[interfaceIndex] に対する排他的制御を放棄するために必要なプラットフォーム固有の手順を実行する。

    2. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. this.[[selectedAlternateSetting]][interfaceIndex] を 0 に設定する。

      2. this.[[claimedInterface]][interfaceIndex] を false に設定する。

      3. 解決する: promiseundefined で。

  10. promise を返す。

selectAlternateInterface(interfaceNumber, alternateSetting) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. activeConfiguration を、現在の構成を見つけるthis に対して行った結果とする。

  4. interfacesactiveConfiguration.[[interfaces]] とする。

  5. interfaceIndex を、インターフェイスのインデックスを見つけるinterfaceNumberactiveConfiguration で行った結果とする。

  6. もし interfaceIndex-1 と等しければ、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  7. もし this.[[claimedInterface]][interfaceIndex] が false なら、 次の理由で拒否される promise を返す: "InvalidStateError" DOMException

  8. interfaceinterfaces[interfaceIndex] とする。

  9. alternateIndex を、代替設定のインデックスを見つけるalternateSettinginterface で行った結果とする。

  10. もし alternateIndex-1 と等しければ、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  11. promise新たな promise とする。

  12. 次の手順を 並列に実行する。

    1. インターフェイス上で現在スケジュールされている転送を中止するinterface に対して実行する。

    2. SET_INTERFACE制御転送 を発行し、 interfaceNumberinterfaceNumber に、 alternateSettingalternateSetting に設定する。

    3. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. 上記の 制御転送 が失敗した場合、拒否する: promise を "NetworkError" DOMException で、これ以降の手順を中止する。

      2. this.[[selectedAlternateSetting]][interfaceIndex] を alternateSetting に設定する。

      3. 解決する: promiseundefined で。

  13. promise を返す。

controlTransferIn(setup, length) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. 制御転送パラメーターの妥当性を確認するthissetup に対して行い、Promise を返すなら、その値を返す。

  4. promise新たな promise とする。

  5. 次の手順を 並列に実行する。

    1. もし length が 0 より大きいなら、bufferlength バイトの領域を持つホストバッファーとする。

    2. 制御転送this に対して発行し、セットアップパケットのパラメーターには setup で与えられたものを用い、bmRequestType のデータ転送方向を "device to host" に、wLengthlength に設定する。 もし定義されていれば、この転送に対する応答として受信したデータを書き込む宛先として buffer も与える。

    3. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. bytesTransferred を、buffer に書き込まれたバイト数とする。

      2. result新たな USBInTransferResult とする。

      3. もしデバイスからデータを受信していれば、新たな ArrayBuffer を作成し、buffer の先頭 bytesTransferred バイトを含め、 result.data をそれの上に構築した 新たな DataView に設定する。

      4. result.status を次のように設定する。

      5. その他の理由で転送が失敗した場合、拒否する: promise を "NetworkError" で、 これ以降の手順を中止する。

      6. 解決する: promiseresult で。

  6. promise を返す。

controlTransferOut(setup, data) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. 制御転送パラメーターの妥当性を確認するthissetup に対して行い、Promise を返すなら、その値を返す。

  4. バッファソースのコピーを取得し、data から結果を bytes とする。

  5. promise新たな promise とする。

  6. 次の手順を 並列に実行する。

    1. 制御転送this に対して発行し、セットアップパケットのパラメーターには setup を用い、bmRequestType のデータ転送方向を "host to device" に、wLength長さ (bytes の) に設定する。

    2. 転送のデータステージbytes を送信する。

    3. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. result新たな USBOutTransferResult とする。

      2. result.status を次のように設定する。

      3. その他の理由で転送が失敗した場合、拒否する: promise を "NetworkError" DOMException で、これ以降の手順を中止する。

      4. 解決する: promiseresult で。

  7. promise を返す。

clearHalt(direction, endpointNumber) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. endpointAddress を、もし direction"in" と等しければ endpointNumber | 0x80、それ以外は endpointNumber とする。

  4. endpoint を、エンドポイントを見つけるendpointAddressthis で行った結果とする。

  5. もし endpointnull なら、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  6. promise新たな promise とする。

  7. 次の手順を 並列に実行する。

    1. ClearFeature(ENDPOINT_HALT)制御転送 をデバイスに対して発行し、 endpoint のハルト状態を解除する。

    2. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. 上記の 制御転送 が失敗した場合、拒否する: promise を "NetworkError" DOMException で、これ以降の手順を中止する。

      2. 解決する: promiseundefined で。

  8. promise を返す。

transferIn(endpointNumber, length) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. endpointAddressendpointNumber | 0x80(すなわち "in" 方向)とする。

  4. endpoint を、エンドポイントを見つけるendpointAddressthis で行った結果とする。

  5. もし endpointnull なら、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  6. もし endpoint.type"bulk" または "interrupt" と等しくないなら、 次の理由で拒否される promise を返す: "InvalidAccessError" DOMException

  7. promise新たな promise とする。

  8. 次の手順を 並列に実行する。

    1. bufferlength バイトの領域を持つホストバッファーとする。

    2. endpoint に適した方法で、バルク または インタラプトIN 転送endpoint に対して実行し、デバイスから length バイトのデータを受信して buffer に書き込む。

    3. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. bytesTransferred を、buffer に書き込まれたバイト数とする。

      2. result新たな USBInTransferResult とする。

      3. もしデバイスからデータが受信されていれば、新たな ArrayBuffer を作成し、buffer の先頭 bytesTransferred バイトを含め、 result.data をそれの上に構築した 新たな DataView に設定する。

      4. result.status を次のように設定する。

        • "stall": デバイスが endpoint をストールで応答した場合。

        • "babble": デバイスが length バイトを超えるデータで応答した場合。

        • "ok": 上記以外の場合。

      5. その他の理由で転送が失敗した場合、拒否する: promise を "NetworkError" DOMException で、これ以降の手順を中止する。

      6. 解決する: promiseresult で。

  9. promise を返す。

transferOut(endpointNumber, data) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. endpointAddressendpointNumber(すなわち "out" 方向)とする。

  4. endpoint を、エンドポイントを見つけるendpointAddressthis で行った結果とする。

  5. もし endpointnull なら、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  6. もし endpoint.type"bulk" または "interrupt" と等しくないなら、 次の理由で拒否される promise を返す: "InvalidAccessError" DOMException

  7. バッファソースのコピーを取得し、data から結果を bytes とする。

  8. promise新たな promise とする。

  9. 次の手順を 並列に実行する。

    1. endpoint に適した方法で、バルク または インタラプトOUT 転送endpoint に対して実行し、bytes をデバイスに送信する。

    2. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. result新たな USBOutTransferResult とする。

      2. result.bytesWritten を、デバイスに正常に送信されたデータ量に設定する。

      3. result.status を次のように設定する。

        • "stall": デバイスが endpoint をストールで応答した場合。

        • "ok": 上記以外の場合。

      4. その他の理由で転送が失敗した場合、拒否する: promise を "NetworkError" DOMException で、これ以降の手順を中止する。

      5. 解決する: promiseresult で。

  10. promise を返す。

isochronousTransferIn(endpointNumber, packetLengths) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. endpointAddressendpointNumber | 0x80(すなわち "in" 方向)とする。

  4. endpoint を、エンドポイントを見つけるendpointAddressthis で行った結果とする。

  5. もし endpointnull なら、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  6. もし endpoint.type"isochronous" と等しくないなら、 次の理由で拒否される promise を返す: "InvalidAccessError" DOMException

  7. promise新たな promise とする。

  8. 次の手順を 並列に実行する。

    1. totalLengthpacketLengths の要素の合計とする。

    2. buffertotalLength バイトの領域を持つホストバッファーとする。

    3. アイソクロナス IN 転送endpoint に対して実行し、packetLengths に従ってデバイスからパケットを buffer に読み込む。

    4. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. result新たな USBIsochronousInTransferResult とする。

      2. data新たな ArrayBuffer とし、buffer で初期化する。

      3. result.data を、新たな DataViewbuffer 上に構築)に設定する。

      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. その他の理由で転送が失敗した場合、拒否する: promise を "NetworkError" DOMException で、これ以降の手順を 中止する。

        5. result.packets[i] を packet に設定する。

      5. 解決する: promiseresult で。

  9. promise を返す。

isochronousTransferOut(endpointNumber, data, packetLengths) メソッドは、呼び出されたとき、次の手順を実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. endpointAddressendpointNumber(すなわち "out" 方向)とする。

  4. endpoint を、エンドポイントを見つけるendpointAddressthis で行った結果とする。

  5. もし endpointnull なら、次の理由で拒否される promise を返す: "NotFoundError" DOMException

  6. もし endpoint.type"isochronous" と等しくないなら、 次の理由で拒否される promise を返す: "InvalidAccessError" DOMException

  7. バッファソースのコピーを取得し、data から結果を bytes とする。

  8. promise新たな promise とする。

  9. 次の手順を 並列に実行する。

    1. アイソクロナス OUT 転送endpoint に対して実行し、bytespacketLengths.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. その他の理由で転送が失敗した場合、拒否する: promise を "NetworkError" DOMException で、これ以降の手順を 中止する。

        5. result.packets[i] を packet に設定する。

      3. 解決する: promiseresult で。

  10. promise を返す。

reset() メソッドは、呼び出されたとき、次の手順を 実行しなければならない。
  1. global を、this関連する大域オブジェクトとする。

  2. デバイスが構成済みか確認するthis に対して行い、Promise を返すなら、その値を返す。

  3. promise新たな promise とする。

  4. 次の手順を 並列に実行する。

    1. デバイス上のすべての操作を中止し、大域タスクをキューに入れUSB タスクソース 上で、それらに関連付けられた promise を "AbortError" DOMException拒否する

    2. デバイスをソフトリセットするために必要なプラットフォーム固有の操作を実行する。

    3. 大域タスクをキューに入れUSB タスクソース 上で global を与えて 次の手順を実行する:

      1. ソフトリセットが失敗した場合、拒否する: promise を "NetworkError" で、これ以降の手順を中止する。

      2. 解決する: 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;
};
control transfer のパラメーターの妥当性を確認するために、 与えられた USBDevice deviceUSBControlTransferParameters setup について、 次の手順を実行する:
  1. configuration を、現在の構成を見つけるdevice に対して行った結果とする。

  2. もし configurationnull なら、undefined を返す。

  3. もし setup.recipient"interface" なら、 次の手順を実行する:

    1. interfaceNumber を、setup.index の下位 8 ビットとする。

    2. interfaceIndex を、インターフェイスのインデックスを見つけるinterfaceNumberconfiguration で行った結果とする。

    3. もし interfaceIndex-1 と等しければ、次の理由で拒否される promise を返す: "NotFoundError" DOMException

    4. interfaceconfiguration.[[interfaces]][interfaceIndex] とする。

    5. インターフェイスがクレームされているかを判定するinterface に対して行った結果が true でないなら、次の理由で拒否される promise を返す: "InvalidStateError" DOMException

  4. もし setup.recipient"endpoint" なら、 次の手順を実行する:

    1. endpointAddresssetup.index とする。

    2. endpoint を、エンドポイントを見つけるendpointAddressdevice で行った結果とする。

    3. もし endpointnull なら、次の理由で拒否される promise を返す: "NotFoundError" DOMException

    4. alternateendpoint.[[alternateInterface]] とする。

    5. interfacealternate.[[interface]] とする。

    6. インターフェイスがクレームされているかを判定するinterface に対して行った結果が false なら、次の理由で拒否される promise を返す: "InvalidStateError" DOMException

  5. undefined を返す。

6.2.1. メンバー

requestType, USBRequestType

requestType 属性は、セットアップパケットbmRequestType フィールドの一部を設定し、 このリクエストが USB 標準、特定の USB デバイスクラス仕様、またはベンダー固有プロトコルのいずれに属するかを示す。

recipient, USBRecipient

recipient 属性は、セットアップパケットbmRequestType フィールドの一部を設定し、 control transfer の宛先がデバイス全体か、特定のインターフェイスまたはエンドポイントかを示す。

request, octet

request 属性は、セットアップパケットbRequest フィールドを設定する。有効なリクエストは USB 標準、USB デバイスクラス仕様、またはデバイスベンダーによって定義される。

value, unsigned short

value および index 属性は、それぞれ セットアップパケットwValuewIndex フィールドを設定する。これらのフィールドの意味は行われるリクエストによって異なる。

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]] sequenceUSBInterface)の空配列 この構成でサポートされるすべてのインターフェイス。
[[configurationValue]] <常に本文で設定> この構成の構成値。
与えられた configurationValue構成のためのディスクリプタの一覧を見つけるには、 次の手順を実行する:
  1. deviceDescriptor を、接続された USB デバイスのデバイスディスクリプタを見つける の結果とする。

  2. numConfigurationsdeviceDescriptorbNumConfigurations とする。

  3. configurationIndex を 0 とする。

  4. configurationIndexnumConfigurations より小さいあいだ:

    1. descriptors を、ディスクリプタ のリストで、Get DescriptorDescriptorTypeCONFIGURATION を、 DescriptorIndexconfigurationIndex を設定して実行した結果とする。

    2. もし descriptors[0] の bDescriptorTypeCONFIGURATION と等しく、かつ descriptorsbConfigurationValueconfigurationValue と等しければ、 descriptors を返す。

    3. configurationIndex を 1 増やす。

  5. empty の結果を返す。

6.3.1. コンストラクター

USBConfiguration(device, configurationValue) コンストラクターは、呼び出されたとき、次の手順を実行しなければならない:
  1. this.[[device]]device に設定する。

  2. this.[[configurationValue]]configurationValue に設定する。

  3. descriptors を、構成のためのディスクリプタの一覧を見つけるconfigurationValueconfigurationValue を設定して実行した結果とする。

  4. seen を空の 順序付き集合 とする。

  5. descriptor について(descriptors の要素):

    1. もし descriptorbDescriptorTypeINTERFACE と等しくなければ、続行する。

    2. もし descriptorbInterfaceNumberseen に含まれていれば、 続行する。

    3. interface を、新たな USBInterface オブジェクトとして、 USBInterface(configuration,interfaceNumber) を用いて生成する。ここで configurationthisinterfaceNumberdescriptorbInterfaceNumber とする。

    4. 追加する: interfacethis.[[interfaces]] に。

    5. 追加する: descriptorbInterfaceNumberseen に。

6.3.2. 属性

configurationValue, octet、読み取り専用

各デバイス構成は、一意の configurationValue を持たなければならず、これはそれを定義する 構成ディスクリプタbConfigurationValue フィールドと一致しなければならない。

configurationName, DOMString、読み取り専用、nullable

configurationName 属性は、定義されている場合、構成ディスクリプタiConfiguration フィールドが参照する 文字列ディスクリプタ の値を含むことが望ましい(SHOULD)。

interfaces, 型 FrozenArray<USBInterface>、読み取り専用

interfaces 属性には、このデバイス構成によって公開されるインターフェイスの一覧が含まれていなければならない(SHALL)。

interfaces の getter 手順は次のとおり:

  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]] sequenceUSBAlternateInterface)の空配列 このインターフェイスでサポートされるすべての代替設定。
[[isProtectedClass]] false このインターフェイスに保護対象のクラスに属する代替設定があるかどうか。

インターフェイスディスクリプタ interface は、かつその場合に限り、 interfacebInterfaceClass が次のいずれかの値と等しいとき、 保護対象のインターフェイスクラスを持つ

保護対象のインターフェイスクラス
コード 説明
0x01 オーディオ
0x03 HID(ヒューマンインターフェイスデバイス)
0x08 マスストレージ
0x0B スマートカード
0x0E ビデオ
0x10 オーディオ/ビデオデバイス
0xE0 ワイヤレスコントローラー

Note: 本仕様は、ユーザーを悪意あるコンテンツから保護するために機微なデバイスへのアクセスを制限することと、 可能な限り多くのデバイスをサポートすることのバランスを取ることを目指している。序文で述べたとおり、 この API の目標は他のより高水準の API でカバーされていないデバイスをサポートすることにある。上記のリストには、 そのような高水準 API が存在し、本 API による低水準アクセスよりもユーザーのプライバシーとセキュリティをより強く保護する インターフェイスクラスが含まれている。

与えられた interface について、インターフェイスがクレームされているかを判定するには、 次の手順を実行する:
  1. configurationinterface.[[configuration]] とする。

  2. deviceconfiguration.[[device]] とする。

  3. もし configuration が、device に対する 現在の構成を見つける の結果と同一でなければ、 false を返す。

  4. interfaceIndex を、インターフェイスのインデックスを見つけるinterface.[[interfaceNumber]]configuration で行った結果とする。

  5. 表明: interfaceIndex-1 と等しくない。

  6. device.[[claimedInterface]][interfaceIndex] を返す。

与えられた interface について、現在の代替設定に対応する代替インターフェイスを見つけるには、 次の手順を実行する:
  1. configurationinterface.[[configuration]] とする。

  2. deviceconfiguration.[[device]] とする。

  3. alternateIndex を 0 とする。

  4. インターフェイスがクレームされているかを判定するinterface に対して行った結果が true の場合:

    1. interfaceIndex を、インターフェイスのインデックスを見つけるinterface.[[interfaceNumber]]configuration で行った結果とする。

    2. 表明: interfaceIndex-1 と等しくない。

    3. alternateIndex を、代替インデックスを見つけるdevice.[[selectedAlternateSetting]][interfaceIndex] と interface で行った結果に設定する。

  5. 表明: alternateIndex-1 と等しくない。

  6. interface.alternates[alternateIndex] を返す。

Note: インターフェイスディスクリプタ [USB31] によれば、インターフェイスには少なくとも 1 つの代替設定が存在する。 つまり、インターフェイスディスクリプタ には 少なくとも 1 つの代替設定がなければならない。

6.4.1. コンストラクター

USBInterface(configuration, interfaceNumber) コンストラクターは、呼び出されたとき、次の手順を実行しなければならない:
  1. this.[[configuration]]configuration に設定する。

  2. this.[[interfaceNumber]]interfaceNumber に設定する。

  3. descriptors を、構成のためのディスクリプタの一覧を見つけるconfigurationValueconfiguration.[[configurationValue]] を設定して実行した結果とする。

  4. descriptor について(descriptors の要素):

    1. もし descriptorbDescriptorTypeINTERFACE と等しくなければ、続行する。

    2. もし descriptorbInterfaceNumberinterfaceNumber と等しくなければ、続行する。

    3. もし descriptor保護対象のインターフェイスクラスを持つ なら、 this.[[isProtectedClass]]true に設定する。

    4. alternate を、新たな USBAlternateInterface オブジェクトとして、 USBAlternateInterface(deviceInterface,alternateSetting) を用いて生成する。ここで deviceInterfacethisalternateSettingdescriptorbAlternateSetting とする。

    5. 追加する: alternatethis.[[alternates]] に。

6.4.2. 属性

interfaceNumber, octet、読み取り専用

各インターフェイスは、alternates の集合を提供し、これらはそれぞれの インターフェイスディスクリプタ にある 単一の bInterfaceNumber フィールドで識別される。interfaceNumber 属性はこのフィールドと一致しなければならない。

interfaceNumber の getter 手順は次のとおり:

  1. this.[[interfaceNumber]] を返す。

alternate, USBAlternateInterface、読み取り専用

alternate 属性は、このインターフェイスで現在選択されている USBAlternateInterface に設定されなければならない。既定では、bAlternateSetting0 のものとする。

alternate の getter 手順は次のとおり:

  1. 現在の代替設定に対応する代替インターフェイスを見つけるthis に対して行った結果を返す。

alternates, 型 FrozenArray<USBAlternateInterface>、読み取り専用

alternates メソッドは、USBAlternateInterface オブジェクトの集合を提供し、これらはそれぞれの インターフェイスディスクリプタ にある 単一の bInterfaceNumber フィールドで識別される。

alternates の getter 手順は次のとおり:

  1. this.[[alternates]] を返す。

claimed, boolean、読み取り専用

claimed 属性は、当該インターフェイスが現在の実行コンテキストによりクレームされているとき true に、 それ以外の場合は false に設定されなければならない。

claimed の getter 手順は次のとおり:

  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]] sequenceUSBEndpoint)の空配列 このインターフェイスによって公開されるすべてのエンドポイント。
[[alternateSetting]] <常に本文で設定> このインターフェイスの代替設定。
与えられた alternateSettinginterfaceNumber、および configurationValue について、エンドポイントディスクリプタの一覧を見つけるには、 次の手順を実行する:
  1. descriptors を、構成のためのディスクリプタの一覧を見つけるconfigurationValue で行った結果とする。

  2. endpointDescriptors を空の list とする。

  3. index を 0 とする。

  4. indexサイズdescriptors の)より小さいあいだ:

    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. 追加する: descriptors[indexEndpoints + offset] を endpointDescriptors に。

      2. indexEndpoints を 1 増やす。

    10. endpointDescriptors を返す。

6.5.1. コンストラクター

USBAlternateInterface(deviceInterface, alternateSetting) コンストラクターは、呼び出されたとき、次の手順を実行しなければならない:
  1. this.[[interface]]deviceInterface に設定する。

  2. this.[[alternateSetting]]alternateSetting に設定する。

  3. descriptors を、エンドポイントディスクリプタの一覧を見つけるinterfaceNumberdeviceInterface.interfaceNumberalternateSettingalternateSettingconfigurationValuedeviceInterface.[[configuration]].[[configurationValue]] を設定して実行した結果とする。

  4. descriptor について(descriptors の要素):

    1. もし descriptorbmAttributes が コントロール転送タイプ(すなわち エンドポイントディスクリプタ に従いビット 0..100)を示すなら、続行する。

    2. endpointAddressdescriptorbEndpointAddress とする。

    3. dir を、もし endpointAddress & 0x800 なら "out"、 それ以外は "in" とする。

    4. endpoint を、新たな USBEndpoint オブジェクトとして、 USBEndpoint(alternate,endpointNumber,direction) を用いて生成する。ここで alternatethisendpointNumberendpointAddress & 0xFdirectiondir とする。

    5. 追加する: endpointthis.[[endpoints]] に。

6.5.2. 属性

alternateSetting, octet、読み取り専用

各代替インターフェイス構成は、与えられたインターフェイス内で一意の alternateSetting を持たなければならず、これはそれを定義する インターフェイスディスクリプタbAlternateSetting フィールドと一致しなければならない。

interfaceClass, octet、読み取り専用
interfaceSubclass, octet、読み取り専用
interfaceProtocol, octet、読み取り専用

interfaceClassinterfaceSubclass、 および interfaceProtocol 属性は、当該インターフェイスがサポートする通信インターフェイスを宣言する。これらはそれぞれ、 インターフェイスディスクリプタbInterfaceClassbInterfaceSubClassbInterfaceProtocol の各フィールドの値と一致しなければならない。

interfaceName, DOMString、読み取り専用、nullable

interfaceName 属性は、定義されている場合、インターフェイスディスクリプタiInterface フィールドで インデックス付けされる 文字列ディスクリプタ の値を含むことが望ましい(SHOULD)。

endpoints, 型 FrozenArray<USBEndpoint>、読み取り専用

endpoints 属性には、このインターフェイスによって公開されるエンドポイントの一覧が含まれていなければならない(SHALL)。これらのエンドポイントは、 当該 インターフェイスディスクリプタ に含まれる エンドポイントディスクリプタ から構築されなければならず、 この配列の要素数は インターフェイスディスクリプタbNumEndpoints の値と一致しなければならない。

endpoints の getter 手順は次のとおり:

  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. endpointDescriptors を、alternateSettingalternateInterface.alternateSetting を、 interfaceNumberinterface.interfaceNumber を、 configurationValueconfiguration.configurationValue を設定して エンドポイントディスクリプタの一覧を見つける を実行した結果とする。

  5. endpointDescriptorendpointDescriptors の要素)について:

    1. もし endpoint.[[endpointAddress]]endpointDescriptorbEndpointAddress と等しければ、 endpointDescriptor を返す。

6.6.1. コンストラクター

endpointNumber(alternate, endpointNumber, direction) コンストラクターは、呼び出されたとき、次の手順を実行しなければならない:
  1. this.[[alternateInterface]]alternate に設定する。

  2. endpointAddress を次のように設定する。もし direction"in" なら endpointNumber | 0x80、 それ以外の場合は endpointNumber

  3. this.[[endpointAddress]]endpointAddress に設定する。

6.6.2. 属性

endpointNumber, octet、読み取り専用
direction, USBDirection、読み取り専用

特定のデバイス構成内の各エンドポイントは、endpointNumberdirection の組み合わせが一意でなければならない(SHALL)。endpointNumber は、当該エンドポイントを定義する エンドポイントディスクリプタbEndpointAddress フィールドの 下位 4 ビットと等しくなければならない(MUST)。

direction 属性は、このエンドポイントがサポートする転送方向を表し、bEndpointAddress の最上位ビットが セットされている場合は "in"、 それ以外は "out" に等しい。 エンドポイントは、デバイスからホストへの IN またはホストからデバイスへの OUT のいずれかのデータを運ぶ。

type, USBEndpointType、読み取り専用

type 属性は、このエンドポイントがサポートするデータ転送の種類を表す。

type の getter 手順は次のとおり:

  1. endpointDescriptor を、エンドポイントディスクリプタを見つけるthis に対して実行した結果とする。

  2. attrendpointDescriptorbmAttributes とする。

  3. typeBitsattr & 0x3 とする。

  4. もし typeBitsb01 と等しければ、"isochronous" を返す。

  5. もし typeBitsb10 と等しければ、"bulk" を返す。

  6. もし typeBitsb11 と等しければ、"interrupt" を返す。

Note: USBAlternateInterface(deviceInterface,alternateSetting) の手順に従うと、コントロール転送タイプに属するエンドポイントオブジェクトは存在しないはずである。

packetSize, unsigned long、読み取り専用

packetSize 属性は、このエンドポイントで用いられるパケットサイズを表し、当該エンドポイントを定義する エンドポイントディスクリプタwMaxPacketSize の値と等しくなければならない(MUST)。High-Speed の High-Bandwidth エンドポイントでは、マイクロフレームあたり複数のトランザクションを発行することによる乗数が含まれる。SuperSpeed デバイスでは、 SuperSpeed Endpoint Companion ディスクリプタの bMaxBurst フィールドによる乗数が含まれる。

packetSize の getter 手順は次のとおり:

  1. endpointDescriptor を、エンドポイントディスクリプタを見つけるthis に対して実行した結果とする。

  2. endpointDescriptorwMaxPacketSize を返す。

7. USB ブロックリスト

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

この仕様は、本リポジトリの blocklist.txt ファイルに依存して、 ウェブサイトがアクセスできるデバイスの集合を制限する。

URL url にあるブロックリストを 解析する 結果は、次のアルゴリズムで生成される listUSBBlocklistEntry オブジェクトである:

  1. url を取得し、その本文を UTF-8 としてデコードしたものを contents とする。

  2. lines を、厳密分割 により contents の先頭からコードポイント '\n' で分割した結果とする。

  3. blocklist を空の list とする。

  4. linelines の要素)について:

    1. line を、先頭から '#' に等しくないコードポイントの列を 収集 した結果に置き換える。

    2. line を、前後の ASCII 空白の除去 結果に置き換える。

    3. components を、厳密分割 により line の先頭から コードポイント ':' で分割した結果とする。

    4. もし sizecomponents)が 2 または 3 でなければ、 continue

    5. idVendor を、components[0] を 16 進数として解釈した結果とする。

    6. idProduct を、components[1] を 16 進数として解釈した結果とする。

    7. bcdDevice0xFFFF とする。

    8. もし sizecomponents)が 3 なら、 bcdDevicecomponents[2] を 16 進数として解釈した結果に設定する。

    9. append する: idVendor, idProduct, bcdDevice を持つ新しい USBBlocklistEntryblocklist に。

  5. blocklist を返す。

USB ブロックリスト は、 parsing the blocklisthttps://raw.githubusercontent.com/WICG/webusb/main/blocklist.txt で行った結果である。UA はこのブロックリストを定期的に再取得すべきだが、その頻度は未規定である。

USBDevice device は、次の手順が "blocked" を返す場合、ブロック対象 である(Document document に対して):

  1. もし documentnull ではなく、かつ documentallowed to use として名付けられた policy-controlled feature"usb-unrestricted" を許可されているなら、 "not blocked" を返す。

  2. entryUSB ブロックリスト の要素)について:

    1. もし device.vendorIdentry.idVendor と等しくなければ、 continue

    2. もし device.productIdentry.idProduct と等しくなければ、 continue

    3. bcdDevicedevice.deviceVersionMajor << 8 + device.deviceVersionMinor << 4 + device.deviceVersionSubminor とする。

    4. もし bcdDeviceentry.bcdDevice 以下なら、 "blocked" を返す。

  3. "not blocked" を返す。

8. 統合

8.1. Permissions Policy

この仕様は、トークン "usb" により識別される policy-controlled feature を定義する。 これは、usb 属性を Navigator オブジェクト上に公開するかどうかを制御する。

この機能の 既定の許可リスト["self"] である。

この仕様は 2 つ目の policy-controlled feature を定義する。これはトークン "usb-unrestricted" により識別され、 ブロックリストに載っている USB デバイスおよび保護対象クラスのデバイスインターフェイスへのアクセス可否を制御する。 この機能は、Web Application Manifest で当該機能を宣言した Isolated Web Apps に対してのみ有効化されなければならない(MUST)。 [APPMANIFEST]

この機能の 既定の許可リスト["self"] である。 通常、これは トップレベルの traversable にある Document では既定で許可されることを意味する。しかし、この機能はマニフェストで宣言された Isolated Web Apps に対してのみ有効化されるという要件により、 実質的な既定の許可リストは ["none"] となる。

8.2. Permission 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 のインスタンスは、USB デバイスの配列を保持する 内部スロット [[devices]] を持つ。

permission result type
[Exposed=(Worker,Window)]
interface USBPermissionResult : PermissionStatus {
  attribute FrozenArray<USBDevice> devices;
};
permission query algorithm
"usb" 権限を照会する ために、USBPermissionDescriptor descUSBPermissionStorage storage、および USBPermissionResult status を用いて、UA は次を行う:
  1. もし desc.filters が設定されているなら、desc.filters 内の各 filter について、 それが 有効なフィルターでない 場合は TypeError を送出し、 これ以降の手順を中止する。

  2. もし desc.exclusionFilters が設定されているなら、desc.exclusionFilters 内の各 exclusionFilter について、 それが 有効なフィルターでない 場合は TypeError を送出し、 これ以降の手順を中止する。

  3. status.state"prompt" に設定する。

  4. matchingDevices を新しい Array とする。

  5. storage.allowedDevices 内の各 allowedDevice について、 さらに allowedDevice@[[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" 権限を要求する

9. 用語

この仕様では、[USB31] から採用した複数の用語を使用する。 参照は Universal Serial Bus のバージョン 3.1 に対して行っているが、これらの概念の多くは 以前のバージョンにも存在する。本仕様に関係する USB バージョン間の重要な差異は明示的に言及する。

Descriptors は、 デバイスから読み取ることができ、その特性および機能を記述するバイナリデータ構造である:

Binary Object StoreBOS)は、標準のデバイス ディスクリプタよりも自由形式の追加ディスクリプタ集合である。特筆すべきは Platform Descriptor であり、 第三者(本仕様のような)が独自のディスクリプタ型を宣言することを可能にする。 これらは UUID によって識別される。Binary Object Store は [USB31] の 9.6.2 節で説明されている。

USB device は単一の device descriptor を持ち、 そこから 1 つ以上の configuration descriptor へとリンクする。 vendor ID は USB-IF によってデバイス製造者へ割り当てられ、device descriptoridVendor フィールドに保存される。 product ID は製造者によって割り当てられ、 device descriptoridProduct フィールドに保存される。 serial number は任意のプロパティであり、 device descriptoriSerialNumber フィールドが 0 でない場合に定義され、そのインデックスが参照する string descriptor である。

Get Configuration は、 現在のデバイス構成値を取得する要求であり、[USB31] の 9.4.2 節に記載されている。

Set Descriptor は、 指定した Descriptor Type と Descriptor Index のディスクリプタを取得する要求であり、 [USB31] の 9.4.8 節に記載されている。

Get Descriptor は、 指定した Descriptor Type と Descriptor Index のディスクリプタを取得する要求であり、 [USB31] の 9.4.3 節に記載されている。

Note: デバイスの descriptors はほとんどの場合変化しない(Set Descriptor によって descriptors が変更される場合を除く)。 デバイスへの冗長な Get Descriptor トラフィックを削減するために、 実装は descriptors を保存する リードスルー/ライトスルーのキャッシュ層を持つことができる。

control transfer は、 もっとも一般的にデバイスの構成に使用される USB トラフィックの特別なクラスである。 これは 3 つの段階、すなわち setupdatastatus からなる。setup stage では、要求パラメータ(転送方向や後続データのサイズを含む)を含む setup packet がデバイスに送られる。 data stage では、そのデータが デバイスへ送信されるか、デバイスから受信される。status stage では、要求の成功が確認されるか、失敗が通知される。

10. 付録: USB の簡単な紹介

この節は規範ではない。

USB はネットワークであるが、従来の TCP/IP ネットワークとは大きく異なる。実際には RPC システムに近い。 すべてのトラフィックはホスト(あなたのコンピュータ)によって制御される。スマートフォンのように USB ホストと USB クライアントの両方として動作できるデバイスもあるが、一度に担える役割は一方のみである。

10.1. ディスクリプタ

USB デバイスは、descriptors と呼ばれる バイナリ構造の集合をホストに提供することで自らを識別する。ホストが最初に読むのは device descriptor であり、USB-IF によって割り当てられた ベンダーおよびプロダクト ID、製造者などの基本情報を含む。その後、ホストはデバイスの configuration descriptor を読み取る。 これは、公開されるインターフェイスやエンドポイントを含むデバイスの機能を記述する。 クラスはデバイスレベルまたは個々のインターフェイスで宣言できる。複数のインターフェイスで 異なる機能を提供するデバイスは 複合デバイス(composite device) と呼ばれる。

10.2. 転送

データがホストからデバイスへ、またはその逆方向へ移動する場合でも、転送は常にホストによって開始される。 OUT transfers はホストからデバイスへのデータを運び、 デバイスが受信を確認するまで待機することがある。IN transfers はデバイスからホストへのデータを運び、 デバイスが送信すべきデータを用意するまで待機しなければならないことがある。転送はデバイスのいずれかの エンドポイントに対して実行され、送信するトラフィックの種類に応じて異なるタイプがある。

適合性

文書の規約

適合性要件は、記述的な断言と RFC 2119 の用語の組み合わせで表現される。 規範的な箇所における “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL” というキーワードは RFC 2119 に従って解釈される。 ただし、可読性のため、本仕様ではこれらの語をすべて大文字で記すとは限らない。

明示的に非規範と示された節、例、および注記を除き、本仕様の本文はすべて規範的である。 [RFC2119]

本仕様の例は “for example” という語で導入されるか、 または class="example" を用いて規範文から区別される。例えば次のようになる:

これは情報提供的な例である。

情報提供的な注記は “Note” で始まり、class="note" を用いて規範文から区別される。 例えば次のようになる:

Note, これは情報提供的な注記である。

索引

この仕様で定義される用語

参照で定義される用語

参考文献

規範的な参考文献

[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;
};

課題一覧

デバイスがリセットされた後、どの構成になりますか? [課題 #36]
デバイス構成に関する非規範的な情報を含めてください。[課題 #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?