次の方法で共有


ファンクション コントローラー クライアント ドライバーの作成

この記事では、USB ファンクション コントローラー拡張機能 (UFX) との対話中にファンクション コントローラー クライアント ドライバーが実行するさまざまなタスクについて説明します。

重要な API

USB ファンクション コントローラー拡張機能 (UFX) との対話中にファンクション コントローラー クライアント ドライバーが実行するさまざまなタスクについて説明します。 UFX とクライアント ドライバーは、エクスポート メソッドとイベント コールバック関数を使用して相互に通信します。 エクスポート メソッド (UfxDeviceXxx または UfxEndpointXxx という名前) は、UFX によってエクスポートされ、クライアント ドライバーによって呼び出されます。 コールバック関数 (EVT_UFX_Xxx という名前) は、クライアント ドライバーに実装され、UFX によって呼び出されます。

UFX は、すべてのクライアント ドライバーのコールバック関数を非同期で呼び出し、オブジェクトごとにコールバックを 1 つずつ呼び出します。 たとえば、1 つの USB デバイス オブジェクトと 3 つのエンドポイント オブジェクトがあるとします。 このとき、最大 4 つのコールバック関数 (デバイス用に 1 つ、エンドポイントごとに 1 つ) を一度に呼び出すことができます。 各コールバック メソッドについて、UFX は、クライアント ドライバーが UfxDeviceEventComplete を呼び出してドライバーが要求を完了したことを示すまで待機します。 これらのエクスポートを待機している間に UFX がリッスンする他のエクスポート メソッドは、UfxDeviceNotifyHardwareFailure のみです。 多くのクライアント コールバック関数は省略可能です。 以下の関数は必須です。

初期化

  1. Windows Driver Foundation (WDF) がクライアント ドライバーの EVT_WDF_DRIVER_DEVICE_ADD コールバック実装を呼び出すと、ファンクション コントローラー クライアント ドライバーは初期化プロセスを開始します。 この実装では、クライアント ドライバーは UfxFdoInit を呼び出してから、WdfDeviceCreate を呼び出してデバイス オブジェクトを作成する必要があります。
  2. クライアント ドライバーは、UfxDeviceCreate を呼び出して USB デバイス オブジェクトを作成し、UFXDEVICE ハンドルを取得します。
  3. クライアント ドライバーは、UfxDeviceNotifyHardwareReady を呼び出して、クライアント ドライバーのコールバック関数の呼び出し準備が整っていることを UFX に示します。
  4. UFX は、以下のような初期化タスクを実行します。
    • UFX は、クライアント ドライバーの EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD 実装を呼び出して、既定のエンドポイントを作成します。
    • UFX は、デバイスでサポートされているインターフェイスの子物理デバイス オブジェクト (PDO) を作成します。
    • UFX は、IOCTL_INTERNAL_USBFN_ACTIVATE_USB_BUS 要求を送信するときに、デバイス クラス ドライバーのアクティブ化を待機します。 また、デバイスが接続されたことを示す UfxDeviceNotifyAttach をクライアント ドライバーが呼び出すまで待機します。

クラス ドライバー通知

セットアップ パケットとバスの状態に関する通知を受け取るには、クラス ドライバーが IOCTL_INTERNAL_USBFN_ACTIVATE_USB_BUS 要求を送信する必要があります。 UFX は、これらの要求をクラス ドライバー固有の通知キューに入れます。 クライアント ドライバーからバス イベントに関する通知を受信すると、UFX は適切な各キューから要求を取り出して完了します。 クラス ドライバーが通知を失うことのないように、UFX はクラス ドライバー用の固定サイズの通知キューを保持します。

デバイスのアタッチ/デタッチ イベント

UFX は、ファンクション コントローラー クライアント ドライバーが UfxDeviceNotifyAttach を呼び出すまで、デバイスがデタッチされていると想定します。

この呼び出しの後、UFX は、USB 仕様での定義に従い、デバイスの状態を Powered に設定します。 状態の変化についてクライアント ドライバーに通知するために、UFX はクライアント ドライバーの EVT_UFX_DEVICE_USB_STATE_CHANGE 実装を呼び出します。

UFX は、デバイスの充電を支援するために充電器ドライバー (Cad.sys) に通知します。 また、UFX は、クラス ドライバーによって以前に送信された IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 要求を完了することによって、クラス ドライバーに通知します。

