Partilhar via


Conectando um driver KMDF a pinos de E/S gpio

Um recurso de E/S de GPIO é um conjunto de um ou mais pinos GPIO configurados como entradas de dados ou saídas de dados. O driver de um dispositivo periférico que se conecta fisicamente a esses pinos adquire o recurso de E/S de GPIO correspondente do sistema operacional. O driver de dispositivo periférico abre uma conexão com os pinos gpio nesse recurso e envia solicitações de E/S para o identificador que representa essa conexão.

O exemplo de código a seguir mostra como o driver KMDF (estrutura de driver do modo kernel) para um dispositivo periférico pode obter uma descrição do recurso de E/S gpio que o gerenciador de Plug and Play (PnP) atribuiu ao driver.

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;

        ...

}

No exemplo de código anterior, a DeviceExtension variável é um ponteiro para o contexto do dispositivo para o dispositivo periférico. A XyzDrvGetDeviceExtension função , que recupera esse contexto de dispositivo, é implementada pelo driver de dispositivo periférico. Esse driver registrou anteriormente sua função de retorno de chamada EvtDevicePrepareHardware chamando o método WdfDeviceInitSetPnpPowerEventCallbacks .

O exemplo de código a seguir mostra como o driver de dispositivo periférico pode usar a descrição do recurso GPIO obtida no exemplo de código anterior para abrir um identificador WDFIOTARGET para o recurso de E/S gpio do driver.

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;
    }
    ...

No exemplo de código anterior, a Device variável é um identificador WDFDEVICE para o objeto de dispositivo de estrutura do dispositivo periférico. A função RESOURCE_HUB_CREATE_PATH_FROM_ID cria uma cadeia de caracteres que contém o nome do recurso de E/S gpio. O exemplo de código usa essa cadeia de caracteres para abrir o recurso de E/S gpio por nome.

Depois que o driver de dispositivo periférico tiver obtido um identificador para um recurso de E/S de GPIO, esse driver poderá enviar solicitações de controle de E/S para ler dados ou gravar dados nos pinos gpio. Um driver que abre um recurso de E/S de GPIO para leituras usa IOCTL_GPIO_READ_PINS solicitações de controle de E/S para ler dados dos pinos no recurso. Um driver que abre um recurso de E/S de GPIO para gravações usa IOCTL_GPIO_WRITE_PINS solicitações de controle de E/S para gravar dados nos pinos no recurso. O exemplo de código a seguir mostra como executar uma operação de leitura ou gravação do 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);
    }

    ...

No exemplo de código anterior, Data é um ponteiro para um buffer de dados, Size é o tamanho, em bytes, desse buffer de dados e ReadOperation indica se a operação solicitada é uma leitura (TRUE) ou uma gravação (FALSE).

Para obter mais informações

Para obter mais informações sobre solicitações de IOCTL_GPIO_READ_PINS , incluindo o mapeamento de pinos de entrada de dados para os bits no buffer de saída da solicitação, consulte IOCTL_GPIO_READ_PINS. Para obter mais informações sobre IOCTL_GPIO_WRITE_PINS solicitações, incluindo o mapeamento dos bits no buffer de entrada de solicitação para pinos de saída de dados, consulte IOCTL_GPIO_WRITE_PINS.

Para obter um driver de exemplo que mostra como escrever um driver periférico GPIO executado no modo kernel, consulte o driver de exemplo SimDevice na coleção de drivers de exemplo GPIO no GitHub.