Programmieren von DMA-Hardware

[Gilt nur für KMDF]

In diesem Thema wird die Funktionalität beschrieben, die ein KMDF-Treiber für ein Bus-master DMA-Gerät in der Regel in seiner EvtProgramDma-Ereignisrückruffunktion bereitstellt. Wenn Ihr Treiber die DMA-Unterstützung des Frameworks verwendet, muss der Treiber diesen Rückruf bereitstellen. Diese Informationen gelten auch für einen KMDF-Treiber für ein DMA-Gerät im Systemmodus , das über einen Hardwareunterbrechung verfügt.

Die Rückruffunktion EvtProgramDma , die unter IRQL = DISPATCH_LEVEL aufgerufen wird, programmiert das Gerät, um eine DMA-Übertragung zu starten. Die Eingabeparameter für diese Rückruffunktion geben die Richtung der Übertragung (Eingabe oder Ausgabe) und eine Punkt-/Sammlungsliste an. Wenn die Übertragung aus einem einzelnen Paket besteht, enthält die Scatter/Gather-Liste ein einzelnes Element.

Die EvtProgramDma-Rückruffunktion programmiert das Gerät mithilfe der Hardwareressourcen, die die Rückruffunktion EvtDevicePrepareHardware des Treibers empfangen hat. Wenn die EvtProgramDma-Rückruffunktion die Hardware erfolgreich programmiert, gibt sie TRUE zurück.

Nachdem die Hardware die DMA-Übertragung abgeschlossen hat, gibt die Hardware in der Regel einen Interrupt aus, und das System ruft die EvtInterruptIsr-Rückruffunktion des Treibers auf. Die EvtInterruptIsr-Rückruffunktion des Treibers in der Regel:

  • Löscht den Hardwareunterbrechung.

  • Speichert die Kontextinformationen des Interrupts, falls erforderlich. Diese Informationen gehen möglicherweise verloren, nachdem die Rückruffunktion zurückgegeben wird und das System die IRQL herabgesetzt hat (da das Senken des IRQL zusätzliche Unterbrechungen zulässt).

  • Ruft WdfInterruptQueueDpcForIsr auf, um eine EvtInterruptDpc-Rückruffunktion zu planen.

Die EvtInterruptDpc-Rückruffunktionschließt die DMA-Übertragung mithilfe von Kontextinformationen ab, die von der EvtInterruptIsr-Rückruffunktion gespeichert wurden.

Wenn die EvtProgramDma-Rückruffunktion einen Fehler erkennt, kann der Treiber die Transaktion beenden.

Um eine Transaktion zu beenden, wenn der Treiber einen Fehler erkennt, muss die Rückruffunktion EvtProgramDma :

  1. Rufen Sie WdfDmaTransactionDmaCompletedFinal auf.

  2. Rufen Sie WdfObjectDelete auf, um das DMA-Transaktionsobjekt zu löschen, oder rufen Sie WdfDmaTransactionRelease auf, um das DMA-Transaktionsobjekt freizugeben und wiederzuverwenden.

  3. Stellen Sie die E/A-Anforderung erneut in die Warteschlange, oder schließen Sie die E/A-Anforderung ab, wenn die Transaktion einem Frameworkanforderungsobjekt zugeordnet ist. Um ein Handle für die Anforderung abzurufen, kann der Treiber WdfDmaTransactionGetRequest aufrufen.

  4. Gibt FALSE zurück.

Die Schritte 1 und 4 werden im folgenden Codebeispiel veranschaulicht, das aus der EvtProgramDma-Rückruffunktion des PLX9x-Beispiels für Leseanforderungen in der Datei Read.c stammt.

    // If errors occur in the EvtProgramDma callback,
    // release the DMA transaction object and complete the request.

    if (errors) {
        NTSTATUS status;

        //
        // Must abort the transaction before deleting.
        //
        (VOID) WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &status);
        ASSERT(NT_SUCCESS(status));

        PLxReadRequestComplete( Transaction, STATUS_INVALID_DEVICE_STATE );
        TraceEvents(TRACE_LEVEL_ERROR, DBG_READ,
                    "<-- PLxEvtProgramReadDma: errors ****");
        return FALSE;
    }

Im Beispiel wird die PLxReadRequestComplete-Funktion aufgerufen, um die Schritte 2 und 3 auszuführen:

VOID
PLxReadRequestComplete(
    IN WDFDMATRANSACTION  DmaTransaction,
    IN NTSTATUS           Status
    )
/*++

Routine Description:

Arguments:

Return Value:

--*/
{
    WDFREQUEST         request;
    size_t             bytesTransferred;

    //
    // Get the associated request from the transaction.
    //
    request = WdfDmaTransactionGetRequest(DmaTransaction);

    ASSERT(request);

    //
    // Get the final bytes transferred count.
    //
    bytesTransferred =  WdfDmaTransactionGetBytesTransferred( DmaTransaction );

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_DPC,
                "PLxReadRequestComplete:  Request %p, Status %!STATUS!, "
                "bytes transferred %d\n",
                 request, Status, (int) bytesTransferred );

    WdfDmaTransactionRelease(DmaTransaction);

    //
    // Complete this Request.
    //
    WdfRequestCompleteWithInformation( request, Status, bytesTransferred);

}