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


Подключение драйвера 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.