Аппаратные ресурсы для User-Mode драйверов периферийных устройств SPB
В примерах кода в этом разделе показано, как драйвер среда выполнения платформы драйвера режима пользователя (UMDF) для периферийного устройства на простой периферийной шине (SPB) получает аппаратные ресурсы, необходимые для работы с устройством. В этих ресурсах содержатся сведения, используемые драйвером для установки логического подключения к устройству. Дополнительные ресурсы могут включать прерывание и один или несколько входных или выходных контактов GPIO. (Закрепление GPIO — это закрепление на устройстве контроллера ввода-вывода общего назначения, настроенного в качестве входных или выходных данных. Дополнительные сведения см. в разделе Драйверы контроллера GPIO.) В отличие от устройства, сопоставленного с памятью, периферийное устройство, подключенное к SPB, не требует блока адресов системной памяти для сопоставления регистров.
Этот драйвер реализует интерфейс IPnpCallbackHardware2 и регистрирует этот интерфейс в UMDF во время вызова метода IDriverEntry::OnDeviceAdd драйвера. Платформа вызывает методы в интерфейсе IPnpCallbackHardware2 , чтобы уведомить драйвер об изменениях в состоянии питания устройства.
При восстановлении питания на периферийном устройстве, подключенном к SPB, платформа драйверов вызывает метод IPnpCallbackHardware2::OnPrepareHardware , чтобы уведомить драйвер о том, что это устройство должно быть подготовлено к использованию. Во время этого вызова драйвер получает два списка аппаратных ресурсов в качестве входных параметров. Параметр pWdfResourcesRaw указывает на список необработанных ресурсов, а параметр pWdfResourcesTranslated — на список переведенных ресурсов. Оба параметра являются указателями на объекты IWDFCmResourceList . Преобразованные ресурсы включают идентификатор подключения , необходимый драйверу периферийных устройств SPB для установки логического подключения к периферийным устройствам, подключенным к SPB. Дополнительные сведения см. в разделе Идентификаторы подключений для периферийных устройств SPB.
Чтобы разрешить драйверу периферийных устройств UMDF получать идентификаторы подключений в списке ресурсов, INF-файл, устанавливающий драйвер, должен содержать следующую директиву в разделе DDInstall для конкретного WDF:
UmdfDirectHardwareAccess = AllowDirectHardwareAccess Дополнительные сведения об этой директиве см. в разделе Указание директив WDF в INF-файлах.
В следующем примере кода показано, как метод OnPrepareHardware драйвера получает идентификатор подключения из параметра pWdfResourcesTranslated .
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, копируется в переменную с именем connectionId
. В следующем примере кода показано, как включить идентификатор подключения в имя пути устройства, которое можно использовать для идентификации периферийного устройства.
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 для отправки запросов ввода-вывода на периферийное устройство. Прежде чем драйвер отправляет на периферийное устройство запрос на чтение, запись или IOCTL, драйвер вызывает метод IWDFRemoteTarget::FormatRequestForRead, IWDFRemoteTarget::FormatRequestForWrite или IWDFRemoteTarget::FormatRequestForIoctl для форматирования запроса ввода-вывода. Интерфейс IWDFRemoteTarget наследует эти три метода от интерфейса IWDFIoTarget . Затем драйвер вызывает метод IWDFIoRequest::Send для отправки запроса ввода-вывода на периферийное устройство, подключенное к 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 создает запрос ввода-вывода и инкапсулирует этот запрос в экземпляре интерфейса IWDFIoRequest , на который указываетpWdfIoRequest
переменная. - Переменная
pWdfDriver
является указателем на интерфейс IWDFDriver объекта драйвера платформы, представляющего драйвер периферийного устройства SPB. ПеременныеpInBuffer
иinBufferSize
указывают адрес и размер входного буфера, содержащего данные для запроса на запись. Метод IWDFDriver::CreatePreallocatedWdfMemory создает объект памяти платформы для входного буфера и назначает объект IWDFIoRequest , на который указываетpWdfIoRequest
, как родительский объект объекта памяти (чтобы объект памяти автоматически освобождался при освобождении родительского объекта). После того как драйвер вызывает метод Release , чтобы освободить локальную ссылку на объект памяти, родительский объект содержит единственную ссылку на этот объект. - Переменная
pWdfRemoteTarget
является удаленным целевым указателем, полученным из вызова OpenFileByName в предыдущем примере кода. Метод IWDFRemoteTarget::FormatRequestForWrite форматирует запрос ввода-вывода для операции записи. - Переменная
fSynchronous
имеет значение TRUE , если запрос на запись отправляется синхронно, и false , если он должен быть отправлен асинхронно. ПеременнаяpCallback
является указателем на ранее созданный интерфейс IRequestCallbackRequestCompletion . Если запрос отправляется асинхронно, вызов метода IWDFIoRequest::SetCompletionCallback регистрирует этот интерфейс. Позже вызывается метод IRequestCallbackRequestCompletion::OnCompletion для уведомления драйвера о асинхронном завершении запроса. - Метод Send отправляет отформатированный запрос на запись на периферийное устройство, подключенное к SPB. Переменная
Flags
указывает, должен ли запрос на запись отправляться синхронно или асинхронно. - Если запрос отправляется синхронно, метод IWDFIoRequest::D eleteWdfObject удаляет объект запроса ввода-вывода, на который
pWdfIoRequest
указывает , и дочерний объект, на которыйpInputMemory
указывает . Интерфейс IWDFIoRequest наследует этот метод от интерфейса IWDFObject . Если запрос отправляется асинхронно, вызов метода DeleteWdfObject должен произойти позже в методе OnCompletion драйвера.
Альтернативная реализация предыдущего примера кода может создавать объекты IWDFIoRequest и IWDFMemory во время инициализации драйвера и многократно использовать эти же объекты вместо создания и удаления новых объектов при каждой отправке запроса ввода-вывода. Дополнительные сведения см. в разделах IWDFIoRequest2::Reuse и IWDFMemory::SetBuffer.
Кроме того, альтернативная реализация может проверить код состояния ввода-вывода из запроса ввода-вывода, если вызов Send завершается успешно. Дополнительные сведения см. в разделе IWDFIoRequest::GetCompletionParams.