用户模式 SPB 外设驱动程序的硬件资源
本主题中的代码示例演示 用户模式驱动程序框架 (UMDF) 驱动程序如何在 简单外围总线 (SPB) 获取运行设备所需的硬件资源。 这些资源中包含驱动程序用于建立与设备的逻辑连接的信息。 其他资源可能包括中断以及一个或多个 GPIO 输入或输出引脚。 (GPIO 引脚是配置为输入或输出的通用 I/O 控制器设备上的引脚;有关详细信息,请参阅 GPIO 控制器驱动程序。) 与内存映射的设备不同,SPB 连接的外围设备不需要系统内存地址块来将其寄存器映射到其中。
此驱动程序实现 IPnpCallbackHardware2 接口,并在调用驱动程序的 IDriverEntry::OnDeviceAdd 方法期间向 UMDF 注册此接口。 框架调用 IPnpCallbackHardware2 接口中的方法,以通知驱动程序设备电源状态的更改。
将电源还原到 SPB 连接的外围设备时,驱动程序框架会调用 IPnpCallbackHardware2::OnPrepareHardware 方法,以通知驱动程序必须准备好使用此设备。 在此调用期间,驱动程序接收两个硬件资源列表作为输入参数。 pWdfResourcesRaw 参数指向原始资源列表,pWdfResourcesTranslated 参数指向已翻译的资源列表。 这两个参数都是指向 IWDFCmResourceList 对象的指针。 转换的资源包括 SPB 外围设备驱动程序建立与 SPB 连接的外围设备的逻辑连接所需的 连接 ID 。 有关详细信息,请参阅 SPB 外围设备的连接 ID。
若要使 UMDF 外围驱动程序能够接收其资源列表中的连接 ID,安装驱动程序的 INF 文件必须在其特定于 WDF 的 DDInstall 节中包含以下指令:
UmdfDirectHardwareAccess = AllowDirectHardwareAccess 有关此指令的详细信息,请参阅 在 INF 文件中指定 WDF 指令。
下面的代码示例演示驱动程序的 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_I2C)
{
if (fConnIdFound == FALSE)
{
// Save the SPB 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;
}
}
前面的代码示例将 SPB 连接的外围设备的连接 ID 复制到名为 的 connectionId
变量中。 下面的代码示例演示如何将连接 ID 合并到可用于标识外围设备的设备路径名称中。
WCHAR szTargetPath[100];
HRESULT hres;
// Create the device path using the well-known resource hub
// path name and the connection ID.
//
// TODO: Replace this hardcoded string with the appropriate
// helper method from Reshub.h when available.
hres = StringCbPrintfW(&szTargetPath[0],
sizeof(szTargetPath),
L"\\\\.\\RESOURCE_HUB\\%0*I64x",
(size_t)(sizeof(LARGE_INTEGER) * 2),
connectionId.QuadPart);
if (FAILED(hres))
{
// Error handling
...
}
前面的代码示例将 SPB 连接的外围设备的路径名称写入数组中 szTargetPath
。 下面的代码示例使用此设备路径名称打开 SPB 连接的外围设备的文件句柄。
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 方法成功,则 SPB 连接的外围设备的驱动程序可以使用 IWDFRemoteTarget 对象向外围设备发送 I/O 请求。 在驱动程序将读取、写入或 IOCTL 请求发送到外围设备之前,驱动程序会调用 IWDFRemoteTarget::FormatRequestForRead、 IWDFRemoteTarget::FormatRequestForWrite 或 IWDFRemoteTarget::FormatRequestForIoctl 方法来格式化 I/O 请求。 IWDFRemoteTarget 接口从 IWDFIoTarget 接口继承这三种方法。 接下来,驱动程序调用 IWDFIoRequest::Send 方法,将 I/O 请求发送到 SPB 连接的外围设备。
在以下代码示例中,SPB 外围设备驱动程序调用 Send 方法将 IRP_MJ_WRITE 请求发送到 SPB 连接的外围设备。
HRESULT hres;
IWDFMemory *pInputMemory = NULL;
IWDFRemoteTarget *pWdfIoRequest = 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
...
}
// After this call, the parent holds the only reference.
pWdfMemory->Release();
}
// Format the I/O request for a write operation.
if (SUCCEEDED(hres))
{
hres = pRemoteTarget->FormatRequestForWrite(pWdfIoRequest,
NULL,
pInputMemory,
NULL,
0);
if (FAILED(hres))
{
// Error handling
...
}
}
// Send the request to the SPB controller.
if (SUCCEEDED(hres))
{
ULONG Flags = fSynchronous ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0;
// Set the I/O completion callback.
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 接口的指针,该对象表示 SPB 连接的外围设备。 IWDFDevice::CreateRequest 方法创建 I/O 请求,并将此请求封装在变量指向的 IWDFIoRequest 接口实例中pWdfIoRequest
。 - 变量
pWdfDriver
是指向表示 SPB 外围设备驱动程序的框架驱动程序对象的 IWDFDriver 接口的指针。pInBuffer
和inBufferSize
变量指定包含写入请求数据的输入缓冲区的地址和大小。 IWDFDriver::CreatePreallocatedWdfMemory 方法为输入缓冲区创建框架内存对象,并将所pWdfIoRequest
指向的 IWDFIoRequest 对象指定为内存对象的父对象 (,以便在释放内存对象的父对象) 时自动释放内存对象。 驱动程序调用 Release 方法以释放其对内存对象的本地引用后,父级将保留对此对象的唯一引用。 - 变量
pWdfRemoteTarget
是从前面的代码示例中的 OpenFileByName 调用获取的远程目标指针。 IWDFRemoteTarget::FormatRequestForWrite 方法格式化写入操作的 I/O 请求。 - 如果要同步发送写入请求,则
fSynchronous
变量为 TRUE ;如果要异步发送,则变量为 FALSE 。 变量pCallback
是指向以前创建的 IRequestCallbackRequestCompletion 接口的指针。 如果要异步发送请求,则对 IWDFIoRequest::SetCompletionCallback 方法的调用将注册此接口。 稍后,将调用 IRequestCallbackRequestCompletion::OnCompletion 方法,以便在请求异步完成时通知驱动程序。 - Send 方法将格式化的写入请求发送到 SPB 连接的外围设备。 变量
Flags
指示是以同步方式还是异步发送写入请求。 - 如果请求是同步发送的, 则 IWDFIoRequest::D eleteWdfObject 方法将删除由
pWdfIoRequest
指向的 I/O 请求对象和 由pInputMemory
指向的子对象。 IWDFIoRequest 接口从 IWDFObject 接口继承此方法。 如果请求是异步发送的,则应稍后在驱动程序的 OnCompletion 方法中调用 DeleteWdfObject 方法。
上述代码示例的替代实现可能在驱动程序初始化期间创建 IWDFIoRequest 和 IWDFMemory 对象,并重复使用这些对象,而不是在每次发送 I/O 请求时创建和删除新对象。 有关详细信息,请参阅 IWDFIoRequest2::Reuse 和 IWDFMemory::SetBuffer。
此外,如果 发送 调用成功,备用实现可能会检查 I/O 请求中的 I/O 状态代码。 有关详细信息,请参阅 IWDFIoRequest::GetCompletionParams。