Compartir a través de


Programación de hardware DMA

[Solo se aplica a KMDF]

En este tema se describe la funcionalidad que suele proporcionar un controlador KMDF para un dispositivo DMA maestro de bus en su función de devolución de llamada de eventos EvtProgramDma . Si el controlador usa la compatibilidad con DMA del marco, el controlador debe proporcionar esta devolución de llamada. Esta información también se aplica a un controlador KMDF para un dispositivo DMA en modo sistema que tiene una interrupción de hardware.

La función de devolución de llamada EvtProgramDma , que se llama en IRQL = DISPATCH_LEVEL, programa el dispositivo para iniciar una transferencia DMA. Los parámetros de entrada de esta función de devolución de llamada proporcionan la dirección de la transferencia (entrada o salida) y una lista de dispersión y recopilación. Si la transferencia consta de un solo paquete, la lista de dispersión y recopilación contiene un único elemento.

La función de devolución de llamada EvtProgramDma programa el dispositivo mediante los recursos de hardware recibidos por la función de devolución de llamada EvtDevicePrepareHardware del controlador. Si la función de devolución de llamada EvtProgramDma programa correctamente el hardware, devuelve TRUE.

Una vez que el hardware haya completado la transferencia DMA, normalmente el hardware emite una interrupción y el sistema llama a la función de devolución de llamada EvtInterruptIsr del controlador. La función de devolución de llamada EvtInterruptIsr del controlador normalmente:

  • Borra la interrupción del hardware.

  • Guarda la información de contexto de la interrupción si es necesario. Esta información puede perderse después de que la función de devolución de llamada se devuelva y el sistema reduzca el IRQL (ya que reducir IRQL permite que se produzcan interrupciones adicionales).

  • Llama a WdfInterruptQueueDpcForIsr para programar una función de devolución de llamada EvtInterruptDpc .

La función de devolución de llamada EvtInterruptDpccompleta la transferencia de DMA mediante la información de contexto que guardó la función de devolución de llamada EvtInterruptIsr .

Si la función de devolución de llamada EvtProgramDma detecta un error, el controlador puede detener la transacción.

Para detener una transacción cuando el controlador detecta un error, la función de devolución de llamada EvtProgramDma debe:

  1. Llame a WdfDmaTransactionDmaCompletedFinal.

  2. Llame a WdfObjectDelete para eliminar el objeto de transacción DMA o llame a WdfDmaTransactionRelease para liberar y reutilizar el objeto de transacción DMA.

  3. Vuelva a poner en cola la solicitud de E/S o complete la solicitud de E/S, si la transacción está asociada a un objeto de solicitud de marco. Para recuperar un identificador de la solicitud, el controlador puede llamar a WdfDmaTransactionGetRequest.

  4. Devuelve FALSE.

Los pasos 1 y 4 se muestran en el ejemplo de código siguiente, tomados de la función de devolución de llamada EvtProgramDma del ejemplo PLX9x5x para las solicitudes de lectura en el archivo Read.c.

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

En el ejemplo se llama a la función PLxReadRequestComplete para realizar los pasos 2 y 3:

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

}