Поделиться через


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

В примерах кода в этом разделе показано, как драйвер KMDF для периферийного устройства на простой периферийной шине (SPB) получает аппаратные ресурсы, необходимые для работы с устройством. В этих ресурсах содержатся сведения, используемые драйвером для установки логического подключения к устройству. Дополнительные ресурсы могут включать прерывание и один или несколько входных или выходных контактов GPIO. (Закрепление GPIO — это контакт на устройстве контроллера ввода-вывода общего назначения, настроенном в качестве входных или выходных данных. Дополнительные сведения см. в статье Драйверы GPIO.) В отличие от устройства, сопоставленного с памятью, периферийное устройство, подключенное к SPB, не требует блока адресов системной памяти для сопоставления регистров.

Этот драйвер реализует набор функций обратного вызова событий управления питанием и Plug and Play. Чтобы зарегистрировать эти функции в KMDF, функция обратного вызова события EvtDriverDeviceAdd драйвера вызывает метод WdfDeviceInitSetPnpPowerEventCallbacks . Платформа вызывает функции обратного вызова событий управления питанием, чтобы уведомить драйвер об изменениях в состоянии питания периферийного устройства. В эти функции входит функция EvtDevicePrepareHardware , которая выполняет все операции, необходимые для обеспечения доступа к устройству для драйвера.

При восстановлении питания на периферийное устройство платформа драйверов вызывает функцию EvtDevicePrepareHardware , чтобы уведомить периферийного драйвера SPB о том, что это устройство должно быть подготовлено к использованию. Во время этого вызова драйвер получает два списка аппаратных ресурсов в качестве входных параметров. Параметр ResourcesRaw — это дескриптор объекта WDFCMRESLIST для списка необработанных ресурсов, а параметр ResourcesTranslated — это дескриптор объекта WDFCMRESLIST для списка переведенных ресурсов. Переведенные ресурсы включают идентификатор подключения , необходимый драйверу для установки логического подключения к периферийным устройствам. Дополнительные сведения см. в разделе Идентификаторы подключений для SPB-Connected периферийных устройств.

В следующем примере кода показано, как функция EvtDevicePrepareHardware получает идентификатор подключения из параметра ResourcesTranslated .

BOOLEAN fConnectionIdFound = FALSE;
LARGE_INTEGER connectionId = 0;
ULONG resourceCount;
NTSTATUS status = STATUS_SUCCESS;

resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);

// Loop through the resources and save the relevant ones.

for (ULONG ix = 0; ix < resourceCount; ix++)
{
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;

    pDescriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, ix);

    if (pDescriptor == NULL)
    {
        status = 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 (fConnectionIdFound == FALSE)
                    {
                        // Save the SPB connection ID.

                        connectionId.LowPart = pDescriptor->u.Connection.IdLowPart;
                        connectionId.HighPart = pDescriptor->u.Connection.IdHighPart;
                        fConnectionIdFound = TRUE;
                    }
                }
            }

            if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO)
            {
                // Check for GPIO pin resource.
                ...
            }
        }
        break;

    case CmResourceTypeInterrupt:
        {
            // Check for interrupt resource.
            ...
        }
        break;

    default:
        // Don't care about other resource descriptors.
        break;
    }
}

В приведенном выше примере кода идентификатор подключения для периферийного устройства, подключенного к SPB, копируется в переменную с именем connectionId.

В следующем примере кода показано, как включить этот идентификатор подключения в имя пути устройства, которое можно использовать для открытия логического подключения к периферийным устройствам. Это имя пути к устройству определяет концентратор ресурсов как системный компонент, из которого можно получить параметры, необходимые для доступа к периферийным устройствам.

// Use the connection ID to create the full device path name.
 
DECLARE_UNICODE_STRING_SIZE(szDeviceName, RESOURCE_HUB_PATH_SIZE);

status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&szDeviceName,
                                          connectionId.LowPart,
                                          connectionId.HighPart);

if (!NT_SUCCESS(status))
{
     // Error handling
     ...
}

В предыдущем примере кода макрос DECLARE_UNICODE_STRING_SIZE создает объявление инициализированной переменной UNICODE_STRING с именем szDeviceName , которая имеет буфер, достаточно большой, чтобы содержать путь к устройству в формате, используемом концентратором ресурсов. Этот макрос определен в файле заголовка Ntdef.h. Константой RESOURCE_HUB_PATH_SIZE указывается количество байтов в имени пути устройства. Макрос RESOURCE_HUB_CREATE_PATH_FROM_ID создает путь к устройству из идентификатора подключения. RESOURCE_HUB_PATH_SIZE и RESOURCE_HUB_CREATE_PATH_FROM_ID определяются в файле заголовка Reshub.h.

