UMDF 周辺機器ドライバーをシリアル ポートに接続する
SerCx2 で管理されるシリアル ポート上の周辺デバイス用の UMDF ドライバーは、デバイスを動作させるために特定のハードウェア リソースを必要とします。 これらのリソースには、ドライバーがシリアル ポートへの論理接続を開くために必要な情報が含まれています。 追加のリソースには、割り込み、および 1 つ以上の GPIO 入力ピンまたは出力ピンが含まれる場合があります。
このドライバーは IPnpCallbackHardware2 インターフェイスを実装し、ドライバーのIDrivertEntry::OnDeviceAddメソッドを呼び出す間にこのインターフェイスを Windows ドライバー フレームワークに登録します。 フレームワークは、IPnpCallbackHardware2 インターフェイスのメソッドを呼び出して、デバイスの電源状態の変化をドライバーに通知します。
シリアル接続された周辺デバイスが初期化されていない D0 デバイス電源状態になると、ドライバー フレームワークはドライバーの IPnpCallbackHardware2::OnPrepareHardware メソッドを呼び出して、このデバイスを使用できるように準備するようにドライバーに指示します。 この呼び出し中に、ドライバーはハードウェア リソースの 2 つのリストを入力パラメーターとして受け取ります。 pWdfResourcesRaw パラメータは未加工リソースのリストを指し、pWdfResourcesTranslated パラメータは翻訳されたリソースのリストを指します。 どちらのパラメータも IWDFCmResourceList オブジェクトへのポインタです。 変換されたリソースには、周辺機器ドライバーがシリアル接続された周辺機器への論理接続を確立するために必要な接続 ID が含まれます。
UMDF 周辺機器ドライバーがそのリソース リストで接続 ID を受信できるようにするには、ドライバーをインストールする INF ファイルの WDF 固有の DDInstall セクションに次のディレクティブを含める必要があります:
UmdfDirectHardwareAccess = AllowDirectHardwareAccess このディレクティブの詳細については、INF ファイルでの WDF ディレクティブの指定をご覧ください。 このディレクティブを使用する INX ファイル (対応する INF ファイルの構築に使用される) の例については、WDK ドライバー サンプルの SpbAccelerometer をご覧ください。
次のコード例は、ドライバーの OnPrepareHardware メソッドが pWdfResourcesTranslated パラメーターから接続 ID を取得する方法を示しています。
BOOLEAN fConnectIdFound = FALSE;
BOOLEAN fDuplicateFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;
resourceCount = pWdfResourcesTranslated->GetCount();
// Loop through the resources and save the relevant ones.
for (ULONG ix = 0; ix < resourceCount; ix++)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
pDescriptor = pWdfResourcesTranslated->GetDescriptor(ix);
if (pDescriptor == NULL)
{
hr = E_POINTER;
break;
}
// Determine the resource type.
switch (pDescriptor->Type)
{
case CmResourceTypeConnection:
{
// Check against the expected connection types.
UCHAR Class = pDescriptor->u.Connection.Class;
UCHAR Type = pDescriptor->u.Connection.Type;
if (Class == CM_RESOURCE_CONNECTION_CLASS_SERIAL)
{
if (Type == CM_RESOURCE_CONNECTION_TYPE_SERIAL_UART)
{
if (fConnIdFound == FALSE)
{
// Save the serial controller's connection ID.
connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
fConnectIdFound = TRUE;
}
else
{
fDuplicateFound = TRUE;
}
}
}
if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO)
{
// Check for GPIO pin resource.
...
}
}
break;
case CmResourceTypeInterrupt:
{
// Check for interrupt resources.
...
}
break;
default:
// Ignore all other resource descriptors.
break;
}
}
前述のコード例では、シリアル接続された周辺デバイスの接続 ID をconnectionId
という名前の変数にコピーします。 次のコード例は、周辺デバイスが接続されているシリアル コントローラーを識別するために使用できるデバイス パス名に接続 ID を組み込む方法を示しています。
WCHAR szTargetPath[100];
HRESULT hres;
// Create the device path using the well-known resource hub
// path name and the connection ID.
//
hres = StringCbPrintfW(&szTargetPath[0],
sizeof(DevicePath),
L"\\\\.\\RESOURCE_HUB\\%0*I64x",
(size_t)(sizeof(LARGE_INTEGER) * 2),
connectionId.QuadPart);
if (FAILED(hres))
{
// Error handling
...
}
前述のコード例では、シリアル コントローラーのデバイス パス名をszTargetPath
配列に書き込みます。 次のコード例では、このパス名を使用してシリアル コントローラーへのファイル ハンドルを開きます。
UMDF_IO_TARGET_OPEN_PARAMS openParams;
openParams.dwShareMode = 0;
openParams.dwCreationDisposition = OPEN_EXISTING;
openParams.dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED;
hres = pRemoteTarget->OpenFileByName(&szTargetPath[0],
(GENERIC_READ | GENERIC_WRITE),
&openParams);
if (FAILED(hres))
{
// Error handling
...
}
前述のコード例では、pRemoteTarget
パラメーターは IWDFRemoteTarget オブジェクトへのポインターです。 IWDFRemoteTarget::OpenFileByName メソッドの呼び出しが成功すると、シリアル接続された周辺機器のドライバは IWDFRemoteTarget ブジェクトを使用してシリアルコントローラにI/O要求を送ることができます。
周辺デバイスに読み取りまたは書き込み要求を送るために、ドライバはまずこのオブジェクトの IWDFRemoteTarget::FormatRequestForRead または IWDFRemoteTarget::FormatRequestForWrite メソッドを呼び出して要求をフォーマットします。 (IWDFRemoteTarget インターフェイスは、IWDFIoTarget インターフェイスからこれら 2 つのメソッドを継承します。)
シリアルコントローラーにI/O制御リクエストを送信するために、ドライバーはまずIWDFRemoteTarget::FormatRequestForIoctlメソッドを呼び出してリクエストをフォーマットします。 (IWDFRemoteTarget インターフェイスは、IWDFIoTarget インターフェイスからこのメソッドを継承します。) 次に、ドライバーは IWDFIoRequest::Send メソッドを呼び出してシリアルに接続された周辺機器に I/O 制御要求を送信します。
次のコード例では、ペリフェラル ドライバーが I/O 制御リクエストをシリアル コントローラーに送信します。
HRESULT hres;
IWDFMemory *pInputMemory = NULL;
// Create a new I/O request.
if (SUCCEEDED(hres))
{
hres = pWdfDevice->CreateRequest(NULL,
pWdfDevice,
&pWdfIoRequest);
if (FAILED(hres))
{
// Error handling
...
}
}
// Allocate memory for the input buffer.
if (SUCCEEDED(hres))
{
hres = pWdfDriver->CreatePreallocatedWdfMemory(pInBuffer,
inBufferSize,
NULL,
pWdfIoRequest,
&pInputMemory);
if (FAILED(hres))
{
// Error handling
...
}
}
// Format the request to be an I/O control request.
if (SUCCEEDED(hres))
{
hres = pRemoteTarget->FormatRequestForIoctl(pWdfIoRequest,
ioctlCode,
NULL,
pInputMemory,
NULL,
NULL,
NULL);
if (FAILED(hres))
{
// Error handling
...
}
}
// Send the request to the serial controller.
if (SUCCEEDED(hres))
{
ULONG Flags = fSynchronous ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0;
if (!fSynchronous)
{
pWdfIoRequest->SetCompletionCallback(pCallback, NULL);
}
hres = pWdfIoRequest->Send(pRemoteTarget, Flags, 0);
if (FAILED(hres))
{
// Error handling
...
}
}
if (fSynchronous || FAILED(hres))
{
pWdfIoRequest->DeleteWdfObject();
SAFE_RELEASE(pWdfIoRequest);
}
前述のコード例では次の処理が行われます:
pWdfDevice
変数は、シリアル接続された周辺デバイスを表すフレームワーク デバイス オブジェクトの IWDFDevice インターフェイスへのポインターです。 IWDFDevice::CreateRequestメソッドはI/Oリクエストを作成し、pWdfIoRequest
パラメーターによって指し示されるIWDFIoRequestインターフェースのインスタンスにこのリクエストをカプセル化します。 I/O リクエストは後で削除されます (ステップ 6 を参照)。 この実装は、送信される I/O リクエストごとにリクエスト オブジェクトを作成して削除するため、やや非効率的です。 より効率的なアプローチは、一連の I/O リクエストに対して同じリクエスト オブジェクトを再利用することです。 詳細については、フレームワーク リクエスト オブジェクトの再利用をご覧ください。pWdfDriver
変数は、ペリフェラル ドライバーを表すフレームワーク ドライバー オブジェクトの IWDFDriver インターフェイスへのポインターです。pInBuffer
変数とinBufferSize
変数は、I/O 制御リクエストの入力バッファのアドレスとサイズを指定します。 IWDFDriver::CreatePreallocatedWdfMemory メソッドは、入力バッファ用のフレームワークメモリオブジェクトを作成し、メモリオブジェクトの親オブジェクトとして、pWdfIoRequest
によって指される IWDFIoRequest オブジェクトを指定します。変数
pWdfRemoteTarget
は、前のコード例の OpenFileByName 呼び出しから取得したリモート ターゲット ポインターです。 IWDFRemoteTarget::FormatRequestForIoctl メソッドは、I/O 制御操作のリクエストをフォーマットします。 変数ioctlCode
は、シリアル I/O リクエスト インターフェイス の表にリストされている I/O 制御コードの 1 つに設定されます。I/O 制御リクエストが同期的に送信される場合、変数
fSynchronous
は TRUE であり、非同期的に送信される場合は FALSEです。 変数pCallback
は、以前に作成された IRequestCallbackRequestCompletion インターフェイスへのポインターです。 リクエストが非同期で送信される場合、IWDFIoRequest::SetCompletionCallback メソッドの呼び出しによってこのインターフェイスが登録されます。 その後、IRequestCallbackRequestCompletion::OnCompletion メソッドが呼び出され、リクエストが非同期的に完了したときにドライバーに通知されます。Send メソッドは、フォーマットされた書き込み要求をシリアル接続された周辺デバイスに送信します。 変数
Flags
は、書き込みリクエストが同期的に送信されるか、非同期的に送信されるかを示します。要求が同期的に送信される場合、IWDFIoRequest::DeleteWdfObject メソッドは、
pWdfIoRequest
が指す I/O 要求オブジェクトとpInputMemory
が指す子オブジェクトの両方を削除します。 IWDFIoRequest インターフェイスは、IWDFObject インターフェイスからこのメソッドを継承します。 リクエストが非同期で送信される場合、DeleteWdfObject メソッドの呼び出しは、ドライバーの OnCompletion メソッドで後で行われます。