Аппаратные ресурсы для 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);
}

В приведенном выше примере кода выполняется следующее:

  1. Переменная pWdfDevice является указателем на интерфейс IWDFDevice объекта устройства платформы, представляющего периферийное устройство, подключенное к SPB. Метод IWDFDevice::CreateRequest создает запрос ввода-вывода и инкапсулирует этот запрос в экземпляре интерфейса IWDFIoRequest , на который указывает pWdfIoRequest переменная.
  2. Переменная pWdfDriver является указателем на интерфейс IWDFDriver объекта драйвера платформы, представляющего драйвер периферийного устройства SPB. Переменные pInBuffer и inBufferSize указывают адрес и размер входного буфера, содержащего данные для запроса на запись. Метод IWDFDriver::CreatePreallocatedWdfMemory создает объект памяти платформы для входного буфера и назначает объект IWDFIoRequest , на который указывает pWdfIoRequest , как родительский объект объекта памяти (чтобы объект памяти автоматически освобождался при освобождении родительского объекта). После того как драйвер вызывает метод Release , чтобы освободить локальную ссылку на объект памяти, родительский объект содержит единственную ссылку на этот объект.
  3. Переменная pWdfRemoteTarget является удаленным целевым указателем, полученным из вызова OpenFileByName в предыдущем примере кода. Метод IWDFRemoteTarget::FormatRequestForWrite форматирует запрос ввода-вывода для операции записи.
  4. Переменная fSynchronous имеет значение TRUE , если запрос на запись отправляется синхронно, и false , если он должен быть отправлен асинхронно. Переменная pCallback является указателем на ранее созданный интерфейс IRequestCallbackRequestCompletion . Если запрос отправляется асинхронно, вызов метода IWDFIoRequest::SetCompletionCallback регистрирует этот интерфейс. Позже вызывается метод IRequestCallbackRequestCompletion::OnCompletion для уведомления драйвера о асинхронном завершении запроса.
  5. Метод Send отправляет отформатированный запрос на запись на периферийное устройство, подключенное к SPB. Переменная Flags указывает, должен ли запрос на запись отправляться синхронно или асинхронно.
  6. Если запрос отправляется синхронно, метод IWDFIoRequest::D eleteWdfObject удаляет объект запроса ввода-вывода, на который pWdfIoRequest указывает , и дочерний объект, на который pInputMemoryуказывает . Интерфейс IWDFIoRequest наследует этот метод от интерфейса IWDFObject . Если запрос отправляется асинхронно, вызов метода DeleteWdfObject должен произойти позже в методе OnCompletion драйвера.

Альтернативная реализация предыдущего примера кода может создавать объекты IWDFIoRequest и IWDFMemory во время инициализации драйвера и многократно использовать эти же объекты вместо создания и удаления новых объектов при каждой отправке запроса ввода-вывода. Дополнительные сведения см. в разделах IWDFIoRequest2::Reuse и IWDFMemory::SetBuffer.

Кроме того, альтернативная реализация может проверить код состояния ввода-вывода из запроса ввода-вывода, если вызов Send завершается успешно. Дополнительные сведения см. в разделе IWDFIoRequest::GetCompletionParams.