mirror of
https://github.com/reactos/reactos.git
synced 2024-08-27 21:58:49 +00:00
1468 lines
51 KiB
C
1468 lines
51 KiB
C
/*--
|
|
|
|
Copyright (C) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
scratch.c
|
|
|
|
Abstract:
|
|
|
|
Functions for using common scratch buffer
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "stddef.h"
|
|
#include "string.h"
|
|
|
|
#include "ntddk.h"
|
|
#include "ntddstor.h"
|
|
#include "cdrom.h"
|
|
#include "ioctl.h"
|
|
#include "scratch.h"
|
|
#include "mmc.h"
|
|
|
|
#ifdef DEBUG_USE_WPP
|
|
#include "scratch.tmh"
|
|
#endif
|
|
|
|
// Forward declarations
|
|
EVT_WDF_REQUEST_COMPLETION_ROUTINE ScratchBuffer_ReadWriteCompletionRoutine;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, ScratchBuffer_Deallocate)
|
|
#pragma alloc_text(PAGE, ScratchBuffer_Allocate)
|
|
#pragma alloc_text(PAGE, ScratchBuffer_SetupSrb)
|
|
#pragma alloc_text(PAGE, ScratchBuffer_ExecuteCdbEx)
|
|
|
|
#endif
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
VOID
|
|
ScratchBuffer_Deallocate(
|
|
_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
release all resources allocated for scratch.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE ();
|
|
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0);
|
|
|
|
if (DeviceExtension->ScratchContext.ScratchHistory != NULL)
|
|
{
|
|
ExFreePool(DeviceExtension->ScratchContext.ScratchHistory);
|
|
DeviceExtension->ScratchContext.ScratchHistory = NULL;
|
|
}
|
|
if (DeviceExtension->ScratchContext.ScratchSense != NULL)
|
|
{
|
|
ExFreePool(DeviceExtension->ScratchContext.ScratchSense);
|
|
DeviceExtension->ScratchContext.ScratchSense = NULL;
|
|
}
|
|
if (DeviceExtension->ScratchContext.ScratchSrb != NULL)
|
|
{
|
|
ExFreePool(DeviceExtension->ScratchContext.ScratchSrb);
|
|
DeviceExtension->ScratchContext.ScratchSrb = NULL;
|
|
}
|
|
if (DeviceExtension->ScratchContext.ScratchBufferSize != 0)
|
|
{
|
|
DeviceExtension->ScratchContext.ScratchBufferSize = 0;
|
|
}
|
|
if (DeviceExtension->ScratchContext.ScratchBufferMdl != NULL)
|
|
{
|
|
IoFreeMdl(DeviceExtension->ScratchContext.ScratchBufferMdl);
|
|
DeviceExtension->ScratchContext.ScratchBufferMdl = NULL;
|
|
}
|
|
if (DeviceExtension->ScratchContext.ScratchBuffer != NULL)
|
|
{
|
|
ExFreePool(DeviceExtension->ScratchContext.ScratchBuffer);
|
|
DeviceExtension->ScratchContext.ScratchBuffer = NULL;
|
|
}
|
|
|
|
if (DeviceExtension->ScratchContext.PartialMdl != NULL)
|
|
{
|
|
IoFreeMdl(DeviceExtension->ScratchContext.PartialMdl);
|
|
DeviceExtension->ScratchContext.PartialMdl = NULL;
|
|
}
|
|
|
|
if (DeviceExtension->ScratchContext.ScratchRequest != NULL)
|
|
{
|
|
PIRP irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
|
|
if (irp->MdlAddress)
|
|
{
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
WdfObjectDelete(DeviceExtension->ScratchContext.ScratchRequest);
|
|
DeviceExtension->ScratchContext.ScratchRequest = NULL;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
BOOLEAN
|
|
ScratchBuffer_Allocate(
|
|
_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
allocate resources allocated for scratch.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE ();
|
|
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse == 0);
|
|
|
|
// quick-exit if already allocated
|
|
if ((DeviceExtension->ScratchContext.ScratchBuffer != NULL) &&
|
|
(DeviceExtension->ScratchContext.ScratchBufferMdl != NULL) &&
|
|
(DeviceExtension->ScratchContext.ScratchBufferSize != 0) &&
|
|
(DeviceExtension->ScratchContext.ScratchRequest != NULL) &&
|
|
(DeviceExtension->ScratchContext.ScratchSrb != NULL) &&
|
|
(DeviceExtension->ScratchContext.ScratchHistory != NULL) &&
|
|
(DeviceExtension->ScratchContext.PartialMdl != NULL)
|
|
)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// validate max transfer already determined
|
|
NT_ASSERT(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes != 0);
|
|
|
|
// validate no partially-saved state
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer == NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl == NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize == 0);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest == NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl == NULL);
|
|
|
|
// limit the scratch buffer to between 4k and 64k (so data length fits into USHORT -- req'd for many commands)
|
|
DeviceExtension->ScratchContext.ScratchBufferSize = min(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, (64*1024));
|
|
|
|
// allocate the buffer
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceExtension->ScratchContext.ScratchBuffer = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
DeviceExtension->ScratchContext.ScratchBufferSize,
|
|
CDROM_TAG_SCRATCH);
|
|
if (DeviceExtension->ScratchContext.ScratchBuffer == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate scratch buffer of %x bytes\n",
|
|
DeviceExtension->ScratchContext.ScratchBufferSize
|
|
));
|
|
}
|
|
else if (BYTE_OFFSET(DeviceExtension->ScratchContext.ScratchBuffer) != 0)
|
|
{
|
|
status = STATUS_INTERNAL_ERROR;
|
|
TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_INIT,
|
|
"Allocation of %x bytes non-paged pool was not "
|
|
"allocated on page boundary? STATUS_INTERNAL_ERROR\n",
|
|
DeviceExtension->ScratchContext.ScratchBufferSize
|
|
));
|
|
}
|
|
}
|
|
|
|
// allocate the MDL
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceExtension->ScratchContext.ScratchBufferMdl = IoAllocateMdl(DeviceExtension->ScratchContext.ScratchBuffer,
|
|
DeviceExtension->ScratchContext.ScratchBufferSize,
|
|
FALSE, FALSE, NULL);
|
|
if (DeviceExtension->ScratchContext.ScratchBufferMdl == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate MDL for %x byte buffer\n",
|
|
DeviceExtension->ScratchContext.ScratchBufferSize
|
|
));
|
|
}
|
|
else
|
|
{
|
|
MmBuildMdlForNonPagedPool(DeviceExtension->ScratchContext.ScratchBufferMdl);
|
|
}
|
|
}
|
|
|
|
// create the request
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
WDF_OBJECT_ATTRIBUTES attributes;
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
|
|
CDROM_REQUEST_CONTEXT);
|
|
|
|
status = WdfRequestCreate(&attributes,
|
|
DeviceExtension->IoTarget,
|
|
&DeviceExtension->ScratchContext.ScratchRequest);
|
|
|
|
if ((!NT_SUCCESS(status)) ||
|
|
(DeviceExtension->ScratchContext.ScratchRequest == NULL))
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate scratch MDL \n"));
|
|
}
|
|
}
|
|
|
|
// allocate the srb
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceExtension->ScratchContext.ScratchSrb = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
sizeof(SCSI_REQUEST_BLOCK),
|
|
CDROM_TAG_SCRATCH);
|
|
|
|
if (DeviceExtension->ScratchContext.ScratchSrb == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate scratch SRB\n"));
|
|
}
|
|
}
|
|
|
|
// allocate the sense buffer
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceExtension->ScratchContext.ScratchSense = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
sizeof(SENSE_DATA),
|
|
CDROM_TAG_SCRATCH);
|
|
|
|
if (DeviceExtension->ScratchContext.ScratchSense == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate scratch sense data\n"
|
|
));
|
|
}
|
|
}
|
|
|
|
// allocate the SRB history data
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
size_t allocationSize = sizeof(SRB_HISTORY) - sizeof(SRB_HISTORY_ITEM);
|
|
allocationSize += 20 * sizeof(SRB_HISTORY_ITEM);
|
|
|
|
DeviceExtension->ScratchContext.ScratchHistory = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
allocationSize,
|
|
CDROM_TAG_SCRATCH);
|
|
if (DeviceExtension->ScratchContext.ScratchHistory == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate scratch history buffer\n"
|
|
));
|
|
}
|
|
else
|
|
{
|
|
// must be initialized here...
|
|
RtlZeroMemory(DeviceExtension->ScratchContext.ScratchHistory, allocationSize);
|
|
DeviceExtension->ScratchContext.ScratchHistory->TotalHistoryCount = 20;
|
|
}
|
|
}
|
|
|
|
// allocate the MDL
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
ULONG transferLength = 0;
|
|
|
|
status = RtlULongAdd(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes, PAGE_SIZE, &transferLength);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceExtension->ScratchContext.PartialMdlIsBuilt = FALSE;
|
|
DeviceExtension->ScratchContext.PartialMdl = IoAllocateMdl(NULL,
|
|
transferLength,
|
|
FALSE,
|
|
FALSE,
|
|
NULL);
|
|
if (DeviceExtension->ScratchContext.PartialMdl == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate MDL for %x byte buffer\n",
|
|
DeviceExtension->ScratchContext.ScratchBufferSize
|
|
));
|
|
}
|
|
else
|
|
{
|
|
NT_ASSERT(DeviceExtension->ScratchContext.PartialMdl->Size >=
|
|
(CSHORT)(sizeof(MDL) + BYTES_TO_PAGES(DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes) * sizeof(PFN_NUMBER)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_INTEGER_OVERFLOW;
|
|
}
|
|
}
|
|
|
|
// cleanup on failure
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ScratchBuffer_Deallocate(DeviceExtension);
|
|
}
|
|
|
|
return NT_SUCCESS(status);
|
|
}
|
|
|
|
|
|
VOID
|
|
ScratchBuffer_ResetItems(
|
|
_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ BOOLEAN ResetRequestHistory
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
reset scratch items for reuse.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
ResetRequestHistory - reset history fields or not
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
WDF_REQUEST_REUSE_PARAMS reuseParams;
|
|
PIRP irp = NULL;
|
|
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchHistory != NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchSense != NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchSrb != NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchRequest != NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferSize != 0);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchBuffer != NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchBufferMdl != NULL);
|
|
NT_ASSERT(DeviceExtension->ScratchContext.ScratchInUse != 0);
|
|
|
|
irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
|
|
|
|
if (ResetRequestHistory)
|
|
{
|
|
PSRB_HISTORY history = DeviceExtension->ScratchContext.ScratchHistory;
|
|
RtlZeroMemory(history->History, sizeof(SRB_HISTORY_ITEM) * history->TotalHistoryCount);
|
|
history->ClassDriverUse[0] = 0;
|
|
history->ClassDriverUse[1] = 0;
|
|
history->ClassDriverUse[2] = 0;
|
|
history->ClassDriverUse[3] = 0;
|
|
history->UsedHistoryCount = 0;
|
|
}
|
|
|
|
// re-use the KMDF request object
|
|
|
|
// deassign the MdlAddress, this is the value we assign explicitly.
|
|
// this is to prevent WdfRequestReuse to release the Mdl unexpectly.
|
|
if (irp->MdlAddress)
|
|
{
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
|
|
WDF_REQUEST_REUSE_PARAMS_INIT(&reuseParams, WDF_REQUEST_REUSE_NO_FLAGS, STATUS_NOT_SUPPORTED);
|
|
status = WdfRequestReuse(DeviceExtension->ScratchContext.ScratchRequest, &reuseParams);
|
|
// WDF request to format the request befor sending it
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// clean up completion routine.
|
|
WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest, NULL, NULL);
|
|
|
|
status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
|
|
DeviceExtension->ScratchContext.ScratchRequest,
|
|
IOCTL_SCSI_EXECUTE_IN,
|
|
NULL, NULL,
|
|
NULL, NULL,
|
|
NULL, NULL);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
|
"ScratchBuffer_ResetItems: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
|
|
status));
|
|
}
|
|
}
|
|
|
|
RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSense, sizeof(SENSE_DATA));
|
|
RtlZeroMemory(DeviceExtension->ScratchContext.ScratchSrb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ScratchBuffer_PerformNextReadWrite(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ BOOLEAN FirstTry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function asynchronously sends the next read/write SRB down the stack.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Device extension
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = &DeviceExtension->ScratchContext.ScratchReadWriteContext;
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
|
|
WDFREQUEST originalRequest = requestContext->OriginalRequest;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
ULONG transferSize;
|
|
BOOLEAN usePartialMdl;
|
|
|
|
transferSize = min((readWriteContext->EntireXferLen - readWriteContext->TransferedBytes), readWriteContext->MaxLength);
|
|
|
|
if (FirstTry)
|
|
{
|
|
DeviceExtension->ScratchContext.NumRetries = 0;
|
|
}
|
|
|
|
ScratchBuffer_ResetItems(DeviceExtension, FALSE);
|
|
|
|
usePartialMdl = (readWriteContext->PacketsCount > 1 || readWriteContext->TransferedBytes > 0);
|
|
|
|
ScratchBuffer_SetupReadWriteSrb(DeviceExtension,
|
|
originalRequest,
|
|
readWriteContext->StartingOffset,
|
|
transferSize,
|
|
readWriteContext->DataBuffer,
|
|
readWriteContext->IsRead,
|
|
usePartialMdl
|
|
);
|
|
|
|
WdfRequestSetCompletionRoutine(DeviceExtension->ScratchContext.ScratchRequest,
|
|
ScratchBuffer_ReadWriteCompletionRoutine, DeviceExtension);
|
|
|
|
status = ScratchBuffer_SendSrb(DeviceExtension, FALSE, (FirstTry ? &readWriteContext->SrbHistoryItem : NULL));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
ScratchBuffer_ReadWriteTimerRoutine(
|
|
struct _KDPC *Dpc,
|
|
PVOID DeferredContext,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Timer routine for retrying read and write requests.
|
|
|
|
Arguments:
|
|
|
|
Timer - WDF timer
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
|
|
PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = NULL;
|
|
WDFREQUEST originalRequest = NULL;
|
|
PCDROM_REQUEST_CONTEXT requestContext = NULL;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
KIRQL oldIrql;
|
|
|
|
UNREFERENCED_PARAMETER(Dpc);
|
|
UNREFERENCED_PARAMETER(SystemArgument1);
|
|
UNREFERENCED_PARAMETER(SystemArgument2);
|
|
|
|
if (DeferredContext == NULL)
|
|
{
|
|
// This is impossible, but definition of KDEFERRED_ROUTINE allows optional argument,
|
|
// and thus OACR will complain.
|
|
|
|
return;
|
|
}
|
|
|
|
originalRequest = (WDFREQUEST) DeferredContext;
|
|
requestContext = RequestGetContext(originalRequest);
|
|
|
|
KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
|
|
|
|
if (!requestContext->ReadWriteIsCompleted)
|
|
{
|
|
// As the first step, unregister the cancellation routine
|
|
status = WdfRequestUnmarkCancelable(originalRequest);
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
|
|
KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
|
|
|
|
if (status != STATUS_CANCELLED)
|
|
{
|
|
deviceExtension = requestContext->DeviceExtension;
|
|
readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
|
|
|
|
// We use timer only for retries, that's why the second parameter is always FALSE
|
|
status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
ScratchBuffer_EndUse(deviceExtension);
|
|
RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Drop the extra reference
|
|
//
|
|
WdfObjectDereference(originalRequest);
|
|
}
|
|
|
|
|
|
EVT_WDF_REQUEST_CANCEL ScratchBuffer_ReadWriteEvtRequestCancel;
|
|
|
|
VOID
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
ScratchBuffer_ReadWriteEvtRequestCancel(
|
|
_In_ WDFREQUEST Request
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cancels a request waiting for the read/write timer to expire. This function does not
|
|
support cancellation of requests that have already been sent down.
|
|
|
|
Arguments:
|
|
|
|
Request - WDF request
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
|
PCDROM_DEVICE_EXTENSION deviceExtension = requestContext->DeviceExtension;
|
|
PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
|
|
|
|
if (KeCancelTimer(&requestContext->ReadWriteTimer))
|
|
{
|
|
//
|
|
// Timer is canceled, we own the request. Drop the reference we took before
|
|
// queueing the timer.
|
|
//
|
|
WdfObjectDereference(Request);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Timer will run and drop the reference but it won't complete the request
|
|
// because we set IsCompleted to TRUE
|
|
//
|
|
}
|
|
|
|
requestContext->ReadWriteIsCompleted = TRUE;
|
|
|
|
KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
|
|
|
|
ScratchBuffer_EndUse(deviceExtension);
|
|
|
|
// If WdfTimerStop returned TRUE, it means this request was scheduled for a retry
|
|
// and the retry has not happened yet. We just need to cancel it and release the scratch buffer.
|
|
RequestCompletion(deviceExtension, Request, STATUS_CANCELLED, readWriteContext->TransferedBytes);
|
|
}
|
|
|
|
VOID
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
ScratchBuffer_ReadWriteCompletionRoutine(
|
|
_In_ WDFREQUEST Request,
|
|
_In_ WDFIOTARGET Target,
|
|
_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
|
|
_In_ WDFCONTEXT Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read/write request completion routine.
|
|
|
|
Arguments:
|
|
Request - WDF request
|
|
Target - The IO target the request was completed by.
|
|
Params - the request completion parameters
|
|
Context - context
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PCDROM_DEVICE_EXTENSION deviceExtension = (PCDROM_DEVICE_EXTENSION) Context;
|
|
PCDROM_SCRATCH_READ_WRITE_CONTEXT readWriteContext = &deviceExtension->ScratchContext.ScratchReadWriteContext;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(deviceExtension->ScratchContext.ScratchRequest);
|
|
WDFREQUEST originalRequest = requestContext->OriginalRequest;
|
|
|
|
if (!NT_SUCCESS(WdfRequestGetStatus(Request)))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"WdfRequestSend: %lx\n",
|
|
WdfRequestGetStatus(Request)
|
|
));
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(Params);
|
|
UNREFERENCED_PARAMETER(Target);
|
|
|
|
// We are not calling ScratchBuffer_BeginUse / ScratchBuffer_EndUse in this function, because we already own
|
|
// the scratch buffer if this function is being called.
|
|
|
|
if ((deviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
|
|
(deviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
|
|
{
|
|
// The request has been cancelled, just need to complete it
|
|
}
|
|
else if (SRB_STATUS(deviceExtension->ScratchContext.ScratchSrb->SrbStatus) != SRB_STATUS_SUCCESS)
|
|
{
|
|
// The SCSI command that we sent down has failed, retry it if necessary
|
|
BOOLEAN shouldRetry = TRUE;
|
|
LONGLONG retryIn100nsUnits = 0;
|
|
|
|
shouldRetry = RequestSenseInfoInterpretForScratchBuffer(deviceExtension,
|
|
deviceExtension->ScratchContext.NumRetries,
|
|
&status,
|
|
&retryIn100nsUnits);
|
|
|
|
if (shouldRetry)
|
|
{
|
|
deviceExtension->ScratchContext.NumRetries++;
|
|
|
|
if (retryIn100nsUnits == 0)
|
|
{
|
|
// We take a shortcut here by calling ScratchBuffer_PerformNextReadWrite directly:
|
|
// this helps to avoid unnecessary context switch.
|
|
status = ScratchBuffer_PerformNextReadWrite(deviceExtension, FALSE);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// We're not done with the request yet, no need to complete it now
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(originalRequest);
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Initialize the spin lock and timer local to the original request.
|
|
//
|
|
if (!originalRequestContext->ReadWriteRetryInitialized)
|
|
{
|
|
KeInitializeSpinLock(&originalRequestContext->ReadWriteCancelSpinLock);
|
|
KeInitializeTimer(&originalRequestContext->ReadWriteTimer);
|
|
KeInitializeDpc(&originalRequestContext->ReadWriteDpc, ScratchBuffer_ReadWriteTimerRoutine, originalRequest);
|
|
originalRequestContext->ReadWriteRetryInitialized = TRUE;
|
|
}
|
|
|
|
KeAcquireSpinLock(&requestContext->ReadWriteCancelSpinLock, &oldIrql);
|
|
|
|
status = WdfRequestMarkCancelableEx(originalRequest, ScratchBuffer_ReadWriteEvtRequestCancel);
|
|
|
|
if (status == STATUS_CANCELLED)
|
|
{
|
|
requestContext->ReadWriteIsCompleted = TRUE;
|
|
|
|
KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
|
|
}
|
|
else
|
|
{
|
|
LARGE_INTEGER t;
|
|
|
|
t.QuadPart = -retryIn100nsUnits;
|
|
|
|
WdfObjectReference(originalRequest);
|
|
|
|
// Use negative time to indicate that we want a relative delay
|
|
KeSetTimer(&originalRequestContext->ReadWriteTimer,
|
|
t,
|
|
&originalRequestContext->ReadWriteDpc
|
|
);
|
|
|
|
KeReleaseSpinLock(&requestContext->ReadWriteCancelSpinLock, oldIrql);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The SCSI command has succeeded
|
|
readWriteContext->DataBuffer += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
|
|
readWriteContext->StartingOffset.QuadPart += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
|
|
readWriteContext->TransferedBytes += deviceExtension->ScratchContext.ScratchSrb->DataTransferLength;
|
|
readWriteContext->PacketsCount--;
|
|
|
|
// Update the SRB history item
|
|
if (readWriteContext->SrbHistoryItem)
|
|
{
|
|
ULONG senseSize;
|
|
|
|
// Query the tick count and store in the history
|
|
KeQueryTickCount(&readWriteContext->SrbHistoryItem->TickCountCompleted);
|
|
|
|
// Copy the SRB Status...
|
|
readWriteContext->SrbHistoryItem->SrbStatus = deviceExtension->ScratchContext.ScratchSrb->SrbStatus;
|
|
|
|
// Determine the amount of valid sense data
|
|
if (deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength >=
|
|
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))
|
|
{
|
|
PSENSE_DATA sense = (PSENSE_DATA)deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer;
|
|
senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) +
|
|
sense->AdditionalSenseLength;
|
|
senseSize = min(senseSize, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
senseSize = deviceExtension->ScratchContext.ScratchSrb->SenseInfoBufferLength;
|
|
}
|
|
|
|
// Normalize the sense data copy in the history
|
|
RtlZeroMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData), sizeof(SENSE_DATA));
|
|
RtlCopyMemory(&(readWriteContext->SrbHistoryItem->NormalizedSenseData),
|
|
deviceExtension->ScratchContext.ScratchSrb->SenseInfoBuffer, senseSize);
|
|
}
|
|
|
|
// Check whether we need to send more SCSI commands to complete the request
|
|
if (readWriteContext->PacketsCount > 0)
|
|
{
|
|
status = ScratchBuffer_PerformNextReadWrite(deviceExtension, TRUE);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// We're not done with the request yet, no need to complete it now
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
ScratchBuffer_EndUse(deviceExtension);
|
|
|
|
|
|
RequestCompletion(deviceExtension, originalRequest, status, readWriteContext->TransferedBytes);
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
VOID
|
|
ScratchBuffer_SetupSrb(
|
|
_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_opt_ WDFREQUEST OriginalRequest,
|
|
_In_ ULONG MaximumTransferLength,
|
|
_In_ BOOLEAN GetDataFromDevice
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
setup scratch SRB for sending out.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
OriginalRequest - original request delivered by WDF
|
|
MaximumTransferLength - transfer length
|
|
GetDataFromDevice - TRUE (get data from device); FALSE (send data to device)
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
WDFREQUEST request = DeviceExtension->ScratchContext.ScratchRequest;
|
|
PIRP irp = WdfRequestWdmGetIrp(request);
|
|
PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
|
|
PIO_STACK_LOCATION irpStack = NULL;
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(request);
|
|
|
|
PAGED_CODE ();
|
|
|
|
requestContext->OriginalRequest = OriginalRequest;
|
|
|
|
// set to use the full scratch buffer via the scratch SRB
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
if (MaximumTransferLength == 0)
|
|
{
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_NONE;
|
|
}
|
|
else if (GetDataFromDevice)
|
|
{
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
|
|
}
|
|
else
|
|
{
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
|
|
}
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
if (MaximumTransferLength > 0)
|
|
{
|
|
// the Irp must show the MDL's address for the transfer
|
|
irp->MdlAddress = DeviceExtension->ScratchContext.ScratchBufferMdl;
|
|
|
|
srb->DataBuffer = DeviceExtension->ScratchContext.ScratchBuffer;
|
|
}
|
|
|
|
// prepare the SRB with default values
|
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
|
srb->SrbStatus = 0;
|
|
srb->ScsiStatus = 0;
|
|
srb->NextSrb = NULL;
|
|
srb->OriginalRequest = irp;
|
|
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense;
|
|
|
|
srb->CdbLength = 16; // to cause failures if not set correctly -- CD devices limited to 12 bytes for now...
|
|
|
|
srb->DataTransferLength = min(DeviceExtension->ScratchContext.ScratchBufferSize, MaximumTransferLength);
|
|
srb->TimeOutValue = DeviceExtension->TimeOutValue;
|
|
srb->SrbFlags = DeviceExtension->SrbFlags;
|
|
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
|
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
|
|
|
|
if (MaximumTransferLength == 0)
|
|
{
|
|
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
|
}
|
|
else if (GetDataFromDevice)
|
|
{
|
|
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
|
}
|
|
else
|
|
{
|
|
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ScratchBuffer_SendSrb(
|
|
_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ BOOLEAN SynchronousSrb,
|
|
_When_(SynchronousSrb, _Pre_null_)
|
|
_When_(!SynchronousSrb, _In_opt_)
|
|
PSRB_HISTORY_ITEM *SrbHistoryItem
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send the command from the scratch SRB to lower driver and retry if necessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
SynchronousSrb - indicates whether the SRB needs to be sent synchronously or nor
|
|
SrbHistoryItem - storage for SRB history item, if this is an asynchronous request
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
|
|
PSRB_HISTORY history = DeviceExtension->ScratchContext.ScratchHistory;
|
|
PSRB_HISTORY_ITEM item = NULL;
|
|
BOOLEAN requestCancelled = FALSE;
|
|
|
|
srb->InternalStatus = 0;
|
|
srb->SrbStatus = 0;
|
|
|
|
// allocate/update history pre-command, if it is a synchronous request or we were supplied
|
|
// with a storage for the history item
|
|
if (SynchronousSrb || SrbHistoryItem != NULL)
|
|
{
|
|
// sending a packet implies a new history unit is to be used.
|
|
NT_ASSERT( history->UsedHistoryCount <= history->TotalHistoryCount );
|
|
|
|
// if already all used up, remove at least one history unit
|
|
if (history->UsedHistoryCount == history->TotalHistoryCount )
|
|
{
|
|
CompressSrbHistoryData(history);
|
|
NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
|
|
}
|
|
|
|
// thus, since we are about to increment the count, it must now be less...
|
|
NT_ASSERT( history->UsedHistoryCount < history->TotalHistoryCount );
|
|
|
|
// increment the number of history units in use
|
|
history->UsedHistoryCount++;
|
|
|
|
// determine index to use
|
|
item = &( history->History[ history->UsedHistoryCount-1 ] );
|
|
|
|
if (SrbHistoryItem != NULL)
|
|
{
|
|
*SrbHistoryItem = item;
|
|
}
|
|
|
|
// zero out the history item
|
|
RtlZeroMemory(item, sizeof(SRB_HISTORY_ITEM));
|
|
|
|
// Query the tick count and store in the history
|
|
KeQueryTickCount(&item->TickCountSent);
|
|
}
|
|
|
|
// get cancellation status;
|
|
{
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
|
|
|
|
if (requestContext->OriginalRequest != NULL)
|
|
{
|
|
requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest);
|
|
}
|
|
}
|
|
|
|
if (!requestCancelled)
|
|
{
|
|
status = RequestSend(DeviceExtension,
|
|
DeviceExtension->ScratchContext.ScratchRequest,
|
|
DeviceExtension->IoTarget,
|
|
SynchronousSrb ? WDF_REQUEST_SEND_OPTION_SYNCHRONOUS : 0,
|
|
NULL);
|
|
|
|
// If this is a synchronous request, update the history item immediately, including "normalized" sense data
|
|
if (SynchronousSrb)
|
|
{
|
|
ULONG senseSize;
|
|
|
|
// Query the tick count and store in the history
|
|
KeQueryTickCount(&item->TickCountCompleted);
|
|
|
|
// Copy the SRB Status
|
|
item->SrbStatus = srb->SrbStatus;
|
|
|
|
// Determine the amount of valid sense data
|
|
if (srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength))
|
|
{
|
|
PSENSE_DATA sense = (PSENSE_DATA)srb->SenseInfoBuffer;
|
|
senseSize = RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength) +
|
|
sense->AdditionalSenseLength;
|
|
senseSize = min(senseSize, sizeof(SENSE_DATA));
|
|
}
|
|
else
|
|
{
|
|
senseSize = srb->SenseInfoBufferLength;
|
|
}
|
|
|
|
// Normalize the sense data copy in the history
|
|
RtlZeroMemory(&(item->NormalizedSenseData), sizeof(SENSE_DATA));
|
|
RtlCopyMemory(&(item->NormalizedSenseData), srb->SenseInfoBuffer, senseSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DeviceExtension->ScratchContext.ScratchSrb->SrbStatus = SRB_STATUS_ABORTED;
|
|
DeviceExtension->ScratchContext.ScratchSrb->InternalStatus = (ULONG)STATUS_CANCELLED;
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
CompressSrbHistoryData(
|
|
_Inout_ PSRB_HISTORY RequestHistory
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
compress the SRB history data.
|
|
|
|
Arguments:
|
|
|
|
RequestHistory - SRB history data
|
|
|
|
Return Value:
|
|
|
|
RequestHistory - compressed history data
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
NT_ASSERT( RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount );
|
|
ValidateSrbHistoryDataPresumptions(RequestHistory);
|
|
|
|
for (i=0; i < RequestHistory->UsedHistoryCount; i++)
|
|
{
|
|
// for each item...
|
|
PSRB_HISTORY_ITEM toMatch = &( RequestHistory->History[i] );
|
|
// hint: read const qualifiers backwards. i.e. srbstatus is a const UCHAR
|
|
// so, "UCHAR const * const x" is read "x is a const pointer to a const UCHAR"
|
|
// unfortunately, "const UCHAR" is equivalent to "UCHAR const", which causes
|
|
// people no end of confusion due to its widespread use.
|
|
UCHAR const srbStatus = toMatch->SrbStatus;
|
|
UCHAR const sense = toMatch->NormalizedSenseData.SenseKey;
|
|
UCHAR const asc = toMatch->NormalizedSenseData.AdditionalSenseCode;
|
|
UCHAR const ascq = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier;
|
|
ULONG j;
|
|
|
|
// see if there are any at higher indices with identical Sense/ASC/ASCQ
|
|
for (j = i+1; (toMatch->ClassDriverUse != 0xFF) && (j < RequestHistory->UsedHistoryCount); j++)
|
|
{
|
|
PSRB_HISTORY_ITEM found = &( RequestHistory->History[j] );
|
|
// close enough match?
|
|
if ((srbStatus == found->SrbStatus) &&
|
|
(sense == found->NormalizedSenseData.SenseKey) &&
|
|
(asc == found->NormalizedSenseData.AdditionalSenseCode) &&
|
|
(ascq == found->NormalizedSenseData.AdditionalSenseCodeQualifier)) {
|
|
|
|
// add the fields to keep reasonable track of delay times.
|
|
if (toMatch->MillisecondsDelayOnRetry + found->MillisecondsDelayOnRetry < toMatch->MillisecondsDelayOnRetry) {
|
|
toMatch->MillisecondsDelayOnRetry = MAXULONG;
|
|
} else {
|
|
toMatch->MillisecondsDelayOnRetry += found->MillisecondsDelayOnRetry;
|
|
}
|
|
|
|
// this found item cannot contain any compressed entries because
|
|
// the first entry with a given set of sense/asc/ascq will always
|
|
// either be full (0xFF) or be the only partially-full entry with
|
|
// that sense/asc/ascq.
|
|
NT_ASSERT(found->ClassDriverUse == 0);
|
|
// add the counts so we still know how many retries total
|
|
toMatch->ClassDriverUse++;
|
|
|
|
|
|
// if not the last entry, need to move later entries earlier in the array
|
|
if (j != RequestHistory->UsedHistoryCount-1) {
|
|
// how many entries remain?
|
|
SIZE_T remainingBytes = RequestHistory->UsedHistoryCount - 1 - j;
|
|
remainingBytes *= sizeof(SRB_HISTORY_ITEM);
|
|
|
|
// note that MOVE is required due to overlapping entries
|
|
RtlMoveMemory(found, found+1, remainingBytes);
|
|
|
|
// Finally, decrement the number of used history count and
|
|
// decrement j to rescan the current location again
|
|
--RequestHistory->UsedHistoryCount;
|
|
--j;
|
|
} // end moving of array elements around
|
|
} // end of close enough match
|
|
} // end j loop
|
|
} // end i loop
|
|
|
|
// unable to compress duplicate sense/asc/ascq, so just lose the most recent data
|
|
if (RequestHistory->UsedHistoryCount == RequestHistory->TotalHistoryCount)
|
|
{
|
|
PSRB_HISTORY_ITEM item = &( RequestHistory->History[ RequestHistory->TotalHistoryCount-1 ] );
|
|
RequestHistory->ClassDriverUse[0] += item->ClassDriverUse; // how many did we "lose"?
|
|
RequestHistory->UsedHistoryCount--;
|
|
}
|
|
|
|
// finally, zero any that are no longer in use
|
|
NT_ASSERT( RequestHistory->UsedHistoryCount != RequestHistory->TotalHistoryCount);
|
|
{
|
|
SIZE_T bytesToZero = RequestHistory->TotalHistoryCount - RequestHistory->UsedHistoryCount;
|
|
bytesToZero *= sizeof(SRB_HISTORY_ITEM);
|
|
RtlZeroMemory(&(RequestHistory->History[RequestHistory->UsedHistoryCount]), bytesToZero);
|
|
}
|
|
|
|
ValidateSrbHistoryDataPresumptions(RequestHistory);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ValidateSrbHistoryDataPresumptions(
|
|
_In_ SRB_HISTORY const * RequestHistory
|
|
)
|
|
{
|
|
#if DBG
|
|
// validate that all fully-compressed items are before any non-fully-compressed items of any particular sense/asc/ascq
|
|
// validate that there is at most one partially-compressed item of any particular sense/asc/ascq
|
|
// validate that all items of any particular sense/asc/ascq that are uncompressed are at the end
|
|
// THUS: A(255) A(255) A( 40) A( 0) A( 0) is legal for all types with A as sense/asc/ascq
|
|
// A(0) B(255) A( 0) B( 17) B( 0) is also legal because A/B are different types of error
|
|
|
|
ULONG i;
|
|
for (i = 0; i < RequestHistory->UsedHistoryCount; i++)
|
|
{
|
|
SRB_HISTORY_ITEM const * toMatch = &( RequestHistory->History[i] );
|
|
UCHAR const srbStatus = toMatch->SrbStatus;
|
|
UCHAR const sense = toMatch->NormalizedSenseData.SenseKey;
|
|
UCHAR const asc = toMatch->NormalizedSenseData.AdditionalSenseCode;
|
|
UCHAR const ascq = toMatch->NormalizedSenseData.AdditionalSenseCodeQualifier;
|
|
ULONG j;
|
|
|
|
BOOLEAN foundPartiallyCompressedItem =
|
|
(toMatch->ClassDriverUse != 0) &&
|
|
(toMatch->ClassDriverUse != 0xFF) ;
|
|
BOOLEAN foundUncompressedItem =
|
|
(toMatch->ClassDriverUse == 0) ;
|
|
|
|
for (j = i+1; j < RequestHistory->UsedHistoryCount; j++)
|
|
{
|
|
SRB_HISTORY_ITEM const * found = &( RequestHistory->History[j] );
|
|
if ((srbStatus == found->SrbStatus) &&
|
|
(sense == found->NormalizedSenseData.SenseKey) &&
|
|
(asc == found->NormalizedSenseData.AdditionalSenseCode) &&
|
|
(ascq == found->NormalizedSenseData.AdditionalSenseCodeQualifier)
|
|
)
|
|
{
|
|
// found a matching type, so validate ordering rules
|
|
if (foundUncompressedItem && (found->ClassDriverUse != 0))
|
|
{
|
|
DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL,
|
|
"History data has compressed history following uncompressed history "
|
|
"for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n",
|
|
srbStatus, sense, asc, ascq,
|
|
i,i, j,j
|
|
);
|
|
NT_ASSERT(FALSE);
|
|
}
|
|
else if (foundPartiallyCompressedItem && (found->ClassDriverUse == 0xFF))
|
|
{
|
|
DbgPrintEx(DPFLTR_CDROM_ID, DPFLTR_ERROR_LEVEL,
|
|
"History data has fully compressed history following partially compressed history "
|
|
"for srbstatus/sense/asc/ascq of %02x/%02x/%02x/%02x at indices %d (%08x) and %d (%08x)\n",
|
|
srbStatus, sense, asc, ascq,
|
|
i,i, j,j
|
|
);
|
|
NT_ASSERT(FALSE);
|
|
}
|
|
|
|
// update if we have now found partially compressed and/or uncompressed items
|
|
if (found->ClassDriverUse == 0)
|
|
{
|
|
foundUncompressedItem = TRUE;
|
|
}
|
|
else if (found->ClassDriverUse != 0xFF)
|
|
{
|
|
foundPartiallyCompressedItem = TRUE;
|
|
}
|
|
} // end match of (toMatch,found)
|
|
} // end loop j
|
|
} // end loop i
|
|
#else
|
|
UNREFERENCED_PARAMETER(RequestHistory);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ScratchBuffer_SetupReadWriteSrb(
|
|
_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ WDFREQUEST OriginalRequest,
|
|
_In_ LARGE_INTEGER StartingOffset,
|
|
_In_ ULONG RequiredLength,
|
|
_Inout_updates_bytes_(RequiredLength) UCHAR* DataBuffer,
|
|
_In_ BOOLEAN IsReadRequest,
|
|
_In_ BOOLEAN UsePartialMdl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
setup SRB for read/write request.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
OriginalRequest - read/write request
|
|
StartingOffset - read/write starting offset
|
|
DataBuffer - buffer for read/write
|
|
IsReadRequest - TRUE (read); FALSE (write)
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
//NOTE: R/W request not use the ScratchBuffer, instead, it uses the buffer associated with IRP.
|
|
|
|
PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
|
|
PCDB cdb = (PCDB)srb->Cdb;
|
|
LARGE_INTEGER logicalBlockAddr;
|
|
ULONG numTransferBlocks;
|
|
|
|
PIRP originalIrp = WdfRequestWdmGetIrp(OriginalRequest);
|
|
|
|
PIRP irp = WdfRequestWdmGetIrp(DeviceExtension->ScratchContext.ScratchRequest);
|
|
PIO_STACK_LOCATION irpStack = NULL;
|
|
|
|
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(DeviceExtension->ScratchContext.ScratchRequest);
|
|
|
|
requestContext->OriginalRequest = OriginalRequest;
|
|
|
|
|
|
logicalBlockAddr.QuadPart = Int64ShrlMod32(StartingOffset.QuadPart, DeviceExtension->SectorShift);
|
|
numTransferBlocks = RequiredLength >> DeviceExtension->SectorShift;
|
|
|
|
// set to use the full scratch buffer via the scratch SRB
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
if (IsReadRequest)
|
|
{
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
|
|
}
|
|
else
|
|
{
|
|
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
|
|
}
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
|
|
// prepare the SRB with default values
|
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
|
srb->SrbStatus = 0;
|
|
srb->ScsiStatus = 0;
|
|
srb->NextSrb = NULL;
|
|
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
srb->SenseInfoBuffer = DeviceExtension->ScratchContext.ScratchSense;
|
|
|
|
srb->DataBuffer = DataBuffer;
|
|
srb->DataTransferLength = RequiredLength;
|
|
|
|
srb->QueueSortKey = logicalBlockAddr.LowPart;
|
|
if (logicalBlockAddr.QuadPart > 0xFFFFFFFF)
|
|
{
|
|
//
|
|
// If the requested LBA is more than max ULONG set the
|
|
// QueueSortKey to the maximum value, so that these
|
|
// requests can be added towards the end of the queue.
|
|
//
|
|
srb->QueueSortKey = 0xFFFFFFFF;
|
|
}
|
|
|
|
srb->OriginalRequest = irp;
|
|
srb->TimeOutValue = DeviceExtension->TimeOutValue;
|
|
|
|
if (RequestIsRealtimeStreaming(OriginalRequest, IsReadRequest) &&
|
|
!TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_NO_STREAMING))
|
|
{
|
|
if (IsReadRequest)
|
|
{
|
|
RtlZeroMemory(&cdb->READ12, sizeof(cdb->READ12));
|
|
REVERSE_BYTES(&cdb->READ12.LogicalBlock, &logicalBlockAddr.LowPart);
|
|
REVERSE_BYTES(&cdb->READ12.TransferLength, &numTransferBlocks);
|
|
cdb->READ12.Streaming = 1;
|
|
cdb->READ12.OperationCode = SCSIOP_READ12;
|
|
srb->CdbLength = sizeof(cdb->READ12);
|
|
}
|
|
else
|
|
{
|
|
RtlZeroMemory(&cdb->WRITE12, sizeof(cdb->WRITE12));
|
|
REVERSE_BYTES(&cdb->WRITE12.LogicalBlock, &logicalBlockAddr.LowPart);
|
|
REVERSE_BYTES(&cdb->WRITE12.TransferLength, &numTransferBlocks);
|
|
cdb->WRITE12.Streaming = 1;
|
|
cdb->WRITE12.OperationCode = SCSIOP_WRITE12;
|
|
srb->CdbLength = sizeof(cdb->WRITE12);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtlZeroMemory(&cdb->CDB10, sizeof(cdb->CDB10));
|
|
cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte3;
|
|
cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte2;
|
|
cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte1;
|
|
cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr.LowPart)->Byte0;
|
|
cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1;
|
|
cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0;
|
|
cdb->CDB10.OperationCode = (IsReadRequest) ? SCSIOP_READ : SCSIOP_WRITE;
|
|
srb->CdbLength = sizeof(cdb->CDB10);
|
|
}
|
|
|
|
// Set SRB and IRP flags
|
|
srb->SrbFlags = DeviceExtension->SrbFlags;
|
|
if (TEST_FLAG(originalIrp->Flags, IRP_PAGING_IO) ||
|
|
TEST_FLAG(originalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO))
|
|
{
|
|
SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PAGING);
|
|
}
|
|
|
|
SET_FLAG(srb->SrbFlags, (IsReadRequest) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
|
|
SET_FLAG(srb->SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
|
|
|
|
//
|
|
// If the request is not split, we can use the original IRP MDL. If the
|
|
// request needs to be split, we need to use a partial MDL. The partial MDL
|
|
// is needed because more than one driver might be mapping the same MDL
|
|
// and this causes problems.
|
|
//
|
|
if (UsePartialMdl == FALSE)
|
|
{
|
|
irp->MdlAddress = originalIrp->MdlAddress;
|
|
}
|
|
else
|
|
{
|
|
if (DeviceExtension->ScratchContext.PartialMdlIsBuilt != FALSE)
|
|
{
|
|
MmPrepareMdlForReuse(DeviceExtension->ScratchContext.PartialMdl);
|
|
}
|
|
|
|
IoBuildPartialMdl(originalIrp->MdlAddress, DeviceExtension->ScratchContext.PartialMdl, srb->DataBuffer, srb->DataTransferLength);
|
|
DeviceExtension->ScratchContext.PartialMdlIsBuilt = TRUE;
|
|
irp->MdlAddress = DeviceExtension->ScratchContext.PartialMdl;
|
|
}
|
|
|
|
//DBGLOGSENDPACKET(Pkt);
|
|
//HISTORYLOGSENDPACKET(Pkt);
|
|
|
|
//
|
|
// Set the original irp here for SFIO.
|
|
//
|
|
srb->SrbExtension = (PVOID)(originalIrp);
|
|
|
|
return;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
ScratchBuffer_ExecuteCdbEx(
|
|
_Inout_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_opt_ WDFREQUEST OriginalRequest,
|
|
_In_ ULONG TransferSize,
|
|
_In_ BOOLEAN GetDataFromDevice,
|
|
_In_ PCDB Cdb,
|
|
_In_ UCHAR OprationLength,
|
|
_In_ ULONG TimeoutValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Use Scratch buffer to send the Cdb, check error and retry if necessary.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device context
|
|
OriginalRequest - original request that requires this CDB operation
|
|
TransferSize - Data transfer size required
|
|
GetFromDevice - TRUE if getting data from device.
|
|
Cdb - SCSI command
|
|
OprationLength - SCSI command length: 6, 10 or 12
|
|
TimeoutValue - if > 0, use it as timeout value for command
|
|
if 0, use the default device timeout value
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PSCSI_REQUEST_BLOCK srb = DeviceExtension->ScratchContext.ScratchSrb;
|
|
PCDB cdb = (PCDB)(srb->Cdb);
|
|
|
|
BOOLEAN shouldRetry = TRUE;
|
|
ULONG timesAlreadyRetried = 0;
|
|
LONGLONG retryIn100nsUnits = 0;
|
|
|
|
PAGED_CODE ();
|
|
|
|
while (shouldRetry)
|
|
{
|
|
ScratchBuffer_SetupSrb(DeviceExtension, OriginalRequest, TransferSize, GetDataFromDevice);
|
|
|
|
// Set up the SRB/CDB
|
|
RtlCopyMemory(cdb, Cdb, sizeof(CDB));
|
|
|
|
srb->CdbLength = OprationLength;
|
|
|
|
if (TimeoutValue > 0)
|
|
{
|
|
srb->TimeOutValue = TimeoutValue;
|
|
}
|
|
|
|
ScratchBuffer_SendSrb(DeviceExtension, TRUE, NULL);
|
|
|
|
if ((DeviceExtension->ScratchContext.ScratchSrb->SrbStatus == SRB_STATUS_ABORTED) &&
|
|
(DeviceExtension->ScratchContext.ScratchSrb->InternalStatus == STATUS_CANCELLED))
|
|
{
|
|
shouldRetry = FALSE;
|
|
status = STATUS_CANCELLED;
|
|
}
|
|
else
|
|
{
|
|
shouldRetry = RequestSenseInfoInterpretForScratchBuffer(DeviceExtension,
|
|
timesAlreadyRetried,
|
|
&status,
|
|
&retryIn100nsUnits);
|
|
if (shouldRetry)
|
|
{
|
|
LARGE_INTEGER t;
|
|
t.QuadPart = -retryIn100nsUnits;
|
|
timesAlreadyRetried++;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &t);
|
|
// keep items clean
|
|
ScratchBuffer_ResetItems(DeviceExtension, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|