クライアント ドライバーは、バスがデタッチされるときに UfxDeviceNotifyDetach を呼び出す必要があります。 クライアントは、UfxDeviceNotifyAttach を呼び出すたびにデタッチを 1 回だけ呼び出す必要があります。 UfxDeviceNotifyDetach の呼び出しの後、UFX は EVT_UFX_DEVICE_HOST_DISCONNECT を呼び出します (インターフェイスの変更ではない場合)。 すると UFX は、すべてのエンドポイント キューの削除や既定のエンドポイント キューの開始など、すべてのクリーンアップ タスクを続行します。 UFX は EVT_UFX_DEVICE_USB_STATE_CHANGE を呼び出し、IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 要求を完了することでクラス ドライバーに通知します。

ハードウェア障害

ハードウェア エラーが発生した場合、クライアント ドライバーは UfxDeviceNotifyHardwareFailure を呼び出す必要があります。 これに応じて、UFX はデバイス スタックを破棄し、クライアント ドライバーの EVT_UFX_DEVICE_CONTROLLER_RESET を呼び出すことによって、この状況からの回復を試行する可能性があります。 クライアントは、コントローラーを初期状態にリセットする必要があります。 別のハードウェア障害が発生した場合、クライアントは UfxDeviceNotifyHardwareFailure を再度呼び出す必要があります。 2 回目の呼び出しでは、UFX はその状態とバグチェックを記録します。

ポート検出

ポート検出は UFX によって実行されます。 ファンクション コントローラー クライアント ドライバーの EVT_UFX_DEVICE_PORT_DETECT コールバック関数の呼び出しにより、デバイスがアタッチされているポートの種類が判定されます。 クライアントは、USBFN_PORT_TYPE で定義されているポートの種類のいずれかを使用して UfxDevicePortDetectComplete または UfxDevicePortDetectCompleteEx を呼び出すことによって応答します。

クライアントがポートの種類を判定できない場合、クライアントは UsbfnUnknownPort を報告する必要があります。 ポートが不明またはダウンストリーム ポートの場合、UFX はクライアント ドライバーの EVT_UFX_DEVICE_HOST_CONNECT 関数を呼び出します。 UFX はしばらくの間、バスをリッスンします。 ポートが不明であっても、セットアップ パケットなどのトラフィックがある場合、UFX は UsbfnStandardDownstreamPort を想定します。 それ以外の場合、UFX はポートを UsbfnInvalidDedicatedChargingPort に割り当てます。 ポートの種類が判定されると、UFX は Cad.sys に通知し、クライアント ドライバーの EVT_UFX_DEVICE_PORT_CHANGE 関数を呼び出します。 この関数では、クライアント ドライバーは、UFX ポートの種類に合わせてハードウェアの状態を変更する必要があります。

エンドポイントの作成

UFX は、ホストから送信されたセットアップ パケットを処理できるように、クライアント ドライバーの EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD を呼び出すことによって、既定のエンドポイント (エンドポイント 0) を作成します。 UFX は、EVT_UFX_DEVICE_ENDPOINT_ADD を呼び出すことによって他のエンドポイントを作成します。 UFX がエンドポイントを作成するのは、クライアント ドライバーが UfxDeviceNotifyHardwareReady を呼び出した後のみです。 これらのコールバック関数では、クライアント ドライバーはエンドポイント オブジェクトに対して UfxEndpointCreate を呼び出し、UFXENDPOINT ハンドルを取得する必要があります。 UFX は、エンドポイントが属するインターフェイスに関連付けられているクラス ドライバー PDO に親を設定します。 既定のエンドポイントの親は、USB デバイス オブジェクトです。 エンドポイントには、転送キューおよびコマンド キューという 2 つのフレームワーク キュー オブジェクトが含まれています。どちらも、デバイスが構成済み状態にある場合にのみアクセスできます (エンドポイント 0 は例外で、UFX が EVT_UFX_DEVICE_HOST_CONNECT を呼び出した後にアクセスできます)。

デバイスの列挙

クライアント ドライバーでは、UFX がドライバーの EVT_UFX_DEVICE_HOST_CONNECT を呼び出す前に、ホストへの接続を許可しないでください。 クライアント ドライバーが UfxDeviceNotifyReset を呼び出すと、デバイスの列挙が開始されます。 Default の状態では、UFX は標準セットアップ パケットを処理します。

リセット

UFX はすべてのエンドポイント キューを削除し、IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE リクエストをクライアント ドライバーに送信して、エンドポイント 0 の wMaxPacketSize を更新します。 UFX は、既定のエンドポイントのキューを開始し、状態を Default に設定します。

既定値

