USB Type-C コネクタ ドライバーを記述する
次のシナリオでは、USB Type-C コネクタ ドライバーを作成する必要があります。
USB Type-C ハードウェアが、電源供給 (PD) ステート マシンを処理する機能を備えている場合。 それ以外の場合は、USB Type-C ポート コントローラー ドライバーの作成を検討します。 詳細については、「USB Type-C ポート コントローラー ドライバーを作成する」を参照してください。
ハードウェアに埋め込みコントローラーがない場合。 それ以外の場合は、Microsoft から提供されるインボックス ドライバーである UcmUcsi.sys (「UCSI ドライバー」を参照) を ACPI トランスポート用に読み込むか、非 ACPI トランスポート用に UCSI クライアント ドライバーを作成します。
まとめ
- クラス拡張機能とクライアント ドライバーで使用される UCM オブジェクト
- UCM クラス拡張機能によって提供されるサービス
- クライアント ドライバーで想定される動作
公式の仕様
適用対象
- Windows 10
WDF のバージョン
- KMDF バージョン 1.15
- UMDF バージョン 2.15
重要な API
USB Type-C コネクタを管理する USB コネクタ マネージャー (UCM) の概要と、コネクタ ドライバーの予期される動作について説明します。
UCM は、WDF クラス拡張クライアント ドライバー モデルを使用して設計されています。 このクラス拡張機能 (UcmCx) は、Microsoft 提供の WDF ドライバーであり、クライアント ドライバーからコネクタに関する情報を報告するために呼び出すことができる、インターフェイスを提供しています。 UCM クライアント ドライバーは、コネクタのハードウェア インターフェイスを使用し、コネクタで発生するイベントをクラス拡張機能に認識させます。 一方、クラス拡張機能は、オペレーティング システム イベントに応答し、クライアント ドライバーに実装されているコールバック関数を呼び出します。
システムの USB Type-C コネクタを有効にするには、クライアント ドライバーを作成する必要があります。
開始する前に
開発用コンピューターに、最新の Windows Driver Kit (WDK) をインストールします。 このキットには、UCM クライアント ドライバーを作成するために必要なヘッダー ファイルとライブラリが含まれています。具体的には、以下が必要になります。
スタブ ライブラリ (UcmCxstub.lib)。 このライブラリはクライアント ドライバーによる呼び出しを変換し、UcmCx に渡します。
ヘッダー ファイル (UcmCx.h)。
ユーザー モードまたはカーネル モードで実行される UCM クライアント ドライバーを作成できます。 ユーザー モードの場合、ドライバーは UMDF 2.x ライブラリにバインドされ、カーネル モードの場合は KMDF 1.15 にバインドされます。 プログラミング インターフェイスは、どちらのモードでも同じです。
クライアント ドライバーで、USB Type-C コネクタと USB Power Delivery の高度な機能をサポートするかどうかを決定します。
このサポートにより、USB Type-C コネクタ、USB Type-C ドックとアクセサリ、USB Type-C 充電器を備えた Windows デバイスを構築できるようになります。 クライアント ドライバーは、オペレーティング システムにシステムの USB および電力消費に関するポリシーを実装するためのコネクタ イベントを報告します。
USB Type-C コネクタを備えたターゲット コンピューターまたは Windows 10 Mobile に、Windows 10 デスクトップ エディション (Home、Pro、Enterprise、Education) をインストールします。
UCM の概要に加え、UCM と他の Windows ドライバーとの対話方法についても理解しておきます。 「アーキテクチャ: Windows システム用 USB Type-C 設計」を参照してください。
Windows Driver Foundation (WDF) についてよく理解しておきます。 『Windows Driver Foundation を使用したドライバーの開発』(共著: Penny Orwick、Guy Smith) を参照することをお勧めします。
UCM クラス拡張機能によって提供されるサービスの概要
UCM クラス拡張機能は、オペレーティング システムに対し、データと電源のロール、充電レベル、およびネゴシエートされた PD コントラクトの変更に関する情報を常に通知する役割を担います。 このような変更が発生した場合、クライアント ドライバーがハードウェアと対話する際にクラス拡張機能に通知する必要があります。 クラス拡張機能は、クライアント ドライバーが通知の送信に使用できる一連のメソッドを提供します (このトピックで説明します)。 提供されるサービスを以下に示します。
データ ロールの構成
USB Type-C システムにおけるデータ ロール (ホストまたはファンクション) は、コネクタの CC ピンの状態で決まります。 クライアント ドライバーは、ポート コントローラーから CC ライン (「アーキテクチャ: Windows システム用 USB Type-C 設計」を参照) の状態を読み取り、ポートがアップストリーム側ポート (UFP) またはダウンストリーム側ポート (UFP) に解決されているかどうかを判断します。 この情報は、現在のロールを USB ロール スイッチ ドライバーに報告できるように、クラス拡張機能に報告されます。
Note
USB ロール スイッチ ドライバーは、Windows 10 Mobile システムで使用されます。 Windows 10 デスクトップ エディション システムでは、クラス拡張機能とロール スイッチ ドライバー間の通信はオプションです。 このようなシステムでは、デュアル ロール コントローラーが使用されていない可能性があり、その場合はロール スイッチ ドライバーが使用されていません。
電源ロールと充電
クライアント ドライバーは、USB Type-C の電流レベルのアドバタイズメントを読み取るか、PD 電源コントラクトをパートナー コネクタとネゴシエートします。
- Windows 10 Mobile システムでは、適切な充電器はソフトウェアの支援によって選択されます。 クライアント ドライバーは、充電レベルを充電判定ドライバー (CAD.sys) に送信できるように、コントラクト情報をクラス拡張機能に報告します。 CAD は、使用する電流レベルを選択し、充電レベル情報をバッテリー サブシステムに転送します。
- Windows 10 デスクトップ エディション システムでは、適切な充電器がハードウェアによって選択されます。 クライアント ドライバーは、その情報を取得してクラス拡張機能に転送することを選択できます。 そのロジックを別のドライバーに実装することもできます。
データ ロール/電源ロールの変更
PD コントラクトのネゴシエート後、データ ロールと電源ロールが変更されることがあります。 この変更は、クライアント ドライバーまたはパートナー コネクタによって開始される可能性があります。 クライアント ドライバーは、必要に応じてクラス拡張機能で再構成を実行できるように、変更に関する情報をクラス拡張機能に報告します。
データ ロール/電源ロールの更新
オペレーティング システムによって、現在のデータ ロールが正しくないと判断される場合があります。 その場合、クラス拡張機能はドライバーのコールバック関数を呼び出して、必要なロールのスワップ操作を実行します。
Microsoft 提供の USB Type-C ポリシー マネージャーは、USB Type-C コネクタのアクティビティを監視します。 Windows バージョン 1809 では、ポリシー マネージャーに対するクライアント ドライバーの作成に使用できるプログラミング インターフェイスのセットが導入されています。 クライアント ドライバーは、USB Type-C コネクタのポリシー決定に参加できます。 このセットでは、カーネル モード エクスポート ドライバーまたはユーザー モード ドライバーの作成を選択できます。 詳細については、USB Type-C ポリシー マネージャー クライアント ドライバーを作成するに関する記事を参照してください 。
クライアント ドライバーで想定される動作
クライアント ドライバーでは、以下のタスクを行います。
- CC ラインでの変更を検出し、UFP、DFP などのパートナーの種類を判断する。 これを行うには、ドライバーは、USB Type-C 仕様で定義されている完全な Type-C ステート マシンを実装する必要があります。
- CC ラインで検出された方向に基づいて、Mux を構成する。 これには、PD 送信機/受信機の電源をオンにすることと、PD メッセージの処理と応答が含まれます。 これを行うには、ドライバーは、USB Power Delivery 2.0 仕様で定義されている完全な PD 送信機/受信機ステート マシンを実装する必要があります。
- (ソースまたはシンクとしての) コントラクトのネゴシエート、ロール スワップなどの PD ポリシー決定を行う。 クライアント ドライバーは、最も適切なコントラクトを決定する役割を担います。
- 代替モードのアドバタイズおよびネゴシエートを行い、代替モードが検出された場合は Mux を構成する。 クライアント ドライバーは、ネゴシエートする代替モードを決定する役割を担います。
- コネクタを介して VBus/VConn を制御する。
1. UCM コネクタ オブジェクト (UCMCONNECTOR) を初期化する
UCM コネクタ オブジェクト (UCMCONNECTOR) は、USB Type-C コネクタを表し、UCM クラス拡張機能とクライアント ドライバーの間でメイン ハンドルの役割を担います。 このオブジェクトは、コネクタの動作モードと電源ソーシング機能を追跡します。
クライアント ドライバーがコネクタの UCMCONNECTOR ハンドルを取得する流れを次に示します。 ドライバー内で、これらのタスクを実行してください。
UcmInitializeDevice を呼び出します。このとき、UCM_MANAGER_CONFIG 構造体への参照を渡します。 ドライバーは、WdfDeviceCreate を呼び出す前に、EVT_WDF_DRIVER_DEVICE_ADD コールバック関数でこのメソッドを呼び出す必要があります。
UCM_CONNECTOR_TYPEC_CONFIG 構造体で、USB Type-C コネクタの初期化パラメータを指定します。 これには、コネクタの動作モード (ダウンストリーム側ポート、アップストリーム側ポート、デュアル ロール対応のいずれか) が含まれます。 また、コネクタが電源である場合は、USB Type-C 電流レベルも指定します。 USB Type-C コネクタは、3.5 mm オーディオ ジャックとして機能するように設計できます。 ハードウェアでこの機能がサポートされている場合は、それに応じてコネクタ オブジェクトを初期化する必要があります。
構造体では、データ ロールを処理するためのクライアント ドライバーのコールバック関数も登録する必要があります。
このコールバック関数は、UCM クラス拡張機能によって呼び出されるコネクタ オブジェクトに関連付けられています。 この関数をクライアント ドライバーに実装する必要があります。
EVT_UCM_CONNECTOR_SET_DATA_ROLE は、パートナー コネクタへの接続時に、コネクタのデータ ロールを指定されているロールに切り替えます。
クライアント ドライバーを PD 対応にする場合、つまりコネクタの Power Delivery 2.0 ハードウェア実装を処理する場合は、PD 初期化パラメーターを指定する UCM_CONNECTOR_PD_CONFIG 構造体も初期化する必要があります。 これには、電力の流れ (コネクタが電源シンクかソースか) が含まれます。
構造体では、電源ロールを処理するためのクライアント ドライバーのコールバック関数も登録する必要があります。
このコールバック関数は、UCM クラス拡張機能によって呼び出されるコネクタ オブジェクトに関連付けられています。 この関数をクライアント ドライバーに実装する必要があります。
EVT_UCM_CONNECTOR_SET_POWER_ROLE は、パートナー コネクタへの接続時に、コネクタの電源ロールを指定されているロールに設定します。
UcmConnectorCreate を呼び出し、コネクタの UCMCONNECTOR ハンドルを取得します。 このメソッドの呼び出しは、必ずクライアント ドライバーが WdfDeviceCreate を呼び出してフレームワーク デバイス オブジェクトを作成した後に行います。 この呼び出しに適切な場所は、ドライバーの EVT_WDF_DEVICE_PREPARE_HARDWARE または EVT_WDF_DEVICE_D0_ENTRY です。
EVT_UCM_CONNECTOR_SET_DATA_ROLE EvtSetDataRole;
NTSTATUS
EvtDevicePrepareHardware(
WDFDEVICE Device,
WDFCMRESLIST ResourcesRaw,
WDFCMRESLIST ResourcesTranslated
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT devCtx;
UCM_MANAGER_CONFIG ucmCfg;
UCM_CONNECTOR_CONFIG connCfg;
UCM_CONNECTOR_TYPEC_CONFIG typeCConfig;
UCM_CONNECTOR_PD_CONFIG pdConfig;
WDF_OBJECT_ATTRIBUTES attr;
PCONNECTOR_CONTEXT connCtx;
UNREFERENCED_PARAMETER(ResourcesRaw);
UNREFERENCED_PARAMETER(ResourcesTranslated);
TRACE_FUNC_ENTRY();
devCtx = GetDeviceContext(Device);
if (devCtx->Connector)
{
goto Exit;
}
//
// Initialize UCM Manager
//
UCM_MANAGER_CONFIG_INIT(&ucmCfg);
status = UcmInitializeDevice(Device, &ucmCfg);
if (!NT_SUCCESS(status))
{
TRACE_ERROR(
"UcmInitializeDevice failed with %!STATUS!.",
status);
goto Exit;
}
TRACE_INFO("UcmInitializeDevice() succeeded.");
//
// Create a USB Type-C connector #0 with PD
//
UCM_CONNECTOR_CONFIG_INIT(&connCfg, 0);
UCM_CONNECTOR_TYPEC_CONFIG_INIT(
&typeCConfig,
UcmTypeCOperatingModeDrp,
UcmTypeCCurrentDefaultUsb | UcmTypeCCurrent1500mA | UcmTypeCCurrent3000mA);
typeCConfig.EvtSetDataRole = EvtSetDataRole;
UCM_CONNECTOR_PD_CONFIG_INIT(&pdConfig, UcmPowerRoleSink | UcmPowerRoleSource);
connCfg.TypeCConfig = &typeCConfig;
connCfg.PdConfig = &pdConfig;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attr, CONNECTOR_CONTEXT);
status = UcmConnectorCreate(Device, &connCfg, &attr, &devCtx->Connector);
if (!NT_SUCCESS(status))
{
TRACE_ERROR(
"UcmConnectorCreate failed with %!STATUS!.",
status);
goto Exit;
}
connCtx = GetConnectorContext(devCtx->Connector);
UcmEventInitialize(&connCtx->EventSetDataRole);
TRACE_INFO("UcmConnectorCreate() succeeded.");
Exit:
TRACE_FUNC_EXIT();
return status;
}
2. パートナー コネクタのアタッチ イベントを報告する
クライアント ドライバーは、パートナー コネクタへの接続が検出されたときに UcmConnectorTypeCAttach を呼び出す必要があります。 この呼び出しによって UCM クラス拡張機能に通知され、そこからオペレーティング システムへの通知が行われます。 この時点でシステムは、USB Type-C レベルでの充電を開始できます。
UCM クラス拡張機能により、USB ロール スイッチ ドライバー (URS) への通知も行われます。 パートナーの種類に基づいて、URS はコントローラをホスト ロールまたはファンクション ロールに設定します。 このメソッドを呼び出す前に、システムの Mux が正しく構成されていることを確認します。 そうしないと、システムがファンクション ロールの場合に、正しくない速度 (SuperSpeed ではなく高速) で接続されます。
UCM_CONNECTOR_TYPEC_ATTACH_PARAMS attachParams;
UCM_CONNECTOR_TYPEC_ATTACH_PARAMS_INIT(
&attachParams,
UcmTypeCPortStateDfp);
attachParams.CurrentAdvertisement = UcmTypeCCurrent1500mA;
status = UcmConnectorTypeCAttach(
Connector,
&attachParams);
if (!NT_SUCCESS(status))
{
TRACE_ERROR(
"UcmConnectorTypeCAttach() failed with %!STATUS!.",
status);
goto Exit;
}
TRACE_INFO("UcmConnectorTypeCAttach() succeeded.");
3. USB Type-C アドバタイズの変更を報告する
最初のアタッチ イベントでは、パートナー コネクタは電流のアドバタイズメントを送信します (パートナーが USB Type-C ダウンストリーム側ポートで、アドバタイズメントによってパートナー コネクタの電流レベルが指定されている場合)。 それ以外の場合は、アドバタイズメントにより、UCMCONNECTOR ハンドル (ローカル コネクタ) で表されるローカル コネクタの電流レベルが指定されます。 この最初のアドバタイズメントは、接続の有効期間中に変更されることがあります。 これらの変更をクライアント ドライバーで監視する必要があります。
ローカル コネクタが電源シンクであり、電流のアドバタイズメントが変化した場合、クライアント ドライバーは電流のアドバタイズメントの変化を検出し、クラス拡張機能に報告する必要があります。 Windows 10 Mobile システムでは、ソースから給電される電流量を調整するために、その情報が CAD.sys とバッテリー サブシステムによって使用されます。 電流レベルの変化をクラス拡張機能に報告するには、クライアント ドライバーが UcmConnectorTypeCCurrentAdChanged を呼び出す必要があります。
4. ネゴシエートされた新しい PD コントラクトを報告する
コネクタが PD をサポートしている場合、最初のアタッチ イベントの後に、コネクタとそのパートナー コネクタの間で PD メッセージが転送されます。 両方のパートナー間で、コネクタまたはパートナーが給電できる電流レベルを決定するための PD コントラクトがネゴシエートされます。 PD コントラクトが変更されるたびに、クライアント ドライバーは以下のメソッドを呼び出して、クラス拡張機能に変更を報告する必要があります。
- クライアント ドライバーは、パートナーからソース機能のアドバタイズメント (未承諾かどうか) を取得するたびに、以下のメソッドを呼び出す必要があります。 ローカル コネクタ (シンク) は、パートナーがソースである場合にのみ、パートナーから未承諾のアドバタイズメントを受け取ります。 また、ローカル コネクタは、(パートナーが現在シンクである場合でも) ソースとして使用できるパートナーに対して、ソース機能を明示的に要求できます。 このやり取りは、パートナーに Get_Source_Caps メッセージを送信することによって行われます。
- UcmConnectorPdPartnerSourceCaps では、パートナー コネクタによってアドバタイズされたソース機能を報告します。
- UcmConnectorPdConnectionStateChanged では、契約の詳細を報告します。 コントラクトは、Power Delivery 2.0 仕様で定義されている要求データ オブジェクトで記述されます。
- 一方、クライアント ドライバーは、ローカル コネクタ (ソース) がパートナーにソース機能をアドバタイズするたびに、これらのメソッドを呼び出す必要があります。 また、ローカル コネクタがパートナーから Get_Source_Caps メッセージを受信した場合は、ローカル コネクタのソース機能で応答する必要があります。
- UcmConnectorPdSourceCaps では、システムによってパートナー コネクタにアドバタイズされたソース機能を報告します。
- UcmConnectorPdConnectionStateChanged では、現在ネゴシエートされている PD コントラクトの接続機能を報告します。
5. バッテリーの充電状態を報告する
充電レベルが十分でない場合、クライアント ドライバーは UCM クラス拡張機能に通知できます。 クラス拡張機能は、この情報をオペレーティング システムに報告します。 システムはその情報を使用して、充電器によってシステムが最適に充電されていないことをユーザーに通知します。 充電状態は、以下のメソッドで報告できます。
これらのメソッドは、充電状態を指定します。 報告されたレベルが UcmChargingStateSlowCharging または UcmChargingStateTrickleCharging (UCM_CHARGING_STATE を参照) の場合、オペレーティング システムはユーザーに通知します。
6. PR_Swap/DR_Swap イベントを報告する
コネクタがパートナーから電源ロール (PR_Swap) またはデータ ロール (DR_Swap) のスワップ メッセージを受け取った場合、クライアント ドライバーは UCM クラス拡張機能に通知する必要があります。
UcmConnectorDataDirectionChanged
このメソッドは、PD の DR_Swap メッセージが処理された後に呼び出します。 この呼び出しの後、オペレーティング システムは新しいロールを URS に報告します。これにより、既存のロール ドライバーが破棄され、新しいロールのドライバーが読み込まれます。
UcmConnectorPowerDirectionChanged
このメソッドは、PD の PR_Swap メッセージが処理された後に呼び出します。 PR_Swap の後、PD コントラクトを再ネゴシエートする必要があります。 クライアント ドライバーは、手順 4 で説明されているメソッドを呼び出すことによって、PD コントラクト ネゴシエーションを報告する必要があります。
7. 電源ロールとデータロールのスワップ要求を処理するコールバック関数を実装する
UCM クラス拡張機能は、コネクタのデータまたは電源の方向を変更する要求を受け取ることがあります。 その場合は、クライアント ドライバーの EVT_UCM_CONNECTOR_SET_DATA_ROLE および EVT_UCM_CONNECTOR_SET_POWER_ROLE コールバック関数の実装が呼び出されます (コネクタが PD を実装している場合)。 クライアント ドライバーでは、ここまでに UcmConnectorCreate への呼び出しでこれらの関数を登録しています。
クライアント ドライバーは、ハードウェア インターフェイスを使用してロール スワップ操作を実行します。
EVT_UCM_CONNECTOR_SET_DATA_ROLE
コールバック実装では、クライアント ドライバーが以下を実行する必要があります。
- PD の DR_Swap メッセージをポート パートナーに送信する。
- UcmConnectorDataDirectionChanged を呼び出して、メッセージ シーケンスが正常に完了したか失敗したかをクラス拡張機能に通知する。
EVT_UCM_CONNECTOR_SET_DATA_ROLE EvtSetDataRole; NTSTATUS EvtSetDataRole( UCMCONNECTOR Connector, UCM_TYPE_C_PORT_STATE DataRole ) { PCONNECTOR_CONTEXT connCtx; TRACE_INFO("EvtSetDataRole(%!UCM_TYPE_C_PORT_STATE!) Entry", DataRole); connCtx = GetConnectorContext(Connector); TRACE_FUNC_EXIT(); return STATUS_SUCCESS; }
EVT_UCM_CONNECTOR_SET_POWER_ROLE
コールバック実装では、クライアント ドライバーが以下を実行する必要があります。
- PD の PR_Swap メッセージをポート パートナーに送信する。
- UcmConnectorPowerDirectionChanged を呼び出して、メッセージ シーケンスが正常に完了したか失敗したかをクラス拡張機能に通知する。
EVT_UCM_CONNECTOR_SET_POWER_ROLE EvtSetPowerRole; NTSTATUS EvtSetPowerRole( UCMCONNECTOR Connector, UCM_POWER_ROLE PowerRole ) { PCONNECTOR_CONTEXT connCtx; TRACE_INFO("EvtSetPowerRole(%!UCM_POWER_ROLE!) Entry", PowerRole); connCtx = GetConnectorContext(Connector); //PR_Swap operation. TRACE_FUNC_EXIT(); return STATUS_SUCCESS; }
Note
クライアント ドライバーは、コールバック スレッドからではなく、UcmConnectorDataDirectionChanged および UcmConnectorPowerDirectionChanged を非同期的に呼び出すことができます。 一般的な実装では、クラス拡張機能がコールバック関数を呼び出し、クライアント ドライバーがメッセージを送信するためのハードウェア トランザクションを開始します。 トランザクションが完了すると、ハードウェアはドライバーに通知します。 ドライバーは、クラス拡張機能に通知するためにこれらのメソッドを呼び出します。
8. パートナー コネクタのデタッチ イベントを報告する
クライアント ドライバーは、パートナー コネクタへの接続が終了したときに UcmConnectorTypeCDetach を呼び出す必要があります。 この呼び出しによって UCM クラス拡張機能に通知され、そこからオペレーティング システムへの通知が行われます。
ユース ケース例: PC に接続されているモバイル デバイス
Windows 10 Mobile を実行しているデバイスが USB Type-C 接続を介し、Windows 10 デスクトップ エディションを実行している PC に接続されている場合、オペレーティング システムはモバイル デバイスがアップストリーム側ポート (UFP) であることを確認します。これは、MTP がこの方向でのみ機能するためです。 このシナリオでのデータ ロール修正の流れは次のとおりです。
- モバイル デバイス上で実行されているクライアント ドライバーは、UcmConnectorTypeCAttach を呼び出してアタッチ イベントを報告し、パートナー コネクタをダウンストリーム側ポート (UFP) として報告します。
- クライアント ドライバーは、UcmConnectorPdPartnerSourceCaps と UcmConnectorPdConnectionStateChanged を呼び出して PD コントラクトを報告します。 クライアント ドライバーは、UcmConnectorPdPartnerSourceCaps および UcmConnectorPdConnectionStateChanged を呼び出して PD コントラクトを報告します。
- UCM クラス拡張機能が USB デバイス側ドライバーに通知して、これらのドライバーがホストからの列挙に応答します。 オペレーティング システム情報のやり取りは、USB 経由で行われます。
- UCM クラス拡張機能 UcmCx は、クライアント ドライバーのコールバック関数を呼び出してロール (EVT_UCM_CONNECTOR_SET_DATA_ROLE と EVT_UCM_CONNECTOR_SET_POWER_ROLE) を変更します。
Note
2 台の Windows 10 Mobile デバイスどうしが接続されている場合、ロールのスワップは実行されず、その接続が有効な接続ではないことがユーザーに通知されます。