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


Подключение драйвера KMDF к пин-адресам ввода-вывода GPIO

Ресурс ввода-вывода GPIO — это набор одного или нескольких пин-адресов GPIO, настроенных как входные данные или выходные данные. Драйвер для периферийных устройств, физически подключающихся к этим контактам, получает соответствующий ресурс ввода-вывода GPIO из операционной системы. Драйвер периферийных устройств открывает подключение к пин-кодам GPIO в этом ресурсе и отправляет запросы ввода-вывода в дескриптор, представляющий это соединение.

В следующем примере кода показано, как драйвер фреймворка драйверов в режиме ядра (KMDF) для периферийного устройства может получить описание ресурса ввода-вывода GPIO, назначенного диспетчером Plug and Play (PnP) драйверу.

NTSTATUS
  EvtDevicePrepareHardware(
    _In_ WDFDEVICE Device,
    _In_ WDFCMRESLIST ResourcesRaw,
    _In_ WDFCMRESLIST ResourcesTranslated
    )
{
    int ResourceCount, Index;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
    XYZ_DEVICE_CONTEXT *DeviceExtension;

    ...

    DeviceExtension = XyzDrvGetDeviceExtension(Device);
    ResourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
    for (Index = 0; Index < ResourceCount; Index += 1) {
        Descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, Index);
        switch (Descriptor->Type) {

        //
        // GPIO I/O descriptors
        //

        case CmResourceTypeConnection:

            //
            // Check against expected connection type.
            //

            if ((Descriptor->u.Connection.Class == CM_RESOURCE_CONNECTION_CLASS_GPIO) &&
                (Descriptor->u.Connection.Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO)) {

                DeviceExtension->ConnectionId.LowPart = Descriptor->u.Connection.IdLowPart;
                DeviceExtension->ConnectionId.HighPart = Descriptor->u.Connection.IdHighPart;

        ...

}

В предыдущем примере кода переменная DeviceExtension является указателем на контекст устройств для периферийного устройства. Функция XyzDrvGetDeviceExtension , извлекающая этот контекст устройства, реализуется драйвером периферийных устройств. Этот драйвер ранее зарегистрировал функцию обратного вызова EvtDevicePrepareHardware, вызвав метод WdfDeviceInitSetPnpPowerEventCallbacks.

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

NTSTATUS IoRoutine(WDFDEVICE Device, BOOLEAN ReadOperation) 
{
    WDFIOTARGET IoTarget;
    XYZ_DEVICE_CONTEXT *DeviceExtension;
    UNICODE_STRING ReadString;
    WCHAR ReadStringBuffer[100];
    BOOL DesiredAccess;
    NTSTATUS Status;
    WDF_OBJECT_ATTRIBUTES ObjectAttributes;
    WDF_IO_TARGET_OPEN_PARAMS OpenParams

    DeviceExtension = XyzDrvGetDeviceExtension(Device);
    RtlInitEmptyUnicodeString(&ReadString,
                              ReadStringBuffer,
                              sizeof(ReadStringBuffer));

    Status = RESOURCE_HUB_CREATE_PATH_FROM_ID(&ReadString,
                                              DeviceExtension->ConnectionId.LowPart,
                                              DeviceExtension->ConnectionId.HighPart);

    NT_ASSERT(NT_SUCCESS(Status));

    WDF_OBJECT_ATTRIBUTES_INIT(&ObjectAttributes);
    ObjectAttributes.ParentObject = Device;

    Status = WdfIoTargetCreate(Device, &ObjectAttributes, &IoTarget);
    if (!NT_SUCCESS(Status)) {
        goto IoErrorEnd;
    }   

    if (ReadOperation != FALSE) {
        DesiredAccess = GENERIC_READ;
    } else {
        DesiredAccess = GENERIC_WRITE;
    }

    WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(&OpenParams, ReadString, DesiredAccess);

    Status = WdfIoTargetOpen(IoTarget, &OpenParams);
    if (!NT_SUCCESS(Status)) {
        goto IoErrorEnd;
    }
    ...

В предыдущем примере кода переменная Device является дескриптором WDFDEVICE для объекта устройства фреймворка, связанного с периферийным устройством. Функция RESOURCE_HUB_CREATE_PATH_FROM_ID создает строку, содержащую имя ресурса ввода-вывода GPIO. В примере кода эта строка используется для открытия ресурса ввода-вывода GPIO по имени.

После получения дескриптора периферийных устройств к ресурсу ввода-вывода GPIO этот драйвер может отправлять запросы управления ввода-вывода для чтения данных из или записи данных в пин-коды GPIO. Драйвер, открывающий ресурс ввода-вывода GPIO для операций чтения, использует запросы управления IOCTL_GPIO_READ_PINS ввода-вывода для чтения данных из закреплений в ресурсе. Драйвер, который открывает ресурс ввода-вывода GPIO для операций записи, использует запросы управления IOCTL_GPIO_WRITE_PINS ввода-вывода для записи данных в закрепленные в ресурсе. В следующем примере кода показано, как выполнить операцию чтения или записи GPIO.

    WDF_OBJECT_ATTRIBUTES RequestAttributes;
    WDF_OBJECT_ATTRIBUTES Attributes;
    WDF_REQUEST_SEND_OPTIONS SendOptions;
    WDFREQUEST IoctlRequest;
    WDFIOTARGET IoTarget;
    WDFMEMORY WdfMemory;
    NTSTATUS Status;

    WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes);
    Status = WdfRequestCreate(&RequestAttributes, IoTarget, &IoctlRequest);
    if (!NT_SUCCESS(Status)) {
        goto RwErrorExit;
    }

    //
    // Set up a WDF memory object for the IOCTL request.
    //

    WDF_OBJECT_ATTRIBUTES_INIT(&Attributes);
    Attributes.ParentObject = IoctlRequest;
    Status = WdfMemoryCreatePreallocated(&Attributes, Data, Size, &WdfMemory);
    if (!NT_SUCCESS(Status)) {
        goto RwErrorExit;
    }

    //
    // Format the request.
    //

    if (ReadOperation != FALSE) {
        Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
                                                  IoctlRequest,
                                                  IOCTL_GPIO_READ_PINS,
                                                  NULL,
                                                  0,
                                                  WdfMemory,
                                                  0);

    } else {
        Status = WdfIoTargetFormatRequestForIoctl(IoTarget,
                                                  IoctlRequest,
                                                  IOCTL_GPIO_WRITE_PINS,
                                                  WdfMemory,
                                                  0,
                                                  WdfMemory,
                                                  0);
    }

    if (!NT_SUCCESS(Status)) {
        goto RwErrorExit;
    }

    //
    // Send the request synchronously (with a 60-second time-out).
    //

    WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions,
                                  WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
    WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&SendOptions,
                                         WDF_REL_TIMEOUT_IN_SEC(60));

    Status = WdfRequestAllocateTimer(IoctlRequest);
    if (!NT_SUCCESS(Status)) {
        goto RwErrorExit;
    }

    if (!WdfRequestSend(IoctlRequest, IoTarget, &SendOptions)) {
        Status = WdfRequestGetStatus(IoctlRequest);
    }

    ...

В приведенном выше примере Data кода является указателем на буфер данных, Size размер в байтах этого буфера данных и ReadOperation указывает, является ли запрошенная операция операцией чтения (TRUE) или записи (FALSE).

Узнать больше

Дополнительные сведения о запросах IOCTL_GPIO_READ_PINS, включая сопоставление пин-кодов ввода данных с битами в выходном буфере запроса, см. в IOCTL_GPIO_READ_PINS. Дополнительные сведения о запросах IOCTL_GPIO_WRITE_PINS, включая сопоставление битов в входном буфере запроса с выводными контактами данных, см. в IOCTL_GPIO_WRITE_PINS.

Пример драйвера, демонстрирующий запись периферийного драйвера GPIO, работающего в режиме ядра, см. в примере драйвера SimDevice в коллекции примеров драйверов GPIO на GitHub.