UFX は、クライアント ドライバーの EVT_UFX_DEVICE_USB_STATE_CHANGE 関数を呼び出します。 また、クラス ドライバーに対して状態の通知も行います。 UFX は、SET_ADDRESS 標準セットアップ パケットを受信した後、状態を Addressed に設定します。

Addressed

UFX は、クライアント ドライバーの EVT_UFX_DEVICE_ADDRESSED 関数を呼び出して、使用すべきアドレスをクライアントに示します。 アドレスが 0 の場合、UFX は状態を Default に戻し、EVT_UFX_DEVICE_USB_STATE_CHANGE を呼び出してクラス ドライバーに通知します。 SET_CONFIGURATION 標準セットアップ パケットを受信すると、UFX は状態を Configured に設定します。

構成済み

選択された構成が 0 の場合、UFX はインターフェイス エンドポイントを削除し、状態を Addressed に設定します。 UFX は、IOCTL_INTERNAL_USBFN_DESCRIPTOR_UPDATE 要求をクライアント ドライバーに送信して、インターフェイス エンドポイントの wMaxPacketSize を更新します。 UFX は、すべてのインターフェイス エンドポイント キューの削除が完了したことを確認し、インターフェイス エンドポイント キューを開始します。 ポートの種類が UsbfnStandardDownstreamPort または UsbfnChargingDownstreamPort でない場合、UFX はポートの種類を UsbfnStandardDownstreamPort に変更して Cad.sys に通知します。また、EVT_UFX_DEVICE_PORT_CHANGE および EVT_UFX_DEVICE_USB_STATE_CHANGE を呼び出して状態の更新をクライアント ドライバーに通知し、構成された状態をクラス ドライバーに通知します。

標準の制御転送

UFX は、クライアント ドライバーが使用する既定のエンドポイントを作成するための EVT_UFX_DEVICE_DEFAULT_ENDPOINT_ADD を呼び出した後、いつでも既定のエンドポイントで制御転送を処理できます。 すべての制御転送は、8 バイトのセットアップ パケットで始まります。 セットアップ パケットをホストに送信するには、クライアント ドライバーで UfxEndpointNotifySetup を呼び出す必要があります。 標準制御転送は UFX によって完了されます。 制御転送に関連付けられたデータがある場合、UFX は、必要に応じて既定の制御エンドポイントに対して読み取りおよび書き込みを行います。

非標準の制御転送

UFX が制御転送を処理できない場合は、IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 要求を完了することで、転送が適切なクラス ドライバーに転送されます。 制御転送は、エンドポイント記述子内で制御エンドポイントとして定義されている任意のエンドポイントで行うことができます。 既定の制御エンドポイント以外のエンドポイントでの制御転送は常に、非標準の制御転送になります。 制御エンドポイントが既定の制御エンドポイントの場合、UFX はクラス ドライバーに対し、そのクラス ドライバーへのクラス要求としてマークされたセットアップ パケットを通知します。 制御エンドポイントがインターフェイスに属している場合、UFX はそのインターフェイスに関連付けられているクラス ドライバーに通知します。 クラス ドライバーは必要に応じて、制御エンドポイントに対して読み取りと書き込みを行う必要があります。

データ転送

データ転送は、IOCTL_INTERNAL_USBFN_TRANSFER_INIOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT、または IOCTL_INTERNAL_USBFN_TRANSFER_OUT 要求を送信することにより、クラス ドライバーによって開始されます。 これらの各要求を検証した後、UFX はそれを適切なエンドポイント キューに転送し、クライアント ドライバーによる処理を求めます。 クライアント ドライバーは、追加の検証を実行する必要があります。 クライアント ドライバーは、エンドポイント キューで転送要求を受け取ります。 クライアント ドライバーは、バスの使用率を最大化するために必要な数の要求ををこのキューから取得できます。 クライアント ドライバーは、正常な要求を STATUS_SUCCESS で完了する必要があります。 ドライバーは要求の取り消しにベスト エフォートで試み、取り消された場合は、その要求を STATUS_CANCELLED で完了する必要があります。 無効なパラメーターが渡された場合、クライアント ドライバーは STATUS_INVALID_PARAMETER で要求を完了します。

制御転送

