функция обратного вызова EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE (wdfdmatransaction.h)
[Применяется только к KMDF]
Функция обратного вызова события EvtDmaTransactionDmaTransferComplete драйвера вызывается, когда контроллер системного режима завершил текущую передачу DMA.
Синтаксис
EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE EvtWdfDmaTransactionDmaTransferComplete;
void EvtWdfDmaTransactionDmaTransferComplete(
[in] WDFDMATRANSACTION Transaction,
[in] WDFDEVICE Device,
[in] WDFCONTEXT Context,
[in] WDF_DMA_DIRECTION Direction,
[in] DMA_COMPLETION_STATUS Status
)
{...}
Параметры
[in] Transaction
Дескриптор объекта транзакции DMA, представляющий только что завершенную передачу DMA .
[in] Device
Дескриптор объекта устройства платформы, указанного драйвером при вызове WdfDmaTransactionCreate.
[in] Context
Указатель контекста, указанный драйвером в предыдущем вызове WdfDmaTransactionSetTransferCompleteCallback.
[in] Direction
WDF_DMA_DIRECTION типизированное значение, указывающее направление завершения операции передачи DMA.
[in] Status
DMA_COMPLETION_STATUS типизированное значение, указывающее состояние передачи.
Возвращаемое значение
None
Remarks
Оборудование для устройства DMA с master шины обычно прерывает передачу данных. Затем драйвер завершает передачу DMA в функции обратного вызова EvtInterruptDpc .
Однако оборудование для устройства DMA в системном режиме не всегда сообщает о завершении передачи DMA, выдавая прерывание. Чтобы получить уведомление о завершении передачи DMA, драйвер для устройства DMA в системном режиме может вместо этого зарегистрировать функцию обратного вызова события EvtDmaTransactionDmaTransferComplete , вызвав WdfDmaTransactionSetTransferCompleteCallback.
Платформа вызывает EvtDmaTransactionDmaTransferComplete после завершения передачи контроллером DMA системы, один раз для каждой передачи в транзакции.
В обратном вызове EvtDmaTransactionDmaTransferComplete драйвер может вызвать следующие методы, чтобы уведомить платформу о завершении передачи:
WdfDmaTransactionDmaCompletedWdfDmaTransactionDmaCompletedFinalWdfDmaTransactionDmaCompletedWithLength . Драйвер может не вызывать один из предыдущих методов из EvtDmaTransactionDmaTransferComplete, выбрав вместо этого создать объект таймера или запланировать DPC для завершения передачи позже, если потребуется. После того как WdfDmaTransactionDmaCompletedXxx возвращает значение TRUE, указывая, что для завершения транзакции DMA больше не требуется передача данных, драйвер может при необходимости вызвать WdfDmaTransactionExecute , чтобы инициировать последующую транзакцию.
Если драйвер вызывает WdfDmaTransactionStopSystemTransfer, платформа вызывает EvtDmaTransactionDmaTransferComplete со значением СостоянияDmaCancelled. В этом случае драйвер должен вызвать WdfDmaTransactionDmaCompletedFinal из evtDmaTransactionDmaTransferComplete, а затем может продолжить обработку запроса.
Драйвер не должен управлять буферами данных, связанными с транзакцией, до тех пор, пока WdfDmaTransactionDmaCompletedXxx не вернет значение TRUE.
Драйвер может вызвать WdfDmaTransactionRelease из evtDmaTransactionDmaTransferComplete , если ему нужно завершить транзакцию DMA.
Дополнительные сведения о системном режиме DMA см. в разделе Поддержка System-Mode DMA.
Примеры
Чтобы определить функцию обратного вызова EvtDmaTransactionDmaTransferComplete , необходимо сначала предоставить объявление функции, определяющее тип определяемой функции обратного вызова. Windows предоставляет набор типов функций обратного вызова для драйверов. Объявление функции с помощью типов функций обратного вызова помогает анализу кода для драйверов, средству проверки статических драйверов (SDV) и другим средствам проверки находить ошибки, и это требование для написания драйверов для операционной системы Windows.
Например, чтобы определить функцию обратного вызова EvtDmaTransactionDmaTransferComplete с именем MyDmaTransactionDmaTransferComplete, используйте тип EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE , как показано в следующем примере кода:
EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE MyDmaTransactionDmaTransferComplete;
Затем реализуйте функцию обратного вызова следующим образом.
_Use_decl_annotations_
VOID
MyDmaTransactionDmaTransferComplete(
WDFDMATRANSACTION Transaction,
WDFDEVICE /* Device */,
WDFCONTEXT Context,
WDF_DMA_DIRECTION /* Direction */,
DMA_COMPLETION_STATUS DmaStatus
)
{
PREQUEST_CONTEXT requestContext = (PREQUEST_CONTEXT) Context;
NTSTATUS requestStatus;
bool overrideStatus = true;
size_t bytesTransferred;
if (DmaStatus == DmaComplete) {
//
// Normal transfer completion. Indicate this to the framework and see
// if there's more work to do.
//
if (WdfDmaTransactionDmaCompleted(Transaction, &requestStatus) == FALSE) {
//
// There are more DMA transfers to come. The transaction
// may already have been completed on another processor.
// Return without touching it again.
//
goto exit;
}
requestStatus = STATUS_SUCCESS;
}
else {
//
// Complete the entire transaction. But throw out the status and
// use one derived from the DmaStatus.
//
WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &requestStatus);
//
// Error or cancellation. Indicate that this was the final transfer to
// the framework.
//
if (DmaStatus == DmaError) {
requestStatus = STATUS_DEVICE_DATA_ERROR;
}
else {
//
// Cancel status should only be triggered by timeout or cancel. Rely on
// someone having already set the status, which means we should lose
// the race for BeginCompletion below.
//
requestStatus = STATUS_PENDING;
overrideStatus = false;
}
}
//
// Begin completion. There's nothing special to do here if cancel or
// timeout got there first.
//
BeginCompletion(requestContext, requestStatus, overrideStatus);
//
// Record the number of bytes we transferred.
//
bytesTransferred = WdfDmaTransactionGetBytesTransferred(
requestContext->DmaTransaction
);
WdfRequestSetInformation(requestContext->Request, bytesTransferred);
//
// Success, error or cancel, this was the last transfer in the
// transaction. Attempt to complete the request.
//
AttemptRequestCompletion(requestContext, true);
exit:
return;
}
bool
BeginCompletion(
__in PREQUEST_CONTEXT RequestContext,
__in NTSTATUS CompletionStatus,
__in bool ForceStatusUpdate
)
{
bool completionStarted;
//
// Grab the object lock and mark the beginning of
// completion.
//
WdfSpinLockAcquire(RequestContext->Lock);
completionStarted = RequestContext->CompletionStarted;
RequestContext->CompletionStarted = true;
if ((completionStarted == false) ||
(ForceStatusUpdate == true)) {
RequestContext->CompletionStatus = CompletionStatus;
}
WdfSpinLockRelease(RequestContext->Lock);
return !completionStarted;
}
VOID
AttemptRequestCompletion(
__in PREQUEST_CONTEXT RequestContext,
__in bool TransferComplete
)
{
LONG refCount;
NT_ASSERTMSG("No thread has begun completion",
RequestContext->CompletionStarted == true);
if (TransferComplete) {
//
// Unmark the request cancelable. If that succeeds then drop the cancel reference
//
if (WdfRequestUnmarkCancelable(RequestContext->Request) == STATUS_SUCCESS) {
refCount = InterlockedDecrement(&(RequestContext->CompletionRefCount));
NT_ASSERTMSGW(L"Reference count should not have gone to zero yet",
refCount != 0);
}
//
// Stop the timer if it's been started.
//
if (RequestContext->TimerStarted == true) {
if (WdfTimerStop(RequestContext->Timer, FALSE) == TRUE) {
//
// The timer was queued but will never run. Drop its
// reference count.
//
refCount = InterlockedDecrement(&RequestContext->CompletionRefCount);
NT_ASSERTMSG("Completion reference count should not reach zero until "
L"this routine calls AttemptRequestCompletion",
refCount > 0);
}
}
}
//
// Drop this caller's reference. If that was the last one then
// complete the request.
//
refCount = InterlockedDecrement(&(RequestContext->CompletionRefCount));
if (refCount == 0) {
NT_ASSERTMSGW(L"Execution reference was released, but execution "
L"path did not set a completion status for the "
L"request",
RequestContext->CompletionStatus != STATUS_PENDING);
//
// Timers are disposed of at passive level. If we leave it attached to
// the request then we can hit a verifier issue, since the request
// needs to be immediately disposable at dispatch-level.
//
// Delete the timer now so that we can complete the request safely.
// At this point the timer has either expired or been successfully
// cancelled so there's no race with the timer routine.
//
if (RequestContext->Timer != NULL) {
WdfObjectDelete(RequestContext->Timer);
RequestContext->Timer = NULL;
}
WdfRequestComplete(RequestContext->Request,
RequestContext->CompletionStatus);
}
}
Тип функции EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE определен в файле заголовка WdfDmaTransaction.h. Чтобы более точно определить ошибки при запуске средств анализа кода, не забудьте добавить заметку Use_decl_annotations в определение функции. Заметка Use_decl_annotations гарантирует использование заметок, которые применяются к типу функции EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE в файле заголовка. Дополнительные сведения о требованиях к объявлениям функций см. в статье Объявление функций с помощью типов ролей функций для драйверов KMDF. Сведения о Use_decl_annotations см. в статье Поведение функции с заметками.
Требования
Требование | Значение |
---|---|
Минимальная версия клиента | Windows 8 |
Целевая платформа | Универсальное |
Минимальная версия KMDF | 1.11 |
Верхняя часть | wdfdmatransaction.h (включая Wdf.h) |
IRQL | DISPATCH_LEVEL |