Аппаратные ресурсы для драйверов периферийных устройств SPB User-Mode

Примеры кода в этом разделе показывают, как драйвер User-Mode Driver Framework (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::DeleteWdfObject удаляет как объект запроса ввода-вывода, на который указывает pWdfIoRequest, так и дочерний объект, на который указывает pInputMemory. Интерфейс IWDFIoRequest наследует этот метод из интерфейса IWDFObject . Если запрос отправляется асинхронно, вызов метода DeleteWdfObject должен выполняться позже в методе OnCompletion драйвера.

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

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