制御転送は、8 バイトのセットアップ パケットで始まります。 セットアップ パケットをホストに送信するには、クライアント ドライバーで UfxEndpointNotifySetup を呼び出す必要があります。 UFX は、通知要求を完了することで、非標準の制御転送をクラス ドライバーに通知します。 クライアントと UFX はどちらも、IOCTL_INTERNAL_USBFN_TRANSFER_INIOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT、または IOCTL_INTERNAL_USBFN_TRANSFER_OUT を使用して、既定の制御エンドポイントに対して読み取りおよび書き込みを行います。 ただし、インターフェイスでは、対応するクラス ドライバーのみが使用できる他の制御エンドポイントを定義できます。 制御エンドポイントは、セットアップ パケットに応答して停止させることができます。 クラス ドライバーは、IOCTL_INTERNAL_USBFN_SET_PIPE_STATE 要求を送信してエンドポイントを停止します。 ハードウェアまたはクライアント ドライバーは、停止が送信された後、エンドポイント上のトラフィックを直ちに再開する必要があります。 制御エンドポイントは、事前データなしで長さ 0 のパケット (ZLP) を送受信することもできます。 クライアント ドライバーと UFX は、IOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_INIOCTL_INTERNAL_USBFN_CONTROL_STATUS_HANDSHAKE_OUT を使用してこれを行うことができます。

一括転送と割り込み転送

一括転送ではデータの配信が保証されるため、大量データの送信に使用されます。 転送は、IOCTL_INTERNAL_USBFN_TRANSFER_INIOCTL_INTERNAL_USBFN_TRANSFER_IN_APPEND_ZERO_PKT、または IOCTL_INTERNAL_USBFN_TRANSFER_OUT を使用して一括エンドポイントで送信できます。 一括エンドポイントを停止する場合は、制御エンドポイントと同様に IOCTL_INTERNAL_USBFN_SET_PIPE_STATE を使用します。 クライアント ドライバーは、すべてのホスト要求に応答して STALL パケットを送信し、IOCTL 要求を保留する必要があります。 制御エンドポイントとは異なり、停止された一括エンドポイントは、stall 状態が明示的にクリアされるまで停止したままになります。

割り込み転送 割り込み転送は一括転送に似ていますが、待機時間が保証されている点が異なります。 割り込み転送には一括転送と同じインターフェイスがありますが、ストリーミング機能はありません。

等時性転送

このバージョンのクライアント ドライバーでは、等時性 (アイソクロナス) 転送のサポートが予定されていません。

電源管理

クライアント ドライバーは、電源管理のすべての側面を所有しています。 コールバック関数は非同期であるため、クライアント ドライバーは、適切な電源状態に戻って要求を完了してから、UfxDeviceEventComplete などの適切なイベント完了エクスポート関数を呼び出す必要があります。

UFX が Working 状態になるのは、デバイス状態 (USBFN_DEVICE_STATE で定義) が UsbfnDeviceStateSuspended または UsbfnDeviceStateAttached であり、ポートの種類が報告されていない場合です。 あるいは、UFX がポートの種類 (USBFN_PORT_TYPE で定義) として UsbfnStandardDownstreamPort または UsbfnChargingDownstreamPort を報告済みの場合です。

UFX の Working 状態が開始または終了する際には、EVT_UFX_DEVICE_USB_STATE_CHANGE または EVT_UFX_DEVICE_PORT_CHANGE 実装が呼び出されます。 Working への移行または Working 状態からの移行は、クライアント ドライバーが UfxDeviceEventComplete を呼び出すと完了します。

Working 状態では、UFX は任意のコールバックを呼び出すことができます。 Working 状態でないときに、UFX が EVT_UFX_DEVICE_USB_STATE_CHANGE を呼び出すのは、動作状態に入る場合のみであり、EVT_UFX_DEVICE_REMOTE_WAKEUP_SIGNAL を呼び出すのは、サスペンド中にリモート ウェイクを発行する場合のみです (サポートされている場合)。

デバイスのサスペンド

デバイスのサスペンドは、バス上に 3 ミリ秒間トラフィックがない場合に発生します。 この場合クライアント ドライバーは、サスペンドと再開を検出したときに UfxDeviceNotifySuspend および UfxDeviceNotifyResume を呼び出して UFX に通知する必要があります。 これらの呼び出しを受け取ると、UFX は EVT_UFX_DEVICE_USB_STATE_CHANGE を呼び出し、IOCTL_INTERNAL_USBFN_BUS_EVENT_NOTIFICATION 要求を完了することでクラス ドライバーに通知します。 リモート ウェイクがデバイスでサポートされ、ホストで有効になっていれば、UFX はサスペンド中に EVT_UFX_DEVICE_USB_STATE_CHANGE を呼び出して、リモート ウェイク信号を発行できます。