В следующем примере кода используется имя пути к устройству, чтобы открыть дескриптор файла (с именем SpbIoTarget) на периферийном устройстве, подключенном к SPB.

// Open the SPB peripheral device as a remote I/O target.
 
WDF_IO_TARGET_OPEN_PARAMS openParams;
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&openParams,
                                            &szDeviceName,
                                            (GENERIC_READ | GENERIC_WRITE));

openParams.ShareAccess = 0;
openParams.CreateDisposition = FILE_OPEN;
openParams.FileAttributes = FILE_ATTRIBUTE_NORMAL;

status = WdfIoTargetOpen(SpbIoTarget, &openParams);

if (!NT_SUCCESS(status))
{
    // Error handling
    ...
}

В предыдущем примере кода функция WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME инициализирует структуру WDF_IO_TARGET_OPEN_PARAMS , чтобы драйвер смог открыть логическое соединение с периферийным устройством, указав имя устройства. Переменная SpbIoTarget является дескриптором WDFIOTARGET для целевого объекта ввода-вывода платформы. Этот дескриптор был получен из предыдущего вызова метода WdfIoTargetCreate , который не показан в примере. Если вызов метода WdfIoTargetOpen завершается успешно, драйвер может использовать дескриптор SpbIoTarget для отправки запросов ввода-вывода на периферийное устройство.

В функции обратного вызова события EvtDriverDeviceAdd периферийный драйвер SPB может вызвать метод WdfRequestCreate , чтобы выделить объект запроса платформы для использования драйвером. Позже, когда объект больше не требуется, драйвер вызывает метод WdfObjectDelete для удаления объекта. Драйвер может многократно использовать объект запроса платформы, полученный из вызова WdfRequestCreate , для отправки запросов ввода-вывода на периферийное устройство. Для запроса на чтение, запись или IOCTL драйвер вызывает метод WdfIoTargetSendReadSynchronously, WdfIoTargetSendWriteSynchronously или WdfIoTargetSendIoctlSynchronously для отправки запроса.

В следующем примере кода драйвер вызывает WdfIoTargetSendWriteSynchronously, чтобы синхронно отправить запрос IRP_MJ_WRITE на периферийное устройство, подключенное к SPB. В начале этого примера переменная указывает на непагрегированные буферы, pBuffer содержащие данные, которые должны быть записаны на периферийное устройство, а dataSize переменная задает размер этих данных в байтах.

ULONG_PTR bytesWritten;
NTSTATUS status;

// Describe the input buffer.

WDF_MEMORY_DESCRIPTOR memoryDescriptor;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor, pBuffer, dataSize);

// Configure the write request to time out after 2 seconds.

WDF_REQUEST_SEND_OPTIONS requestOptions;
WDF_REQUEST_SEND_OPTIONS_INIT(&requestOptions, WDF_REQUEST_SEND_OPTION_TIMEOUT);
requestOptions.Timeout = WDF_REL_TIMEOUT_IN_SEC(2);

// Send the write request synchronously.

status = WdfIoTargetSendWriteSynchronously(SpbIoTarget,
                                           SpbRequest,
                                           &memoryDescriptor,
                                           NULL,
                                           &requestOptions,
                                           &bytesWritten);
if (!NT_SUCCESS(status))
{
    // Error handling
    ...
}

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

  1. Вызов функции WDF_MEMORY_DESCRIPTOR_INIT_BUFFER инициализирует memoryDescriptor переменную, которая представляет собой WDF_MEMORY_DESCRIPTOR структуру, описывающую входной буфер. Ранее драйвер вызывал подпрограмму, например ExAllocatePoolWithTag , для выделения буфера из непагрегированного пула и скопировал данные записи в этот буфер.
  2. Вызов функции WDF_REQUEST_SEND_OPTIONS_INIT инициализирует requestOptions переменную, которая представляет собой WDF_REQUEST_SEND_OPTIONS структуру, содержащую необязательные параметры для запроса на запись. В этом примере структура настраивает время ожидания запроса, если он не завершается через две секунды.
  3. Вызов метода WdfIoTargetSendWriteSynchronously отправляет запрос на запись на периферийное устройство, подключенное к SPB. Метод возвращается синхронно после завершения операции записи или времени ожидания. При необходимости другой поток драйвера может вызвать WdfRequestCancelSentRequest , чтобы отменить запрос.

В вызове WdfIoTargetSendWriteSynchronously драйвер предоставляет переменную с именем SpbRequest, которая является дескриптором объекта запроса платформы, созданного ранее драйвером. После вызова WdfIoTargetSendWriteSynchronously драйвер обычно должен вызывать метод WdfRequestReuse , чтобы подготовить объект запроса платформы для повторного использования.