Verbinden eines KMDF-Treibers mit GPIO-E/A-Pins

Eine GPIO-E/A-Ressource ist ein Satz von mindestens einem GPIO-Pin, die als Dateneingaben oder Datenausgaben konfiguriert sind. Der Treiber für ein Peripheriegerät, das physisch eine Verbindung mit diesen Pins herstellt, ruft die entsprechende GPIO-E/A-Ressource vom Betriebssystem ab. Der Treiber für Peripheriegeräte öffnet eine Verbindung mit den GPIO-Pins in dieser Ressource und sendet E/A-Anforderungen an das Handle, das diese Verbindung darstellt.

Das folgende Codebeispiel zeigt, wie der Kernelmodustreiberframeworktreiber (KMDF) für ein Peripheriegerät eine Beschreibung der GPIO-E/A-Ressource abrufen kann, die der PnP-Manager (Plug & Play) dem Treiber zugewiesen hat.

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;

        ...

}

Im vorherigen Codebeispiel ist die DeviceExtension Variable ein Zeiger auf den Gerätekontext für das Peripheriegerät. Die XyzDrvGetDeviceExtension Funktion, die diesen Gerätekontext abruft, wird vom Treiber für Peripheriegeräte implementiert. Dieser Treiber hat zuvor seine EvtDevicePrepareHardware-Rückruffunktion registriert, indem er die WdfDeviceInitSetPnpPowerEventCallbacks-Methode aufruft .

Das folgende Codebeispiel zeigt, wie der Treiber für Peripheriegeräte die GPIO-Ressourcenbeschreibung verwenden kann, die er im vorherigen Codebeispiel erhalten hat, um ein WDFIOTARGET-Handle für die GPIO-E/A-Ressource des Treibers zu öffnen.

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

Im vorherigen Codebeispiel ist die Device Variable ein WDFDEVICE-Handle für das Frameworkgeräteobjekt für das Peripheriegerät. Die RESOURCE_HUB_CREATE_PATH_FROM_ID-Funktion erstellt eine Zeichenfolge, die den Namen der GPIO-E/A-Ressource enthält. Im Codebeispiel wird diese Zeichenfolge verwendet, um die GPIO-E/A-Ressource nach Name zu öffnen.

Nachdem der Treiber des Peripheriegeräts ein Handle für eine GPIO-E/A-Ressource abgerufen hat, kann dieser Treiber E/A-Steuerungsanforderungen senden, um Daten aus den GPIO-Pins zu lesen oder Daten in die GPIO-Pins zu schreiben. Ein Treiber, der eine GPIO-E/A-Ressource für Lesevorgänge öffnet, verwendet IOCTL_GPIO_READ_PINS E/A-Steuerelementanforderungen, um Daten von den Pins in der Ressource zu lesen. Ein Treiber, der eine GPIO-E/A-Ressource für Schreibvorgänge öffnet, verwendet IOCTL_GPIO_WRITE_PINS E/A-Steuerelementanforderungen, um Daten in die Pins in der Ressource zu schreiben. Das folgende Codebeispiel zeigt, wie Sie einen GPIO-Lese- oder Schreibvorgang ausführen.

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

    ...

Im vorherigen Codebeispiel Data ist ein Zeiger auf einen Datenpuffer, Size gibt die Größe dieses Datenpuffers in Bytes an und ReadOperation gibt an, ob der angeforderte Vorgang ein Lesevorgang (TRUE) oder ein Schreibvorgang (FALSE) ist.

Weitere Informationen finden Sie unter

Weitere Informationen zu IOCTL_GPIO_READ_PINS Anforderungen, einschließlich der Zuordnung von Dateneingabepins zu den Bits im Anforderungsausgabepuffer, finden Sie unter IOCTL_GPIO_READ_PINS. Weitere Informationen zu IOCTL_GPIO_WRITE_PINS Anforderungen, einschließlich der Zuordnung der Bits im Anforderungseingabepuffer zu Datenausgabepins, finden Sie unter IOCTL_GPIO_WRITE_PINS.

Einen Beispieltreiber, der zeigt, wie ein GPIO-Peripherietreiber geschrieben wird, der im Kernelmodus ausgeführt wird, finden Sie im SimDevice-Beispieltreiber in der GPIO-Beispieltreibersammlung auf GitHub.