mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 02:25:17 +00:00
3878 lines
120 KiB
C
3878 lines
120 KiB
C
/*++
|
||
|
||
Copyright (C) Microsoft Corporation. All rights reserved.
|
||
|
||
Module Name:
|
||
|
||
common.c
|
||
|
||
Abstract:
|
||
|
||
shared private routines for cdrom.sys
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include "ntddk.h"
|
||
#include "ntddstor.h"
|
||
#include "ntstrsafe.h"
|
||
|
||
#include "cdrom.h"
|
||
#include "scratch.h"
|
||
|
||
|
||
#ifdef DEBUG_USE_WPP
|
||
#include "common.tmh"
|
||
#endif
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(PAGE, DeviceGetParameter)
|
||
#pragma alloc_text(PAGE, DeviceSetParameter)
|
||
#pragma alloc_text(PAGE, DeviceSendSrbSynchronously)
|
||
#pragma alloc_text(PAGE, DevicePickDvdRegion)
|
||
#pragma alloc_text(PAGE, StringsAreMatched)
|
||
#pragma alloc_text(PAGE, PerformEjectionControl)
|
||
#pragma alloc_text(PAGE, DeviceFindFeaturePage)
|
||
#pragma alloc_text(PAGE, DevicePrintAllFeaturePages)
|
||
#pragma alloc_text(PAGE, DeviceRegisterInterface)
|
||
#pragma alloc_text(PAGE, DeviceRestoreDefaultSpeed)
|
||
#pragma alloc_text(PAGE, DeviceSendRequestSynchronously)
|
||
#pragma alloc_text(PAGE, MediaReadCapacity)
|
||
#pragma alloc_text(PAGE, MediaReadCapacityDataInterpret)
|
||
#pragma alloc_text(PAGE, DeviceRetrieveModeSenseUsingScratch)
|
||
#pragma alloc_text(PAGE, ModeSenseFindSpecificPage)
|
||
#pragma alloc_text(PAGE, DeviceUnlockExclusive)
|
||
|
||
#endif
|
||
|
||
LPCSTR LockTypeStrings[] = {"Simple",
|
||
"Secure",
|
||
"Internal"
|
||
};
|
||
|
||
VOID
|
||
RequestSetReceivedTime(
|
||
_In_ WDFREQUEST Request
|
||
)
|
||
{
|
||
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
||
LARGE_INTEGER temp;
|
||
|
||
KeQueryTickCount(&temp);
|
||
|
||
requestContext->TimeReceived = temp;
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RequestSetSentTime(
|
||
_In_ WDFREQUEST Request
|
||
)
|
||
{
|
||
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
||
LARGE_INTEGER temp;
|
||
|
||
KeQueryTickCount(&temp);
|
||
|
||
if (requestContext->TimeSentDownFirstTime.QuadPart == 0)
|
||
{
|
||
requestContext->TimeSentDownFirstTime = temp;
|
||
}
|
||
|
||
requestContext->TimeSentDownLasttTime = temp;
|
||
|
||
if (requestContext->OriginalRequest != NULL)
|
||
{
|
||
PCDROM_REQUEST_CONTEXT originalRequestContext = RequestGetContext(requestContext->OriginalRequest);
|
||
|
||
if (originalRequestContext->TimeSentDownFirstTime.QuadPart == 0)
|
||
{
|
||
originalRequestContext->TimeSentDownFirstTime = temp;
|
||
}
|
||
|
||
originalRequestContext->TimeSentDownLasttTime = temp;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RequestClearSendTime(
|
||
_In_ WDFREQUEST Request
|
||
)
|
||
/*
|
||
Routine Description:
|
||
|
||
This function is used to clean SentTime fields in reusable request context.
|
||
|
||
Arguments:
|
||
Request -
|
||
|
||
Return Value:
|
||
N/A
|
||
|
||
*/
|
||
{
|
||
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
||
|
||
requestContext->TimeSentDownFirstTime.QuadPart = 0;
|
||
requestContext->TimeSentDownLasttTime.QuadPart = 0;
|
||
|
||
return;
|
||
}
|
||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||
VOID
|
||
DeviceGetParameter(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_opt_ PWSTR SubkeyName,
|
||
_In_ PWSTR ParameterName,
|
||
_Inout_ PULONG ParameterValue // also default value
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
retrieve device parameter from registry.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context.
|
||
|
||
SubkeyName - name of subkey
|
||
|
||
ParameterName - the registry parameter to be retrieved
|
||
|
||
Return Value:
|
||
|
||
ParameterValue - registry value retrieved
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
WDFKEY rootKey = NULL;
|
||
WDFKEY subKey = NULL;
|
||
UNICODE_STRING registrySubKeyName;
|
||
UNICODE_STRING registryValueName;
|
||
ULONG defaultParameterValue;
|
||
|
||
PAGED_CODE();
|
||
|
||
RtlInitUnicodeString(®istryValueName, ParameterName);
|
||
|
||
if (SubkeyName != NULL)
|
||
{
|
||
RtlInitUnicodeString(®istrySubKeyName, SubkeyName);
|
||
}
|
||
|
||
// open the hardware key
|
||
status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
|
||
PLUGPLAY_REGKEY_DEVICE,
|
||
KEY_READ,
|
||
WDF_NO_OBJECT_ATTRIBUTES,
|
||
&rootKey);
|
||
|
||
// open the sub key
|
||
if (NT_SUCCESS(status) && (SubkeyName != NULL))
|
||
{
|
||
status = WdfRegistryOpenKey(rootKey,
|
||
®istrySubKeyName,
|
||
KEY_READ,
|
||
WDF_NO_OBJECT_ATTRIBUTES,
|
||
&subKey);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
WdfRegistryClose(rootKey);
|
||
rootKey = NULL;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status) && (rootKey != NULL))
|
||
{
|
||
defaultParameterValue = *ParameterValue;
|
||
|
||
status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey,
|
||
®istryValueName,
|
||
ParameterValue);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
*ParameterValue = defaultParameterValue; // use default value
|
||
}
|
||
}
|
||
|
||
// close what we open
|
||
if (subKey != NULL)
|
||
{
|
||
WdfRegistryClose(subKey);
|
||
subKey = NULL;
|
||
}
|
||
|
||
if (rootKey != NULL)
|
||
{
|
||
WdfRegistryClose(rootKey);
|
||
rootKey = NULL;
|
||
}
|
||
|
||
// Windows 2000 SP3 uses the driver-specific key, so look in there
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
// open the software key
|
||
status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
|
||
PLUGPLAY_REGKEY_DRIVER,
|
||
KEY_READ,
|
||
WDF_NO_OBJECT_ATTRIBUTES,
|
||
&rootKey);
|
||
|
||
// open the sub key
|
||
if (NT_SUCCESS(status) && (SubkeyName != NULL))
|
||
{
|
||
status = WdfRegistryOpenKey(rootKey,
|
||
®istrySubKeyName,
|
||
KEY_READ,
|
||
WDF_NO_OBJECT_ATTRIBUTES,
|
||
&subKey);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
WdfRegistryClose(rootKey);
|
||
rootKey = NULL;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status) && (rootKey != NULL))
|
||
{
|
||
defaultParameterValue = *ParameterValue;
|
||
|
||
status = WdfRegistryQueryULong((subKey != NULL) ? subKey : rootKey,
|
||
®istryValueName,
|
||
ParameterValue);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
*ParameterValue = defaultParameterValue; // use default value
|
||
}
|
||
else
|
||
{
|
||
// Migrate the value over to the device-specific key
|
||
DeviceSetParameter(DeviceExtension, SubkeyName, ParameterName, *ParameterValue);
|
||
}
|
||
}
|
||
|
||
// close what we open
|
||
if (subKey != NULL)
|
||
{
|
||
WdfRegistryClose(subKey);
|
||
subKey = NULL;
|
||
}
|
||
|
||
if (rootKey != NULL)
|
||
{
|
||
WdfRegistryClose(rootKey);
|
||
rootKey = NULL;
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
} // end DeviceetParameter()
|
||
|
||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||
NTSTATUS
|
||
DeviceSetParameter(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_opt_z_ PWSTR SubkeyName,
|
||
_In_ PWSTR ParameterName,
|
||
_In_ ULONG ParameterValue
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
set parameter to registry.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context.
|
||
|
||
SubkeyName - name of subkey
|
||
|
||
ParameterName - the registry parameter to be retrieved
|
||
|
||
ParameterValue - registry value to be set
|
||
|
||
Return Value:
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
WDFKEY rootKey = NULL;
|
||
WDFKEY subKey = NULL;
|
||
UNICODE_STRING registrySubKeyName;
|
||
UNICODE_STRING registryValueName;
|
||
|
||
PAGED_CODE();
|
||
|
||
RtlInitUnicodeString(®istryValueName, ParameterName);
|
||
|
||
if (SubkeyName != NULL)
|
||
{
|
||
RtlInitUnicodeString(®istrySubKeyName, SubkeyName);
|
||
}
|
||
|
||
// open the hardware key
|
||
status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
|
||
PLUGPLAY_REGKEY_DEVICE,
|
||
KEY_READ | KEY_WRITE,
|
||
WDF_NO_OBJECT_ATTRIBUTES,
|
||
&rootKey);
|
||
|
||
// open the sub key
|
||
if (NT_SUCCESS(status) && (SubkeyName != NULL))
|
||
{
|
||
status = WdfRegistryOpenKey(rootKey,
|
||
®istrySubKeyName,
|
||
KEY_READ | KEY_WRITE,
|
||
WDF_NO_OBJECT_ATTRIBUTES,
|
||
&subKey);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
WdfRegistryClose(rootKey);
|
||
rootKey = NULL;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status) && (rootKey != NULL))
|
||
{
|
||
status = WdfRegistryAssignULong((subKey != NULL) ? subKey : rootKey,
|
||
®istryValueName,
|
||
ParameterValue);
|
||
}
|
||
|
||
// close what we open
|
||
if (subKey != NULL)
|
||
{
|
||
WdfRegistryClose(subKey);
|
||
subKey = NULL;
|
||
}
|
||
|
||
if (rootKey != NULL)
|
||
{
|
||
WdfRegistryClose(rootKey);
|
||
rootKey = NULL;
|
||
}
|
||
|
||
return status;
|
||
|
||
} // end DeviceSetParameter()
|
||
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
NTSTATUS
|
||
DeviceSendRequestSynchronously(
|
||
_In_ WDFDEVICE Device,
|
||
_In_ WDFREQUEST Request,
|
||
_In_ BOOLEAN RequestFormated
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
send a request to lower driver synchronously.
|
||
|
||
Arguments:
|
||
|
||
Device - device object.
|
||
|
||
Request - request object
|
||
|
||
RequestFormated - if the request is already formatted, will no do it in this function
|
||
|
||
Return Value:
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
|
||
BOOLEAN requestCancelled = FALSE;
|
||
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!RequestFormated)
|
||
{
|
||
// set request up for sending down
|
||
WdfRequestFormatRequestUsingCurrentType(Request);
|
||
}
|
||
|
||
// get cancellation status for the original request
|
||
if (requestContext->OriginalRequest != NULL)
|
||
{
|
||
requestCancelled = WdfRequestIsCanceled(requestContext->OriginalRequest);
|
||
}
|
||
|
||
if (!requestCancelled)
|
||
{
|
||
status = RequestSend(deviceExtension,
|
||
Request,
|
||
deviceExtension->IoTarget,
|
||
WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
|
||
NULL);
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_CANCELLED;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||
NTSTATUS
|
||
DeviceSendSrbSynchronously(
|
||
_In_ WDFDEVICE Device,
|
||
_In_ PSCSI_REQUEST_BLOCK Srb,
|
||
_In_opt_ PVOID BufferAddress,
|
||
_In_ ULONG BufferLength,
|
||
_In_ BOOLEAN WriteToDevice,
|
||
_In_opt_ WDFREQUEST OriginalRequest
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Send a SRB structure to lower driver synchronously.
|
||
|
||
Process of this function:
|
||
1. Allocate SenseBuffer; Create Request; Allocate MDL
|
||
2. Do following loop if necessary
|
||
2.1 Reuse Request
|
||
2.2 Format Srb, Irp
|
||
2.3 Send Request
|
||
2.4 Error Intepret and retry decision making.
|
||
3. Release all allocated resosurces.
|
||
|
||
Arguments:
|
||
|
||
Device - device object.
|
||
|
||
Request - request object
|
||
|
||
RequestFormated - if the request is already formatted, will no do it in this function
|
||
|
||
Return Value:
|
||
NTSTATUS
|
||
|
||
NOTE:
|
||
The caller needs to setup following fields before calling this routine.
|
||
srb.CdbLength
|
||
srb.TimeOutValue
|
||
cdb
|
||
|
||
BufferLength and WriteToDevice to control the data direction of the device
|
||
BufferLength = 0: No data transfer
|
||
BufferLenth != 0 && !WriteToDevice: get data from device
|
||
BufferLenth != 0 && WriteToDevice: send data to device
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
|
||
PCDROM_PRIVATE_FDO_DATA fdoData = deviceExtension->PrivateFdoData;
|
||
PUCHAR senseInfoBuffer = NULL;
|
||
ULONG retryCount = 0;
|
||
BOOLEAN retry = FALSE;
|
||
ULONG ioctlCode = 0;
|
||
WDFREQUEST request = NULL;
|
||
PIRP irp = NULL;
|
||
PIO_STACK_LOCATION nextStack = NULL;
|
||
PMDL mdlAddress = NULL;
|
||
BOOLEAN memoryLocked = FALSE;
|
||
WDF_OBJECT_ATTRIBUTES attributes;
|
||
PZERO_POWER_ODD_INFO zpoddInfo = deviceExtension->ZeroPowerODDInfo;
|
||
|
||
PAGED_CODE();
|
||
|
||
// NOTE: This code is only pagable because we are not freezing
|
||
// the queue. Allowing the queue to be frozen from a pagable
|
||
// routine could leave the queue frozen as we try to page in
|
||
// the code to unfreeze the queue. The result would be a nice
|
||
// case of deadlock. Therefore, since we are unfreezing the
|
||
// queue regardless of the result, just set the NO_FREEZE_QUEUE
|
||
// flag in the SRB.
|
||
NT_ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
||
|
||
//1. allocate SenseBuffer and initiate Srb common fields
|
||
// these fields will not be changed by lower driver.
|
||
{
|
||
// Write length to SRB.
|
||
Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
||
|
||
// Sense buffer is in aligned nonpaged pool.
|
||
senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
|
||
SENSE_BUFFER_SIZE,
|
||
CDROM_TAG_SENSE_INFO);
|
||
|
||
if (senseInfoBuffer == NULL)
|
||
{
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceSendSrbSynchronously: Can't allocate MDL\n"));
|
||
|
||
goto Exit;
|
||
}
|
||
|
||
Srb->SenseInfoBuffer = senseInfoBuffer;
|
||
Srb->DataBuffer = BufferAddress;
|
||
|
||
// set timeout value to default value if it's not specifically set by caller.
|
||
if (Srb->TimeOutValue == 0)
|
||
{
|
||
Srb->TimeOutValue = deviceExtension->TimeOutValue;
|
||
}
|
||
}
|
||
|
||
//2. Create Request object
|
||
{
|
||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
|
||
CDROM_REQUEST_CONTEXT);
|
||
|
||
status = WdfRequestCreate(&attributes,
|
||
deviceExtension->IoTarget,
|
||
&request);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceSendSrbSynchronously: Can't create request: %lx\n",
|
||
status));
|
||
|
||
goto Exit;
|
||
}
|
||
|
||
irp = WdfRequestWdmGetIrp(request);
|
||
}
|
||
|
||
// 3. Build an MDL for the data buffer and stick it into the irp.
|
||
if (BufferAddress != NULL)
|
||
{
|
||
mdlAddress = IoAllocateMdl( BufferAddress,
|
||
BufferLength,
|
||
FALSE,
|
||
FALSE,
|
||
irp );
|
||
if (mdlAddress == NULL)
|
||
{
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceSendSrbSynchronously: Can't allocate MDL\n"));
|
||
|
||
goto Exit;
|
||
}
|
||
|
||
_SEH2_TRY
|
||
{
|
||
MmProbeAndLockPages(mdlAddress,
|
||
KernelMode,
|
||
(WriteToDevice ? IoReadAccess : IoWriteAccess));
|
||
}
|
||
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
||
{
|
||
status = _SEH2_GetExceptionCode();
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceSendSrbSynchronously: Exception %lx locking buffer\n", status));
|
||
|
||
_SEH2_YIELD(goto Exit);
|
||
}
|
||
_SEH2_END;
|
||
|
||
memoryLocked = TRUE;
|
||
}
|
||
|
||
// 4. Format Srb, Irp; Send request and retry when necessary
|
||
do
|
||
{
|
||
// clear the control variable.
|
||
retry = FALSE;
|
||
|
||
// 4.1 reuse the request object; set originalRequest field.
|
||
{
|
||
WDF_REQUEST_REUSE_PARAMS params;
|
||
PCDROM_REQUEST_CONTEXT requestContext = NULL;
|
||
|
||
// deassign the MdlAddress, this is the value we assign explicitly.
|
||
// doing this can prevent WdfRequestReuse to release the Mdl unexpectly.
|
||
if (irp->MdlAddress)
|
||
{
|
||
irp->MdlAddress = NULL;
|
||
}
|
||
|
||
WDF_REQUEST_REUSE_PARAMS_INIT(¶ms,
|
||
WDF_REQUEST_REUSE_NO_FLAGS,
|
||
STATUS_SUCCESS);
|
||
|
||
status = WdfRequestReuse(request, ¶ms);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceSendSrbSynchronously: WdfRequestReuse failed, %!STATUS!\n",
|
||
status));
|
||
// exit the loop.
|
||
break;
|
||
}
|
||
|
||
// WDF requests to format the request befor sending it
|
||
status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
|
||
request,
|
||
ioctlCode,
|
||
NULL, NULL,
|
||
NULL, NULL,
|
||
NULL, NULL);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceSendSrbSynchronously: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
|
||
status));
|
||
// exit the loop.
|
||
break;
|
||
}
|
||
|
||
requestContext = RequestGetContext(request);
|
||
requestContext->OriginalRequest = OriginalRequest;
|
||
}
|
||
|
||
// 4.2 Format Srb and Irp
|
||
{
|
||
Srb->OriginalRequest = irp;
|
||
Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
||
Srb->DataTransferLength = BufferLength;
|
||
Srb->SrbFlags = deviceExtension->SrbFlags;
|
||
|
||
// Disable synchronous transfer for these requests.
|
||
SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
|
||
|
||
if (BufferAddress != NULL)
|
||
{
|
||
if (WriteToDevice)
|
||
{
|
||
SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
|
||
ioctlCode = IOCTL_SCSI_EXECUTE_OUT;
|
||
}
|
||
else
|
||
{
|
||
SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
ioctlCode = IOCTL_SCSI_EXECUTE_IN;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ioctlCode = IOCTL_SCSI_EXECUTE_NONE;
|
||
}
|
||
|
||
|
||
// Zero out status.
|
||
Srb->ScsiStatus = 0;
|
||
Srb->SrbStatus = 0;
|
||
Srb->NextSrb = NULL;
|
||
|
||
// irp related fields
|
||
irp->MdlAddress = mdlAddress;
|
||
|
||
nextStack = IoGetNextIrpStackLocation(irp);
|
||
|
||
nextStack->MajorFunction = IRP_MJ_SCSI;
|
||
nextStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
|
||
nextStack->Parameters.Scsi.Srb = Srb;
|
||
}
|
||
|
||
// 4.3 send Request to lower driver.
|
||
status = DeviceSendRequestSynchronously(Device, request, TRUE);
|
||
|
||
if (status != STATUS_CANCELLED)
|
||
{
|
||
NT_ASSERT(SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_PENDING);
|
||
NT_ASSERT(status != STATUS_PENDING);
|
||
NT_ASSERT(!(Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN));
|
||
|
||
// 4.4 error process.
|
||
if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS)
|
||
{
|
||
LONGLONG retryIntervalIn100ns = 0;
|
||
|
||
// Update status and determine if request should be retried.
|
||
retry = RequestSenseInfoInterpret(deviceExtension,
|
||
request,
|
||
Srb,
|
||
retryCount,
|
||
&status,
|
||
&retryIntervalIn100ns);
|
||
|
||
if (retry)
|
||
{
|
||
LARGE_INTEGER t;
|
||
t.QuadPart = -retryIntervalIn100ns;
|
||
retryCount++;
|
||
KeDelayExecutionThread(KernelMode, FALSE, &t);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Request succeeded.
|
||
fdoData->LoggedTURFailureSinceLastIO = FALSE;
|
||
status = STATUS_SUCCESS;
|
||
retry = FALSE;
|
||
}
|
||
}
|
||
} while(retry);
|
||
|
||
if ((zpoddInfo != NULL) &&
|
||
(zpoddInfo->MonitorStartStopUnit != FALSE) &&
|
||
(SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS))
|
||
{
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_POWER,
|
||
"DeviceSendSrbSynchronously: soft eject detected, device marked as active\n"));
|
||
|
||
DeviceMarkActive(deviceExtension, TRUE, FALSE);
|
||
}
|
||
|
||
// 5. Release all allocated resources.
|
||
|
||
// required even though we allocated our own, since the port driver may
|
||
// have allocated one also
|
||
if (PORT_ALLOCATED_SENSE(deviceExtension, Srb))
|
||
{
|
||
FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, Srb);
|
||
}
|
||
|
||
Exit:
|
||
|
||
if (senseInfoBuffer != NULL)
|
||
{
|
||
FREE_POOL(senseInfoBuffer);
|
||
}
|
||
|
||
Srb->SenseInfoBuffer = NULL;
|
||
Srb->SenseInfoBufferLength = 0;
|
||
|
||
if (mdlAddress)
|
||
{
|
||
if (memoryLocked)
|
||
{
|
||
MmUnlockPages(mdlAddress);
|
||
memoryLocked = FALSE;
|
||
}
|
||
|
||
IoFreeMdl(mdlAddress);
|
||
irp->MdlAddress = NULL;
|
||
}
|
||
|
||
if (request)
|
||
{
|
||
WdfObjectDelete(request);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
DeviceSendNotification(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ const GUID* Guid,
|
||
_In_ ULONG ExtraDataSize,
|
||
_In_opt_ PVOID ExtraData
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
send notification to other components
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context.
|
||
|
||
Guid - GUID for the notification
|
||
|
||
ExtraDataSize - data size along with notification
|
||
|
||
ExtraData - data buffer send with notification
|
||
|
||
Return Value:
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PTARGET_DEVICE_CUSTOM_NOTIFICATION notification;
|
||
ULONG requiredSize;
|
||
NTSTATUS status;
|
||
|
||
status = RtlULongAdd((sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)),
|
||
ExtraDataSize,
|
||
&requiredSize);
|
||
|
||
if (!(NT_SUCCESS(status)) || (requiredSize > 0x0000ffff))
|
||
{
|
||
// MAX_USHORT, max total size for these events!
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_MCN,
|
||
"Error sending event: size too large! (%x)\n",
|
||
requiredSize));
|
||
return;
|
||
}
|
||
|
||
notification = ExAllocatePoolWithTag(NonPagedPoolNx,
|
||
requiredSize,
|
||
CDROM_TAG_NOTIFICATION);
|
||
|
||
// if none allocated, exit
|
||
if (notification == NULL)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Prepare and send the request!
|
||
RtlZeroMemory(notification, requiredSize);
|
||
notification->Version = 1;
|
||
notification->Size = (USHORT)(requiredSize);
|
||
notification->FileObject = NULL;
|
||
notification->NameBufferOffset = -1;
|
||
notification->Event = *Guid;
|
||
|
||
if (ExtraData != NULL)
|
||
{
|
||
RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
|
||
}
|
||
|
||
IoReportTargetDeviceChangeAsynchronous(DeviceExtension->LowerPdo,
|
||
notification,
|
||
NULL,
|
||
NULL);
|
||
|
||
FREE_POOL(notification);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
DeviceSendStartUnit(
|
||
_In_ WDFDEVICE Device
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send command to SCSI unit to start or power up.
|
||
Because this command is issued asynchronounsly, that is, without
|
||
waiting on it to complete, the IMMEDIATE flag is not set. This
|
||
means that the CDB will not return until the drive has powered up.
|
||
This should keep subsequent requests from being submitted to the
|
||
device before it has completely spun up.
|
||
|
||
This routine is called from the InterpretSense routine, when a
|
||
request sense returns data indicating that a drive must be
|
||
powered up.
|
||
|
||
This routine may also be called from a class driver's error handler,
|
||
or anytime a non-critical start device should be sent to the device.
|
||
|
||
Arguments:
|
||
|
||
Device - The device object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
|
||
WDF_OBJECT_ATTRIBUTES attributes;
|
||
WDFREQUEST startUnitRequest = NULL;
|
||
WDFMEMORY inputMemory = NULL;
|
||
|
||
PCOMPLETION_CONTEXT context = NULL;
|
||
PSCSI_REQUEST_BLOCK srb = NULL;
|
||
PCDB cdb = NULL;
|
||
|
||
deviceExtension = DeviceGetExtension(Device);
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// Allocate Srb from nonpaged pool.
|
||
context = ExAllocatePoolWithTag(NonPagedPoolNx,
|
||
sizeof(COMPLETION_CONTEXT),
|
||
CDROM_TAG_COMPLETION_CONTEXT);
|
||
|
||
if (context == NULL)
|
||
{
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceSendStartUnit: Failed to allocate completion context\n"));
|
||
|
||
status = STATUS_INTERNAL_ERROR;
|
||
}
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// Save the device object in the context for use by the completion
|
||
// routine.
|
||
context->Device = Device;
|
||
srb = &context->Srb;
|
||
|
||
// Zero out srb.
|
||
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
|
||
// setup SRB structure.
|
||
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
||
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
srb->TimeOutValue = START_UNIT_TIMEOUT;
|
||
|
||
srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
|
||
SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
||
|
||
// setup CDB
|
||
srb->CdbLength = 6;
|
||
cdb = (PCDB)srb->Cdb;
|
||
|
||
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
||
cdb->START_STOP.Start = 1;
|
||
cdb->START_STOP.Immediate = 0;
|
||
cdb->START_STOP.LogicalUnitNumber = srb->Lun;
|
||
|
||
//Create Request for sending down to port driver
|
||
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
|
||
CDROM_REQUEST_CONTEXT);
|
||
attributes.ParentObject = deviceExtension->IoTarget;
|
||
|
||
status = WdfRequestCreate(&attributes,
|
||
deviceExtension->IoTarget,
|
||
&startUnitRequest);
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
srb->OriginalRequest = WdfRequestWdmGetIrp(startUnitRequest);
|
||
NT_ASSERT(srb->OriginalRequest != NULL);
|
||
|
||
//Prepare the request
|
||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||
attributes.ParentObject = startUnitRequest;
|
||
|
||
status = WdfMemoryCreatePreallocated(&attributes,
|
||
(PVOID)srb,
|
||
sizeof(SCSI_REQUEST_BLOCK),
|
||
&inputMemory);
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
|
||
startUnitRequest,
|
||
IOCTL_SCSI_EXECUTE_NONE,
|
||
inputMemory,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// Set a CompletionRoutine callback function.
|
||
WdfRequestSetCompletionRoutine(startUnitRequest,
|
||
DeviceAsynchronousCompletion,
|
||
context);
|
||
|
||
status = RequestSend(deviceExtension,
|
||
startUnitRequest,
|
||
deviceExtension->IoTarget,
|
||
0,
|
||
NULL);
|
||
}
|
||
|
||
// release resources when failed.
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
FREE_POOL(context);
|
||
if (startUnitRequest != NULL)
|
||
{
|
||
WdfObjectDelete(startUnitRequest);
|
||
}
|
||
}
|
||
|
||
return;
|
||
} // end StartUnit()
|
||
|
||
|
||
VOID
|
||
DeviceSendIoctlAsynchronously(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ ULONG IoControlCode,
|
||
_In_ PDEVICE_OBJECT TargetDeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send an IOCTL asynchronously
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context.
|
||
IoControlCode - IOCTL code.
|
||
TargetDeviceObject - target device object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PIRP irp = NULL;
|
||
PIO_STACK_LOCATION nextIrpStack = NULL;
|
||
|
||
irp = IoAllocateIrp(DeviceExtension->DeviceObject->StackSize, FALSE);
|
||
|
||
if (irp != NULL)
|
||
{
|
||
nextIrpStack = IoGetNextIrpStackLocation(irp);
|
||
|
||
nextIrpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
||
|
||
nextIrpStack->Parameters.DeviceIoControl.OutputBufferLength = 0;
|
||
nextIrpStack->Parameters.DeviceIoControl.InputBufferLength = 0;
|
||
nextIrpStack->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
|
||
nextIrpStack->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
||
|
||
IoSetCompletionRoutine(irp,
|
||
RequestAsynchronousIrpCompletion,
|
||
DeviceExtension,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
(VOID) IoCallDriver(TargetDeviceObject, irp);
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
RequestAsynchronousIrpCompletion(
|
||
_In_ PDEVICE_OBJECT DeviceObject,
|
||
_In_ PIRP Irp,
|
||
_In_reads_opt_(_Inexpressible_("varies")) PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free the Irp.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - device that the completion routine fires on.
|
||
|
||
Irp - The irp to be completed.
|
||
|
||
Context - IRP context
|
||
|
||
Return Value:
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
IoFreeIrp(Irp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
VOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
DeviceAsynchronousCompletion(
|
||
_In_ WDFREQUEST Request,
|
||
_In_ WDFIOTARGET Target,
|
||
_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
|
||
_In_ WDFCONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when an asynchronous I/O request
|
||
which was issused by the class driver completes. Examples of such requests
|
||
are release queue or START UNIT. This routine releases the queue if
|
||
necessary. It then frees the context and the IRP.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - The device object for the logical unit; however since this
|
||
is the top stack location the value is NULL.
|
||
|
||
Irp - Supplies a pointer to the Irp to be processed.
|
||
|
||
Context - Supplies the context to be used to process this request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PCOMPLETION_CONTEXT context = (PCOMPLETION_CONTEXT)Context;
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(context->Device);
|
||
|
||
UNREFERENCED_PARAMETER(Target);
|
||
UNREFERENCED_PARAMETER(Params);
|
||
|
||
// If this is an execute srb, then check the return status and make sure.
|
||
// the queue is not frozen.
|
||
if (context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI)
|
||
{
|
||
// Check for a frozen queue.
|
||
if (context->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN)
|
||
{
|
||
// Unfreeze the queue getting the device object from the context.
|
||
DeviceReleaseQueue(context->Device);
|
||
}
|
||
}
|
||
|
||
// free port-allocated sense buffer if we can detect
|
||
//
|
||
if (PORT_ALLOCATED_SENSE(deviceExtension, &context->Srb))
|
||
{
|
||
FREE_PORT_ALLOCATED_SENSE_BUFFER(deviceExtension, &context->Srb);
|
||
}
|
||
|
||
FREE_POOL(context);
|
||
|
||
WdfObjectDelete(Request);
|
||
|
||
} // end DeviceAsynchronousCompletion()
|
||
|
||
|
||
VOID
|
||
DeviceReleaseQueue(
|
||
_In_ WDFDEVICE Device
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine issues an internal device control command
|
||
to the port driver to release a frozen queue. The call
|
||
is issued asynchronously as DeviceReleaseQueue will be invoked
|
||
from the IO completion DPC (and will have no context to
|
||
wait for a synchronous call to complete).
|
||
|
||
This routine must be called with the remove lock held.
|
||
|
||
Arguments:
|
||
|
||
Device - The functional device object for the device with the frozen queue.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
|
||
PSCSI_REQUEST_BLOCK srb = NULL;
|
||
KIRQL currentIrql;
|
||
|
||
// we raise irql seperately so we're not swapped out or suspended
|
||
// while holding the release queue irp in this routine. this lets
|
||
// us release the spin lock before lowering irql.
|
||
KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql);
|
||
|
||
WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock);
|
||
|
||
if (deviceExtension->ReleaseQueueInProgress)
|
||
{
|
||
// Someone is already doing this work - just set the flag to indicate that
|
||
// we need to release the queue again.
|
||
deviceExtension->ReleaseQueueNeeded = TRUE;
|
||
WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
|
||
KeLowerIrql(currentIrql);
|
||
|
||
return;
|
||
}
|
||
|
||
// Mark that there is a release queue in progress and drop the spinlock.
|
||
deviceExtension->ReleaseQueueInProgress = TRUE;
|
||
|
||
WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
|
||
|
||
srb = &(deviceExtension->ReleaseQueueSrb);
|
||
|
||
// Optical media are removable, so we just flush the queue. This will also release it.
|
||
srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
|
||
|
||
srb->OriginalRequest = WdfRequestWdmGetIrp(deviceExtension->ReleaseQueueRequest);
|
||
|
||
// Set a CompletionRoutine callback function.
|
||
WdfRequestSetCompletionRoutine(deviceExtension->ReleaseQueueRequest,
|
||
DeviceReleaseQueueCompletion,
|
||
Device);
|
||
// Send the request. If an error occurs, complete the request.
|
||
RequestSend(deviceExtension,
|
||
deviceExtension->ReleaseQueueRequest,
|
||
deviceExtension->IoTarget,
|
||
WDF_REQUEST_SEND_OPTION_IGNORE_TARGET_STATE,
|
||
NULL);
|
||
|
||
KeLowerIrql(currentIrql);
|
||
|
||
return;
|
||
|
||
} // end DeviceReleaseQueue()
|
||
|
||
VOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
DeviceReleaseQueueCompletion(
|
||
_In_ WDFREQUEST Request,
|
||
_In_ WDFIOTARGET Target,
|
||
_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
|
||
_In_ WDFCONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when an asynchronous release queue request which
|
||
was issused in DeviceReleaseQueue completes. This routine prepares for
|
||
the next release queue request and resends it if necessary.
|
||
|
||
Arguments:
|
||
|
||
Request - The completed request.
|
||
|
||
Target - IoTarget object
|
||
|
||
Params - Completion parameters
|
||
|
||
Context - WDFDEVICE object handle.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
WDFDEVICE device = Context;
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
|
||
|
||
BOOLEAN releaseQueueNeeded = FALSE;
|
||
WDF_REQUEST_REUSE_PARAMS params = {0};
|
||
|
||
UNREFERENCED_PARAMETER(Target);
|
||
UNREFERENCED_PARAMETER(Params);
|
||
|
||
WDF_REQUEST_REUSE_PARAMS_INIT(¶ms,
|
||
WDF_REQUEST_REUSE_NO_FLAGS,
|
||
STATUS_SUCCESS);
|
||
|
||
// Grab the spinlock and clear the release queue in progress flag so others
|
||
// can run. Save (and clear) the state of the release queue needed flag
|
||
// so that we can issue a new release queue outside the spinlock.
|
||
WdfSpinLockAcquire(deviceExtension->ReleaseQueueSpinLock);
|
||
|
||
releaseQueueNeeded = deviceExtension->ReleaseQueueNeeded;
|
||
|
||
deviceExtension->ReleaseQueueNeeded = FALSE;
|
||
deviceExtension->ReleaseQueueInProgress = FALSE;
|
||
|
||
// Reuse the ReleaseQueueRequest for the next time.
|
||
status = WdfRequestReuse(Request,¶ms);
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// Preformat the ReleaseQueueRequest for the next time.
|
||
// This should always succeed because it was already preformatted once during device initialization
|
||
status = WdfIoTargetFormatRequestForInternalIoctlOthers(deviceExtension->IoTarget,
|
||
Request,
|
||
IOCTL_SCSI_EXECUTE_NONE,
|
||
deviceExtension->ReleaseQueueInputMemory,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"DeviceReleaseQueueCompletion: WdfIoTargetFormatRequestForInternalIoctlOthers failed, %!STATUS!\n",
|
||
status));
|
||
}
|
||
|
||
RequestClearSendTime(Request);
|
||
|
||
WdfSpinLockRelease(deviceExtension->ReleaseQueueSpinLock);
|
||
|
||
// If we need a release queue then issue one now. Another processor may
|
||
// have already started one in which case we'll try to issue this one after
|
||
// it is done - but we should never recurse more than one deep.
|
||
if (releaseQueueNeeded)
|
||
{
|
||
DeviceReleaseQueue(device);
|
||
}
|
||
|
||
return;
|
||
|
||
} // DeviceReleaseQueueCompletion()
|
||
|
||
|
||
//
|
||
// In order to provide better performance without the need to reboot,
|
||
// we need to implement a self-adjusting method to set and clear the
|
||
// srb flags based upon current performance.
|
||
//
|
||
// whenever there is an error, immediately grab the spin lock. the
|
||
// MP perf hit here is acceptable, since we're in an error path. this
|
||
// is also neccessary because we are guaranteed to be modifying the
|
||
// SRB flags here, setting SuccessfulIO to zero, and incrementing the
|
||
// actual error count (which is always done within this spinlock).
|
||
//
|
||
// whenever there is no error, increment a counter. if there have been
|
||
// errors on the device, and we've enabled dynamic perf, *and* we've
|
||
// just crossed the perf threshhold, then grab the spin lock and
|
||
// double check that the threshhold has, indeed been hit(*). then
|
||
// decrement the error count, and if it's dropped sufficiently, undo
|
||
// some of the safety changes made in the SRB flags due to the errors.
|
||
//
|
||
// * this works in all cases. even if lots of ios occur after the
|
||
// previous guy went in and cleared the successfulio counter, that
|
||
// just means that we've hit the threshhold again, and so it's proper
|
||
// to run the inner loop again.
|
||
//
|
||
|
||
VOID
|
||
DevicePerfIncrementErrorCount(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
||
)
|
||
{
|
||
PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData;
|
||
KIRQL oldIrql;
|
||
ULONG errors;
|
||
|
||
KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
|
||
|
||
fdoData->Perf.SuccessfulIO = 0; // implicit interlock
|
||
errors = InterlockedIncrement((PLONG)&DeviceExtension->ErrorCount);
|
||
|
||
if (errors >= CLASS_ERROR_LEVEL_1)
|
||
{
|
||
// If the error count has exceeded the error limit, then disable
|
||
// any tagged queuing, multiple requests per lu queueing
|
||
// and sychronous data transfers.
|
||
//
|
||
// Clearing the no queue freeze flag prevents the port driver
|
||
// from sending multiple requests per logical unit.
|
||
CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
|
||
CLEAR_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
|
||
|
||
SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"PerfIncrementErrorCount: Too many errors; disabling tagged queuing and "
|
||
"synchronous data tranfers.\n"));
|
||
}
|
||
|
||
if (errors >= CLASS_ERROR_LEVEL_2)
|
||
{
|
||
// If a second threshold is reached, disable disconnects.
|
||
SET_FLAG(DeviceExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_GENERAL,
|
||
"PerfIncrementErrorCount: Too many errors; disabling disconnects.\n"));
|
||
}
|
||
|
||
KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
|
||
return;
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
PVOID
|
||
DeviceFindFeaturePage(
|
||
_In_reads_bytes_(Length) PGET_CONFIGURATION_HEADER FeatureBuffer,
|
||
_In_ ULONG const Length,
|
||
_In_ FEATURE_NUMBER const Feature
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
find the specific feature page in the buffer
|
||
|
||
Arguments:
|
||
|
||
FeatureBuffer - buffer contains the device feature set.
|
||
|
||
Length - buffer length
|
||
|
||
Feature - the feature number looking for.
|
||
|
||
Return Value:
|
||
|
||
PVOID - pointer to the starting location of the specific feature in buffer.
|
||
|
||
--*/
|
||
{
|
||
PUCHAR buffer;
|
||
PUCHAR limit;
|
||
ULONG validLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
// Calculate the length of valid data available in the
|
||
// capabilities buffer from the DataLength field
|
||
REVERSE_BYTES(&validLength, FeatureBuffer->DataLength);
|
||
|
||
validLength += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
|
||
|
||
// set limit to point to first illegal address
|
||
limit = (PUCHAR)FeatureBuffer;
|
||
limit += min(Length, validLength);
|
||
|
||
// set buffer to point to first page
|
||
buffer = FeatureBuffer->Data;
|
||
|
||
// loop through each page until we find the requested one, or
|
||
// until it's not safe to access the entire feature header
|
||
// (if equal, have exactly enough for the feature header)
|
||
while (buffer + sizeof(FEATURE_HEADER) <= limit)
|
||
{
|
||
PFEATURE_HEADER header = (PFEATURE_HEADER)buffer;
|
||
FEATURE_NUMBER thisFeature;
|
||
|
||
thisFeature = (header->FeatureCode[0] << 8) |
|
||
(header->FeatureCode[1]);
|
||
|
||
if (thisFeature == Feature)
|
||
{
|
||
PUCHAR temp;
|
||
|
||
// if don't have enough memory to safely access all the feature
|
||
// information, return NULL
|
||
temp = buffer;
|
||
temp += sizeof(FEATURE_HEADER);
|
||
temp += header->AdditionalLength;
|
||
|
||
if (temp > limit)
|
||
{
|
||
// this means the transfer was cut-off, an insufficiently
|
||
// small buffer was given, or other arbitrary error. since
|
||
// it's not safe to view the amount of data (even though
|
||
// the header is safe) in this feature, pretend it wasn't
|
||
// transferred at all...
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
||
"Feature %x exists, but not safe to access all its data. returning NULL\n",
|
||
Feature));
|
||
return NULL;
|
||
}
|
||
else
|
||
{
|
||
return buffer;
|
||
}
|
||
}
|
||
|
||
if ((header->AdditionalLength % 4) &&
|
||
!(Feature >= 0xff00 && Feature <= 0xffff))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
buffer += sizeof(FEATURE_HEADER);
|
||
buffer += header->AdditionalLength;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
VOID
|
||
DevicePrintAllFeaturePages(
|
||
_In_reads_bytes_(Usable) PGET_CONFIGURATION_HEADER Buffer,
|
||
_In_ ULONG const Usable
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
print out all feature pages in the buffer
|
||
|
||
Arguments:
|
||
|
||
Buffer - buffer contains the device feature set.
|
||
|
||
Usable -
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
{
|
||
#if DBG
|
||
PFEATURE_HEADER header;
|
||
|
||
PAGED_CODE();
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// items expected to ALWAYS be current if they exist
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureProfileList);
|
||
if (header != NULL) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: CurrentProfile %x "
|
||
"with %x bytes of data at %p\n",
|
||
Buffer->CurrentProfile[0] << 8 |
|
||
Buffer->CurrentProfile[1],
|
||
Usable, Buffer));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureCore);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"CORE Features"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureMorphing);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Morphing"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureRemovableMedium);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Removable Medium"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeaturePowerManagement);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Power Management"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureEmbeddedChanger);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Embedded Changer"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureMicrocodeUpgrade);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Microcode Update"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureTimeout);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Timeouts"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureLogicalUnitSerialNumber);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"LUN Serial Number"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureFirmwareDate);
|
||
if (header) {
|
||
|
||
ULONG featureSize = header->AdditionalLength;
|
||
featureSize += RTL_SIZEOF_THROUGH_FIELD(FEATURE_HEADER, AdditionalLength);
|
||
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Firmware Date"
|
||
));
|
||
|
||
if (featureSize >= RTL_SIZEOF_THROUGH_FIELD(FEATURE_DATA_FIRMWARE_DATE, Minute))
|
||
{
|
||
PFEATURE_DATA_FIRMWARE_DATE date = (PFEATURE_DATA_FIRMWARE_DATE)header;
|
||
// show date as "YYYY/MM/DD hh:mm", which is 18 chars (17+NULL)
|
||
UCHAR dateString[18] = { 0 };
|
||
dateString[ 0] = date->Year[0];
|
||
dateString[ 1] = date->Year[1];
|
||
dateString[ 2] = date->Year[2];
|
||
dateString[ 3] = date->Year[3];
|
||
dateString[ 4] = '/';
|
||
dateString[ 5] = date->Month[0];
|
||
dateString[ 6] = date->Month[1];
|
||
dateString[ 7] = '/';
|
||
dateString[ 8] = date->Day[0];
|
||
dateString[ 9] = date->Day[1];
|
||
dateString[10] = ' ';
|
||
dateString[11] = ' ';
|
||
dateString[12] = date->Hour[0];
|
||
dateString[13] = date->Hour[1];
|
||
dateString[14] = ':';
|
||
dateString[15] = date->Minute[0];
|
||
dateString[16] = date->Minute[1];
|
||
dateString[17] = 0;
|
||
// SECONDS IS NOT AVAILABLE ON EARLY IMPLEMENTATIONS -- ignore it
|
||
//dateString[17] = ':';
|
||
//dateString[18] = date->Seconds[0];
|
||
//dateString[19] = date->Seconds[1];
|
||
//dateString[20] = 0;
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: Firmware Date/Time %s (UTC)\n",
|
||
(PCSTR)dateString
|
||
));
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
// items expected not to always be current
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteProtect);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Software Write Protect"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomReadable);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Random Reads"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureMultiRead);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Multi-Read"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdRead);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"reading from CD-ROM/R/RW"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRead);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DVD Structure Reads"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureRandomWritable);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Random Writes"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureIncrementalStreamingWritable);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Incremental Streaming Writing"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureSectorErasable);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Sector Erasable Media"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureFormattable);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Formatting"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDefectManagement);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"defect management"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureWriteOnce);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Write Once Media"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureRestrictedOverwrite);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Restricted Overwrites"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdrwCAVWrite);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"CD-RW CAV recording"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureMrw);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Mount Rainier media"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureEnhancedDefectReporting);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Enhanced Defect Reporting"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdPlusRW);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DVD+RW media"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureRigidRestrictedOverwrite);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Rigid Restricted Overwrite"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdTrackAtOnce);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"CD Recording (Track At Once)"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureCdMastering);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"CD Recording (Mastering)"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdRecordableWrite);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DVD Recording (Mastering)"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRead);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DD CD Reading"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWrite);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DD CD-R Writing"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDDCDRWWrite);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DD CD-RW Writing"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureLayerJumpRecording);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Layer Jump Recording"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDRead);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"HD-DVD Reading"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureHDDVDWrite);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"HD-DVD Writing"
|
||
));
|
||
}
|
||
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureSMART);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"S.M.A.R.T."
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureCDAudioAnalogPlay);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Analogue CD Audio Operations"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCSS);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DVD CSS"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureRealTimeStreaming);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"Real-time Streaming Reads"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDiscControlBlocks);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DVD Disc Control Blocks"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureDvdCPRM);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"DVD CPRM"
|
||
));
|
||
}
|
||
|
||
header = DeviceFindFeaturePage(Buffer, Usable, FeatureAACS);
|
||
if (header) {
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_INIT,
|
||
"CdromGetConfiguration: %s %s\n",
|
||
(header->Current ?
|
||
"Currently supports" : "Is able to support"),
|
||
"AACS"
|
||
));
|
||
}
|
||
|
||
#else
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(Usable);
|
||
UNREFERENCED_PARAMETER(Buffer);
|
||
|
||
#endif // DBG
|
||
return;
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||
NTSTATUS
|
||
MediaReadCapacity(
|
||
_In_ WDFDEVICE Device
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Get media capacity
|
||
|
||
Arguments:
|
||
|
||
Device - the device that owns the media
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb = NULL;
|
||
READ_CAPACITY_DATA capacityData;
|
||
|
||
PAGED_CODE();
|
||
|
||
RtlZeroMemory(&srb, sizeof(srb));
|
||
RtlZeroMemory(&capacityData, sizeof(capacityData));
|
||
|
||
cdb = (PCDB)(&srb.Cdb);
|
||
|
||
//Prepare SCSI command fields
|
||
srb.CdbLength = 10;
|
||
srb.TimeOutValue = CDROM_READ_CAPACITY_TIMEOUT;
|
||
cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
|
||
|
||
status = DeviceSendSrbSynchronously(Device,
|
||
&srb,
|
||
&capacityData,
|
||
sizeof(READ_CAPACITY_DATA),
|
||
FALSE,
|
||
NULL);
|
||
|
||
//Remember the result
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
//Set the BytesPerBlock to zero, this is for safe as if error happens this field should stay zero (no change).
|
||
//it will be treated as error case in MediaReadCapacityDataInterpret()
|
||
capacityData.BytesPerBlock = 0;
|
||
}
|
||
|
||
MediaReadCapacityDataInterpret(Device, &capacityData);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
VOID
|
||
MediaReadCapacityDataInterpret(
|
||
_In_ WDFDEVICE Device,
|
||
_In_ PREAD_CAPACITY_DATA ReadCapacityBuffer
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
Interpret media capacity and set corresponding fields in device context
|
||
|
||
Arguments:
|
||
|
||
Device - the device that owns the media
|
||
|
||
ReadCapacityBuffer - data buffer of capacity
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
{
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
|
||
ULONG lastSector = 0;
|
||
ULONG bps = 0;
|
||
ULONG lastBit = 0;
|
||
ULONG bytesPerBlock = 0;
|
||
BOOLEAN errorHappened = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
NT_ASSERT(ReadCapacityBuffer);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
||
"MediaReadCapacityDataInterpret: Entering\n"));
|
||
|
||
// Swizzle bytes from Read Capacity and translate into
|
||
// the necessary geometry information in the device extension.
|
||
bytesPerBlock = ReadCapacityBuffer->BytesPerBlock;
|
||
|
||
((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3;
|
||
((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2;
|
||
((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1;
|
||
((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0;
|
||
|
||
// Insure that bps is a power of 2.
|
||
// This corrects a problem with the HP 4020i CDR where it
|
||
// returns an incorrect number for bytes per sector.
|
||
if (!bps)
|
||
{
|
||
// Set disk geometry to default values (per ISO 9660).
|
||
bps = 2048;
|
||
errorHappened = TRUE;
|
||
}
|
||
else
|
||
{
|
||
lastBit = (ULONG)(-1);
|
||
while (bps)
|
||
{
|
||
lastBit++;
|
||
bps = (bps >> 1);
|
||
}
|
||
bps = (1 << lastBit);
|
||
}
|
||
|
||
deviceExtension->DiskGeometry.BytesPerSector = bps;
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
||
"MediaReadCapacityDataInterpret: Calculated bps %#x\n",
|
||
deviceExtension->DiskGeometry.BytesPerSector));
|
||
|
||
// Copy last sector in reverse byte order.
|
||
bytesPerBlock = ReadCapacityBuffer->LogicalBlockAddress;
|
||
|
||
((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&bytesPerBlock)->Byte3;
|
||
((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&bytesPerBlock)->Byte2;
|
||
((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&bytesPerBlock)->Byte1;
|
||
((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&bytesPerBlock)->Byte0;
|
||
|
||
// Calculate sector to byte shift.
|
||
WHICH_BIT(bps, deviceExtension->SectorShift);
|
||
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
|
||
"MediaReadCapacityDataInterpret: Sector size is %d\n",
|
||
deviceExtension->DiskGeometry.BytesPerSector));
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
||
"MediaReadCapacityDataInterpret: Number of Sectors is %d\n",
|
||
lastSector + 1));
|
||
|
||
// Calculate media capacity in bytes.
|
||
if (errorHappened)
|
||
{
|
||
// Set disk geometry to default values (per ISO 9660).
|
||
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
|
||
}
|
||
else
|
||
{
|
||
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
|
||
deviceExtension->PartitionLength.QuadPart =
|
||
(deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
|
||
}
|
||
|
||
// we've defaulted to 32/64 forever. don't want to change this now...
|
||
deviceExtension->DiskGeometry.TracksPerCylinder = 0x40;
|
||
deviceExtension->DiskGeometry.SectorsPerTrack = 0x20;
|
||
|
||
// Calculate number of cylinders.
|
||
deviceExtension->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1) / (32 * 64));
|
||
|
||
deviceExtension->DiskGeometry.MediaType = RemovableMedia;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
VOID
|
||
DevicePickDvdRegion(
|
||
_In_ WDFDEVICE Device
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
pick a default dvd region
|
||
|
||
Arguments:
|
||
|
||
Device - Device Object
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
|
||
|
||
// these five pointers all point to dvdReadStructure or part of
|
||
// its data, so don't deallocate them more than once!
|
||
PDVD_READ_STRUCTURE dvdReadStructure;
|
||
PDVD_COPY_PROTECT_KEY copyProtectKey;
|
||
PDVD_COPYRIGHT_DESCRIPTOR dvdCopyRight;
|
||
PDVD_RPC_KEY rpcKey;
|
||
PDVD_SET_RPC_KEY dvdRpcKey;
|
||
|
||
size_t bytesReturned = 0;
|
||
ULONG bufferLen = 0;
|
||
UCHAR mediaRegion = 0;
|
||
ULONG pickDvdRegion = 0;
|
||
ULONG defaultDvdRegion = 0;
|
||
ULONG dvdRegion = 0;
|
||
WDFKEY registryKey = NULL;
|
||
|
||
DECLARE_CONST_UNICODE_STRING(registryValueName, DVD_DEFAULT_REGION);
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((pickDvdRegion = InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, 0)) == 0)
|
||
{
|
||
// it was non-zero, so either another thread will do this, or
|
||
// we no longer need to pick a region
|
||
return;
|
||
}
|
||
|
||
bufferLen = max(
|
||
max(sizeof(DVD_DESCRIPTOR_HEADER) +
|
||
sizeof(DVD_COPYRIGHT_DESCRIPTOR),
|
||
sizeof(DVD_READ_STRUCTURE)
|
||
),
|
||
max(DVD_RPC_KEY_LENGTH,
|
||
DVD_SET_RPC_KEY_LENGTH
|
||
)
|
||
);
|
||
|
||
dvdReadStructure = (PDVD_READ_STRUCTURE)
|
||
ExAllocatePoolWithTag(PagedPool, bufferLen, DVD_TAG_DVD_REGION);
|
||
|
||
if (dvdReadStructure == NULL)
|
||
{
|
||
InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion);
|
||
return;
|
||
}
|
||
|
||
copyProtectKey = (PDVD_COPY_PROTECT_KEY)dvdReadStructure;
|
||
|
||
dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR)
|
||
((PDVD_DESCRIPTOR_HEADER)dvdReadStructure)->Data;
|
||
|
||
// get the media region
|
||
RtlZeroMemory (dvdReadStructure, bufferLen);
|
||
dvdReadStructure->Format = DvdCopyrightDescriptor;
|
||
|
||
// Build and send a request for READ_KEY
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Getting Copyright Descriptor\n",
|
||
Device));
|
||
|
||
status = ReadDvdStructure(deviceExtension,
|
||
NULL,
|
||
dvdReadStructure,
|
||
sizeof(DVD_READ_STRUCTURE),
|
||
dvdReadStructure,
|
||
sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR),
|
||
&bytesReturned);
|
||
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Got Copyright Descriptor %x\n",
|
||
Device, status));
|
||
|
||
if ((NT_SUCCESS(status)) &&
|
||
(dvdCopyRight->CopyrightProtectionType == 0x01))
|
||
{
|
||
// keep the media region bitmap around
|
||
// a 1 means ok to play
|
||
if (dvdCopyRight->RegionManagementInformation == 0xff)
|
||
{
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): RegionManagementInformation "
|
||
"is set to dis-allow playback for all regions. This is "
|
||
"most likely a poorly authored disc. defaulting to all "
|
||
"region disc for purpose of choosing initial region\n",
|
||
Device));
|
||
dvdCopyRight->RegionManagementInformation = 0;
|
||
}
|
||
|
||
mediaRegion = ~dvdCopyRight->RegionManagementInformation;
|
||
}
|
||
else
|
||
{
|
||
// can't automatically pick a default region on a drive without media, so just exit
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): failed to auto-choose a region due to status %x getting copyright descriptor\n",
|
||
Device, status));
|
||
goto getout;
|
||
}
|
||
|
||
// get the device region
|
||
RtlZeroMemory (copyProtectKey, bufferLen);
|
||
copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
|
||
copyProtectKey->KeyType = DvdGetRpcKey;
|
||
|
||
// Build and send a request for READ_KEY for RPC key
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Getting RpcKey\n",
|
||
Device));
|
||
status = DvdStartSessionReadKey(deviceExtension,
|
||
IOCTL_DVD_READ_KEY,
|
||
NULL,
|
||
copyProtectKey,
|
||
DVD_RPC_KEY_LENGTH,
|
||
copyProtectKey,
|
||
DVD_RPC_KEY_LENGTH,
|
||
&bytesReturned);
|
||
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Got RpcKey %x\n",
|
||
Device, status));
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): failed to get RpcKey from "
|
||
"a DVD Device\n", Device));
|
||
goto getout;
|
||
}
|
||
|
||
// so we now have what we can get for the media region and the
|
||
// drive region. we will not set a region if the drive has one
|
||
// set already (mask is not all 1's), nor will we set a region
|
||
// if there are no more user resets available.
|
||
rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData;
|
||
|
||
if (rpcKey->RegionMask != 0xff)
|
||
{
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): not picking a region since "
|
||
"it is already chosen\n", Device));
|
||
goto getout;
|
||
}
|
||
|
||
if (rpcKey->UserResetsAvailable <= 1)
|
||
{
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): not picking a region since "
|
||
"only one change remains\n", Device));
|
||
goto getout;
|
||
}
|
||
|
||
// OOBE sets this key based upon the system locale
|
||
status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
|
||
KEY_READ,
|
||
WDF_NO_OBJECT_ATTRIBUTES,
|
||
®istryKey);
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
status = WdfRegistryQueryULong(registryKey,
|
||
®istryValueName,
|
||
&defaultDvdRegion);
|
||
|
||
WdfRegistryClose(registryKey);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): failed to read registry value due to status %x\n",
|
||
Device, status));
|
||
|
||
// by default the default Dvd region is 0
|
||
defaultDvdRegion = 0;
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (defaultDvdRegion > DVD_MAX_REGION)
|
||
{
|
||
// the registry has a bogus default
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): registry has a bogus default "
|
||
"region value of %x\n", Device, defaultDvdRegion));
|
||
|
||
defaultDvdRegion = 0;
|
||
}
|
||
|
||
// if defaultDvdRegion == 0, it means no default.
|
||
|
||
// we will select the initial dvd region for the user
|
||
|
||
if ((defaultDvdRegion != 0) &&
|
||
(mediaRegion & (1 << (defaultDvdRegion - 1))))
|
||
{
|
||
// first choice:
|
||
// the media has region that matches
|
||
// the default dvd region.
|
||
dvdRegion = (1 << (defaultDvdRegion - 1));
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Choice #1: media matches "
|
||
"drive's default, chose region %x\n", Device, dvdRegion));
|
||
}
|
||
else if (mediaRegion)
|
||
{
|
||
// second choice:
|
||
// pick the lowest region number from the media
|
||
UCHAR mask = 1;
|
||
dvdRegion = 0;
|
||
|
||
while (mediaRegion && !dvdRegion)
|
||
{
|
||
// pick the lowest bit
|
||
dvdRegion = mediaRegion & mask;
|
||
mask <<= 1;
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Choice #2: choosing lowest "
|
||
"media region %x\n", Device, dvdRegion));
|
||
}
|
||
else if (defaultDvdRegion)
|
||
{
|
||
// third choice:
|
||
// default dvd region from the dvd class installer
|
||
dvdRegion = (1 << (defaultDvdRegion - 1));
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Choice #3: using default "
|
||
"region for this install %x\n", Device, dvdRegion));
|
||
}
|
||
else
|
||
{
|
||
// unable to pick one for the user -- this should rarely
|
||
// happen, since the proppage dvd class installer sets
|
||
// the key based upon the system locale
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Choice #4: failed to choose "
|
||
"a media region\n", Device));
|
||
goto getout;
|
||
}
|
||
|
||
// now that we've chosen a region, set the region by sending the
|
||
// appropriate request to the drive
|
||
RtlZeroMemory (copyProtectKey, bufferLen);
|
||
copyProtectKey->KeyLength = DVD_SET_RPC_KEY_LENGTH;
|
||
copyProtectKey->KeyType = DvdSetRpcKey;
|
||
dvdRpcKey = (PDVD_SET_RPC_KEY)copyProtectKey->KeyData;
|
||
dvdRpcKey->PreferredDriveRegionCode = (UCHAR)~dvdRegion;
|
||
|
||
// Build and send request for SEND_KEY
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Sending new Rpc Key to region %x\n",
|
||
Device, dvdRegion));
|
||
|
||
status = DvdSendKey(deviceExtension,
|
||
NULL,
|
||
copyProtectKey,
|
||
DVD_SET_RPC_KEY_LENGTH,
|
||
&bytesReturned);
|
||
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL,
|
||
"DevicePickDvdRegion (%p): Sent new Rpc Key %x\n",
|
||
Device, status));
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): unable to set dvd initial "
|
||
" region code (%x)\n", Device, status));
|
||
}
|
||
else
|
||
{
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_IOCTL, "DevicePickDvdRegion (%p): Successfully set dvd "
|
||
"initial region\n", Device));
|
||
|
||
pickDvdRegion = 0;
|
||
}
|
||
|
||
getout:
|
||
if (dvdReadStructure)
|
||
{
|
||
FREE_POOL(dvdReadStructure);
|
||
}
|
||
|
||
// update the new PickDvdRegion value
|
||
InterlockedExchange((PLONG)&deviceExtension->DeviceAdditionalData.PickDvdRegion, pickDvdRegion);
|
||
|
||
return;
|
||
}
|
||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||
NTSTATUS
|
||
DeviceRegisterInterface(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ CDROM_DEVICE_INTERFACES InterfaceType
|
||
)
|
||
/*++
|
||
Routine Description:
|
||
|
||
used to register device class interface or mount device interface
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context
|
||
|
||
InterfaceType - interface type to be registered.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
WDFSTRING string = NULL;
|
||
GUID* interfaceGuid = NULL;
|
||
PUNICODE_STRING savingString = NULL;
|
||
BOOLEAN setRestricted = FALSE;
|
||
UNICODE_STRING localString;
|
||
|
||
PAGED_CODE();
|
||
|
||
//Get parameters
|
||
switch(InterfaceType)
|
||
{
|
||
case CdRomDeviceInterface:
|
||
interfaceGuid = (LPGUID)&GUID_DEVINTERFACE_CDROM;
|
||
setRestricted = TRUE;
|
||
savingString = &localString;
|
||
break;
|
||
case MountedDeviceInterface:
|
||
interfaceGuid = (LPGUID)&MOUNTDEV_MOUNTED_DEVICE_GUID;
|
||
savingString = &(DeviceExtension->MountedDeviceInterfaceName);
|
||
break;
|
||
default:
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
status = WdfDeviceCreateDeviceInterface(DeviceExtension->Device,
|
||
interfaceGuid,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
||
"DeviceRegisterInterface: Unable to register cdrom "
|
||
"DCA for fdo %p type: %s [%lx]\n",
|
||
DeviceExtension->Device,
|
||
(InterfaceType == CdRomDeviceInterface)? "CdRom Interface" : "Mounted Device Interface",
|
||
status));
|
||
}
|
||
|
||
// Retrieve interface string
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// The string object will be released when its parent object is released.
|
||
WDF_OBJECT_ATTRIBUTES attributes;
|
||
|
||
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
||
attributes.ParentObject = DeviceExtension->Device;
|
||
|
||
status = WdfStringCreate(WDF_NO_OBJECT_ATTRIBUTES,
|
||
NULL,
|
||
&string);
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
status = WdfDeviceRetrieveDeviceInterfaceString(DeviceExtension->Device,
|
||
interfaceGuid,
|
||
NULL,
|
||
string);
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
WdfStringGetUnicodeString(string, savingString);
|
||
|
||
if (setRestricted) {
|
||
|
||
|
||
WdfObjectDelete(string);
|
||
}
|
||
}
|
||
|
||
return status;
|
||
} // end DeviceRegisterInterface()
|
||
|
||
|
||
VOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
DeviceRestoreDefaultSpeed(
|
||
_In_ WDFWORKITEM WorkItem
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This workitem is called on a media change when the CDROM device
|
||
speed should be restored to the default value.
|
||
|
||
Arguments:
|
||
|
||
Fdo - Supplies the device object for the CDROM device.
|
||
WorkItem - Supplies the pointer to the workitem.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
WDFDEVICE device = WdfWorkItemGetParentObject(WorkItem);
|
||
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(device);
|
||
PPERFORMANCE_DESCRIPTOR perfDescriptor;
|
||
ULONG transferLength = sizeof(PERFORMANCE_DESCRIPTOR);
|
||
SCSI_REQUEST_BLOCK srb = {0};
|
||
PCDB cdb = (PCDB)srb.Cdb;
|
||
|
||
PAGED_CODE();
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL, "DeviceRestoreDefaultSpeed: Restore device speed for %p\n", device));
|
||
|
||
perfDescriptor = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
|
||
transferLength,
|
||
CDROM_TAG_STREAM);
|
||
if (perfDescriptor == NULL)
|
||
{
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory(perfDescriptor, transferLength);
|
||
|
||
perfDescriptor->RestoreDefaults = TRUE;
|
||
|
||
srb.TimeOutValue = deviceExtension->TimeOutValue;
|
||
|
||
srb.CdbLength = 12;
|
||
cdb->SET_STREAMING.OperationCode = SCSIOP_SET_STREAMING;
|
||
REVERSE_BYTES_SHORT(&cdb->SET_STREAMING.ParameterListLength, &transferLength);
|
||
|
||
status = DeviceSendSrbSynchronously(device,
|
||
&srb,
|
||
perfDescriptor,
|
||
transferLength,
|
||
TRUE,
|
||
NULL);
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"DeviceRestoreDefaultSpeed: Set Streaming command completed with status: 0x%X\n", status));
|
||
|
||
FREE_POOL(perfDescriptor);
|
||
WdfObjectDelete(WorkItem);
|
||
|
||
return;
|
||
}
|
||
|
||
// custom string match -- careful!
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
BOOLEAN
|
||
StringsAreMatched(
|
||
_In_opt_z_ PCHAR StringToMatch,
|
||
_In_z_ PCHAR TargetString
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
compares if two strings are identical
|
||
|
||
Arguments:
|
||
|
||
StringToMatch - source string.
|
||
TargetString - target string.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE (identical); FALSE (not match)
|
||
|
||
--*/
|
||
{
|
||
size_t length;
|
||
|
||
PAGED_CODE();
|
||
|
||
NT_ASSERT(TargetString);
|
||
|
||
// if no match requested, return TRUE
|
||
if (StringToMatch == NULL)
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
// cache the string length for efficiency
|
||
length = strlen(StringToMatch);
|
||
|
||
// ZERO-length strings may only match zero-length strings
|
||
if (length == 0)
|
||
{
|
||
return (strlen(TargetString) == 0);
|
||
}
|
||
|
||
// strncmp returns zero if the strings match
|
||
return (strncmp(StringToMatch, TargetString, length) == 0);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RequestSetContextFields(
|
||
_In_ WDFREQUEST Request,
|
||
_In_ PSYNC_HANDLER Handler
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
set the request object context fields
|
||
|
||
Arguments:
|
||
|
||
Request - request object.
|
||
Handler - the function that finally handles this request.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PCDROM_REQUEST_CONTEXT requestContext = RequestGetContext(Request);
|
||
PKEVENT syncEvent = NULL;
|
||
|
||
syncEvent = ExAllocatePoolWithTag(NonPagedPoolNx,
|
||
sizeof(KEVENT),
|
||
CDROM_TAG_SYNC_EVENT);
|
||
|
||
if (syncEvent == NULL)
|
||
{
|
||
// memory allocation failed.
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
else
|
||
{
|
||
// now, put the special synchronization information into the context
|
||
requestContext->SyncRequired = TRUE;
|
||
requestContext->SyncEvent = syncEvent;
|
||
requestContext->SyncCallback = Handler;
|
||
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RequestDuidGetDeviceIdProperty(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ WDFREQUEST Request,
|
||
_In_ WDF_REQUEST_PARAMETERS RequestParameters,
|
||
_Out_ size_t * DataLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context
|
||
Request - request object.
|
||
RequestParameters - request parameter
|
||
DataLength - transferred data length.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PSTORAGE_DEVICE_ID_DESCRIPTOR deviceIdDescriptor = NULL;
|
||
PSTORAGE_DESCRIPTOR_HEADER descHeader = NULL;
|
||
STORAGE_PROPERTY_ID propertyId = StorageDeviceIdProperty;
|
||
|
||
*DataLength = 0;
|
||
|
||
// Get the VPD page 83h data.
|
||
status = DeviceRetrieveDescriptor(DeviceExtension->Device,
|
||
&propertyId,
|
||
(PSTORAGE_DESCRIPTOR_HEADER*)&deviceIdDescriptor);
|
||
|
||
if (NT_SUCCESS(status) && (deviceIdDescriptor == NULL))
|
||
{
|
||
status = STATUS_NOT_FOUND;
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
status = WdfRequestRetrieveOutputBuffer(Request,
|
||
RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
|
||
&descHeader,
|
||
NULL);
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid = NULL;
|
||
ULONG offset = descHeader->Size;
|
||
PUCHAR dest = (PUCHAR)descHeader + offset;
|
||
size_t outputBufferSize;
|
||
|
||
outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
||
// Adjust required size and potential destination location.
|
||
status = RtlULongAdd(descHeader->Size, deviceIdDescriptor->Size, &descHeader->Size);
|
||
|
||
if (NT_SUCCESS(status) &&
|
||
(outputBufferSize < descHeader->Size))
|
||
{
|
||
// Output buffer is too small. Return error and make sure
|
||
// the caller gets info about required buffer size.
|
||
*DataLength = descHeader->Size;
|
||
status = STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader;
|
||
storageDuid->StorageDeviceIdOffset = offset;
|
||
|
||
RtlCopyMemory(dest,
|
||
deviceIdDescriptor,
|
||
deviceIdDescriptor->Size);
|
||
|
||
*DataLength = storageDuid->Size;
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
FREE_POOL(deviceIdDescriptor);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
RequestDuidGetDeviceProperty(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ WDFREQUEST Request,
|
||
_In_ WDF_REQUEST_PARAMETERS RequestParameters,
|
||
_Out_ size_t * DataLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context
|
||
Request - request object.
|
||
RequestParameters - request parameter
|
||
DataLength - transferred data length.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = DeviceExtension->DeviceDescriptor;
|
||
PSTORAGE_DESCRIPTOR_HEADER descHeader = NULL;
|
||
PSTORAGE_DEVICE_UNIQUE_IDENTIFIER storageDuid;
|
||
PUCHAR dest = NULL;
|
||
|
||
if (deviceDescriptor == NULL)
|
||
{
|
||
status = STATUS_NOT_FOUND;
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
status = WdfRequestRetrieveOutputBuffer(Request,
|
||
RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
|
||
&descHeader,
|
||
NULL);
|
||
}
|
||
|
||
if (NT_SUCCESS(status) &&
|
||
(deviceDescriptor->SerialNumberOffset == 0))
|
||
{
|
||
status = STATUS_NOT_FOUND;
|
||
}
|
||
|
||
// Use this info only if serial number is available.
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
ULONG offset = descHeader->Size;
|
||
size_t outputBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
||
// Adjust required size and potential destination location.
|
||
dest = (PUCHAR)descHeader + offset;
|
||
|
||
status = RtlULongAdd(descHeader->Size, deviceDescriptor->Size, &descHeader->Size);
|
||
|
||
if (NT_SUCCESS(status) &&
|
||
(outputBufferSize < descHeader->Size))
|
||
{
|
||
// Output buffer is too small. Return error and make sure
|
||
// the caller get info about required buffer size.
|
||
*DataLength = descHeader->Size;
|
||
status = STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
storageDuid = (PSTORAGE_DEVICE_UNIQUE_IDENTIFIER)descHeader;
|
||
storageDuid->StorageDeviceOffset = offset;
|
||
|
||
RtlCopyMemory(dest,
|
||
deviceDescriptor,
|
||
deviceDescriptor->Size);
|
||
|
||
*DataLength = storageDuid->Size;
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
ULONG
|
||
DeviceRetrieveModeSenseUsingScratch(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
|
||
_In_ ULONG Length,
|
||
_In_ UCHAR PageCode,
|
||
_In_ UCHAR PageControl
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
retrieve mode sense informaiton of the device
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context
|
||
ModeSenseBuffer - buffer to savee the mode sense info.
|
||
Length - buffer length
|
||
PageCode - .
|
||
PageControl -
|
||
|
||
Return Value:
|
||
|
||
ULONG - transferred data length
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
ULONG transferSize = min(Length, DeviceExtension->ScratchContext.ScratchBufferSize);
|
||
CDB cdb;
|
||
|
||
PAGED_CODE();
|
||
|
||
ScratchBuffer_BeginUse(DeviceExtension);
|
||
|
||
RtlZeroMemory(&cdb, sizeof(CDB));
|
||
// Set up the CDB
|
||
cdb.MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb.MODE_SENSE.PageCode = PageCode;
|
||
cdb.MODE_SENSE.Pc = PageControl;
|
||
cdb.MODE_SENSE.AllocationLength = (UCHAR)transferSize;
|
||
|
||
status = ScratchBuffer_ExecuteCdb(DeviceExtension, NULL, transferSize, TRUE, &cdb, 6);
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
transferSize = min(Length, DeviceExtension->ScratchContext.ScratchSrb->DataTransferLength);
|
||
RtlCopyMemory(ModeSenseBuffer,
|
||
DeviceExtension->ScratchContext.ScratchBuffer,
|
||
transferSize);
|
||
}
|
||
|
||
ScratchBuffer_EndUse(DeviceExtension);
|
||
|
||
return transferSize;
|
||
}
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
PVOID
|
||
ModeSenseFindSpecificPage(
|
||
_In_reads_bytes_(Length) PCHAR ModeSenseBuffer,
|
||
_In_ size_t Length,
|
||
_In_ UCHAR PageMode,
|
||
_In_ BOOLEAN Use6BytesCdb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine scans through the mode sense data and finds the requested
|
||
mode sense page code.
|
||
|
||
Arguments:
|
||
ModeSenseBuffer - Supplies a pointer to the mode sense data.
|
||
|
||
Length - Indicates the length of valid data.
|
||
|
||
PageMode - Supplies the page mode to be searched for.
|
||
|
||
Use6BytesCdb - Indicates whether 6 or 10 byte mode sense was used.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the the requested mode page. If the mode page was not found
|
||
then NULL is return.
|
||
|
||
--*/
|
||
{
|
||
PCHAR limit;
|
||
ULONG parameterHeaderLength;
|
||
PVOID result = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
limit = ModeSenseBuffer + Length;
|
||
parameterHeaderLength = (Use6BytesCdb)
|
||
? sizeof(MODE_PARAMETER_HEADER)
|
||
: sizeof(MODE_PARAMETER_HEADER10);
|
||
|
||
if (Length >= parameterHeaderLength)
|
||
{
|
||
PMODE_PARAMETER_HEADER10 modeParam10;
|
||
ULONG blockDescriptorLength;
|
||
|
||
// Skip the mode select header and block descriptors.
|
||
if (Use6BytesCdb)
|
||
{
|
||
blockDescriptorLength = ((PMODE_PARAMETER_HEADER)ModeSenseBuffer)->BlockDescriptorLength;
|
||
}
|
||
else
|
||
{
|
||
modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
|
||
blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
|
||
}
|
||
|
||
ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
|
||
|
||
// ModeSenseBuffer now points at pages. Walk the pages looking for the
|
||
// requested page until the limit is reached.
|
||
while (ModeSenseBuffer +
|
||
RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit)
|
||
{
|
||
if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode)
|
||
{
|
||
// found the mode page. make sure it's safe to touch it all
|
||
// before returning the pointer to caller
|
||
if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit)
|
||
{
|
||
// Return NULL since the page is not safe to access in full
|
||
result = NULL;
|
||
}
|
||
else
|
||
{
|
||
result = ModeSenseBuffer;
|
||
}
|
||
break;
|
||
}
|
||
|
||
// Advance to the next page which is 4-byte-aligned offset after this page.
|
||
ModeSenseBuffer += ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
|
||
RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
} // end ModeSenseFindSpecificPage()
|
||
|
||
|
||
_IRQL_requires_max_(PASSIVE_LEVEL)
|
||
NTSTATUS
|
||
PerformEjectionControl(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ WDFREQUEST Request,
|
||
_In_ MEDIA_LOCK_TYPE LockType,
|
||
_In_ BOOLEAN Lock
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
ejection control process
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device extension
|
||
Request - WDF request to be used for communication with the device
|
||
LockType - the type of lock
|
||
Lock - if TRUE, lock the device; if FALSE, unlock it
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PFILE_OBJECT_CONTEXT fileObjectContext = NULL;
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb = NULL;
|
||
|
||
LONG newLockCount = 0;
|
||
LONG newProtectedLockCount = 0;
|
||
LONG newInternalLockCount = 0;
|
||
LONG newFileLockCount = 0;
|
||
BOOLEAN countChanged = FALSE;
|
||
BOOLEAN previouslyLocked = FALSE;
|
||
BOOLEAN nowLocked = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
// Prevent race conditions while working with lock counts
|
||
status = WdfWaitLockAcquire(DeviceExtension->EjectSynchronizationLock, NULL);
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
NT_ASSERT(FALSE);
|
||
}
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"PerformEjectionControl: "
|
||
"Received request for %s lock type\n",
|
||
LockTypeStrings[LockType]
|
||
));
|
||
|
||
|
||
// If this is a "secured" request, retrieve the file object context
|
||
if (LockType == SecureMediaLock)
|
||
{
|
||
WDFFILEOBJECT fileObject = NULL;
|
||
|
||
fileObject = WdfRequestGetFileObject(Request);
|
||
|
||
if (fileObject == NULL)
|
||
{
|
||
status = STATUS_INVALID_HANDLE;
|
||
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
|
||
"FileObject does not match to the one in IRP_MJ_CREATE, KMDF returns NULL\n"));
|
||
goto Exit;
|
||
}
|
||
|
||
fileObjectContext = FileObjectGetContext(fileObject);
|
||
NT_ASSERT(fileObjectContext != NULL);
|
||
}
|
||
|
||
// Lock counts should never fall below 0
|
||
NT_ASSERT(DeviceExtension->LockCount >= 0);
|
||
NT_ASSERT(DeviceExtension->ProtectedLockCount >= 0);
|
||
NT_ASSERT(DeviceExtension->InternalLockCount >= 0);
|
||
|
||
// Get the current lock counts
|
||
newLockCount = DeviceExtension->LockCount;
|
||
newProtectedLockCount = DeviceExtension->ProtectedLockCount;
|
||
newInternalLockCount = DeviceExtension->InternalLockCount;
|
||
if (fileObjectContext)
|
||
{
|
||
// fileObjectContext->LockCount is ULONG and should always >= 0
|
||
newFileLockCount = fileObjectContext->LockCount;
|
||
}
|
||
|
||
// Determine which lock counts need to be changed and how
|
||
if (Lock && LockType == SimpleMediaLock)
|
||
{
|
||
newLockCount++;
|
||
countChanged = TRUE;
|
||
}
|
||
else if (Lock && LockType == SecureMediaLock)
|
||
{
|
||
newFileLockCount++;
|
||
newProtectedLockCount++;
|
||
countChanged = TRUE;
|
||
}
|
||
else if (Lock && LockType == InternalMediaLock)
|
||
{
|
||
newInternalLockCount++;
|
||
countChanged = TRUE;
|
||
}
|
||
else if (!Lock && LockType == SimpleMediaLock)
|
||
{
|
||
if (newLockCount != 0)
|
||
{
|
||
newLockCount--;
|
||
countChanged = TRUE;
|
||
}
|
||
}
|
||
else if (!Lock && LockType == SecureMediaLock)
|
||
{
|
||
if ( (newFileLockCount == 0) || (newProtectedLockCount == 0) )
|
||
{
|
||
status = STATUS_INVALID_DEVICE_STATE;
|
||
goto Exit;
|
||
}
|
||
newFileLockCount--;
|
||
newProtectedLockCount--;
|
||
countChanged = TRUE;
|
||
}
|
||
else if (!Lock && LockType == InternalMediaLock)
|
||
{
|
||
NT_ASSERT(newInternalLockCount != 0);
|
||
newInternalLockCount--;
|
||
countChanged = TRUE;
|
||
}
|
||
|
||
if ( (DeviceExtension->LockCount != 0) ||
|
||
(DeviceExtension->ProtectedLockCount != 0) ||
|
||
(DeviceExtension->InternalLockCount != 0) )
|
||
{
|
||
previouslyLocked = TRUE;
|
||
}
|
||
if ( (newLockCount != 0) ||
|
||
(newProtectedLockCount != 0) ||
|
||
(newInternalLockCount != 0) )
|
||
{
|
||
nowLocked = TRUE;
|
||
}
|
||
|
||
// Only send command down to device when necessary
|
||
if (previouslyLocked != nowLocked)
|
||
{
|
||
// Compose and send the PREVENT ALLOW MEDIA REMOVAL command.
|
||
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
|
||
srb.CdbLength = 6;
|
||
srb.TimeOutValue = DeviceExtension->TimeOutValue;
|
||
|
||
cdb = (PCDB)&srb.Cdb;
|
||
cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
|
||
cdb->MEDIA_REMOVAL.Prevent = Lock;
|
||
|
||
status = DeviceSendSrbSynchronously(DeviceExtension->Device,
|
||
&srb,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
Request);
|
||
}
|
||
|
||
Exit:
|
||
|
||
// Store the updated lock counts on success
|
||
if (countChanged && NT_SUCCESS(status))
|
||
{
|
||
DeviceExtension->LockCount = newLockCount;
|
||
DeviceExtension->ProtectedLockCount = newProtectedLockCount;
|
||
DeviceExtension->InternalLockCount = newInternalLockCount;
|
||
if (fileObjectContext)
|
||
{
|
||
fileObjectContext->LockCount = newFileLockCount;
|
||
}
|
||
}
|
||
|
||
WdfWaitLockRelease(DeviceExtension->EjectSynchronizationLock);
|
||
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"PerformEjectionControl: %!STATUS!, "
|
||
"Count Changed: %d, Command Sent: %d, "
|
||
"Current Counts: Internal: %x Secure: %x Simple: %x\n",
|
||
status,
|
||
countChanged,
|
||
previouslyLocked != nowLocked,
|
||
DeviceExtension->InternalLockCount,
|
||
DeviceExtension->ProtectedLockCount,
|
||
DeviceExtension->LockCount
|
||
));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(APC_LEVEL)
|
||
NTSTATUS
|
||
DeviceUnlockExclusive(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ WDFFILEOBJECT FileObject,
|
||
_In_ BOOLEAN IgnorePreviousMediaChanges
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
to unlock the exclusive lock
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device context
|
||
FileObject - file object that currently holds the lock
|
||
IgnorePreviousMediaChanges - if TRUE, ignore previously accumulated media changes
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PCDROM_DATA cdData = &DeviceExtension->DeviceAdditionalData;
|
||
PMEDIA_CHANGE_DETECTION_INFO info = DeviceExtension->MediaChangeDetectionInfo;
|
||
BOOLEAN ANPending = 0;
|
||
LONG requestInUse = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!EXCLUSIVE_MODE(cdData))
|
||
{
|
||
// Device is not locked for exclusive access.
|
||
// Can not process unlock request.
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
|
||
"RequestHandleExclusiveAccessUnlockDevice: Device not locked for exclusive access, can't unlock device.\n"));
|
||
status = STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
else if (!EXCLUSIVE_OWNER(cdData, FileObject))
|
||
{
|
||
// Request not from the exclusive owner, can't unlock the device.
|
||
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
|
||
"RequestHandleExclusiveAccessUnlockDevice: Unable to unlock device, invalid file object\n"));
|
||
|
||
status = STATUS_INVALID_HANDLE;
|
||
}
|
||
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
// Unless we were explicitly requested not to do so, generate a media removal notification
|
||
// followed by a media arrival notification similar to volume lock/unlock file system events.
|
||
if (!IgnorePreviousMediaChanges)
|
||
{
|
||
MEDIA_CHANGE_DETECTION_STATE previousMediaState = MediaUnknown;
|
||
|
||
// Change the media state to "unavailable", which will cause a removal notification if the media
|
||
// was previously present. At the same time, store the previous state in previousMediaState.
|
||
DeviceSetMediaChangeStateEx(DeviceExtension, MediaUnavailable, &previousMediaState);
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
||
"DeviceUnlockExclusive: Changing the media state to MediaUnavailable\n"));
|
||
|
||
// Restore the previous media state, which will cause a media arrival notification if the media
|
||
// was originally present.
|
||
DeviceSetMediaChangeStateEx(DeviceExtension, previousMediaState, NULL);
|
||
}
|
||
|
||
// Set DO_VERIFY_VOLUME so that the file system will remount on it.
|
||
if (IsVolumeMounted(DeviceExtension->DeviceObject))
|
||
{
|
||
SET_FLAG(DeviceExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
|
||
}
|
||
|
||
// Set MMC state to update required
|
||
cdData->Mmc.WriteAllowed = FALSE;
|
||
cdData->Mmc.UpdateState = CdromMmcUpdateRequired;
|
||
|
||
// Send unlock notification
|
||
DeviceSendNotification(DeviceExtension,
|
||
&GUID_IO_CDROM_EXCLUSIVE_UNLOCK,
|
||
0,
|
||
NULL);
|
||
|
||
InterlockedExchangePointer((PVOID)&cdData->ExclusiveOwner, NULL);
|
||
|
||
if ((info != NULL) && (info->AsynchronousNotificationSupported != FALSE))
|
||
{
|
||
ANPending = info->ANSignalPendingDueToExclusiveLock;
|
||
info->ANSignalPendingDueToExclusiveLock = FALSE;
|
||
|
||
if ((ANPending != FALSE) && (info->MediaChangeDetectionDisableCount == 0))
|
||
{
|
||
// if the request is not in use, mark it as such.
|
||
requestInUse = InterlockedCompareExchange((PLONG)&info->MediaChangeRequestInUse, 1, 0);
|
||
|
||
if (requestInUse == 0)
|
||
{
|
||
// The last MCN finished. ok to issue the new one.
|
||
RequestSetupMcnSyncIrp(DeviceExtension);
|
||
|
||
// The irp will go into KMDF framework and a request will be created there to represent it.
|
||
IoCallDriver(DeviceExtension->DeviceObject, info->MediaChangeSyncIrp);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
RequestCompletion(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ WDFREQUEST Request,
|
||
_In_ NTSTATUS Status,
|
||
_In_ ULONG_PTR Information
|
||
)
|
||
{
|
||
#ifdef DBG
|
||
ULONG ioctlCode = 0;
|
||
WDF_REQUEST_PARAMETERS requestParameters;
|
||
|
||
// Get the Request parameters
|
||
WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
|
||
WdfRequestGetParameters(Request, &requestParameters);
|
||
|
||
if (requestParameters.Type == WdfRequestTypeDeviceControl)
|
||
{
|
||
ioctlCode = requestParameters.Parameters.DeviceIoControl.IoControlCode;
|
||
|
||
if (requestParameters.Parameters.DeviceIoControl.IoControlCode != IOCTL_MCN_SYNC_FAKE_IOCTL)
|
||
{
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
||
"Request complete - IOCTL - code: %X; Status: %X; Information: %X\n",
|
||
ioctlCode,
|
||
Status,
|
||
(ULONG)Information));
|
||
}
|
||
else
|
||
{
|
||
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_GENERAL,
|
||
"Request complete - IOCTL - code: %X; Status: %X; Information: %X\n",
|
||
ioctlCode,
|
||
Status,
|
||
(ULONG)Information));
|
||
}
|
||
}
|
||
else if (requestParameters.Type == WdfRequestTypeRead)
|
||
{
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
||
"Request complete - READ - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n",
|
||
(ULONG)requestParameters.Parameters.Read.DeviceOffset,
|
||
(ULONG)requestParameters.Parameters.Read.Length,
|
||
(ULONG)Information,
|
||
Status));
|
||
}
|
||
else if (requestParameters.Type == WdfRequestTypeWrite)
|
||
{
|
||
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_GENERAL,
|
||
"Request complete - WRITE - Starting Offset: %X; Length: %X; Transferred Length: %X; Status: %X\n",
|
||
(ULONG)requestParameters.Parameters.Write.DeviceOffset,
|
||
(ULONG)requestParameters.Parameters.Write.Length,
|
||
(ULONG)Information,
|
||
Status));
|
||
}
|
||
#endif
|
||
|
||
if (IoIsErrorUserInduced(Status))
|
||
{
|
||
PIRP irp = WdfRequestWdmGetIrp(Request);
|
||
if (irp->Tail.Overlay.Thread)
|
||
{
|
||
IoSetHardErrorOrVerifyDevice(irp, DeviceExtension->DeviceObject);
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status) && DeviceExtension->SurpriseRemoved == TRUE)
|
||
{
|
||
// IMAPI expects ERROR_DEV_NOT_EXISTS if recorder has been surprised removed,
|
||
// or it will retry WRITE commands for up to 3 minutes
|
||
// CDROM behavior should be consistent for all requests, including SCSI pass-through
|
||
Status = STATUS_DEVICE_DOES_NOT_EXIST;
|
||
}
|
||
|
||
WdfRequestCompleteWithInformation(Request, Status, Information);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
||
RequestDummyCompletionRoutine(
|
||
_In_ WDFREQUEST Request,
|
||
_In_ WDFIOTARGET Target,
|
||
_In_ PWDF_REQUEST_COMPLETION_PARAMS Params,
|
||
_In_ WDFCONTEXT Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is a dummy competion routine that simply calls WdfRequestComplete. We have to use
|
||
this dummy competion routine instead of WDF_REQUEST_SEND_OPTION_SEND_AND_FORGET, because
|
||
the latter causes the framework to not check if the I/O target is closed or not.
|
||
|
||
Arguments:
|
||
|
||
Request - completed request
|
||
Target - the I/O target that completed the request
|
||
Params - request parameters
|
||
Context - not used
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
{
|
||
UNREFERENCED_PARAMETER(Target);
|
||
UNREFERENCED_PARAMETER(Params);
|
||
UNREFERENCED_PARAMETER(Context);
|
||
|
||
WdfRequestCompleteWithInformation(Request,
|
||
WdfRequestGetStatus(Request),
|
||
WdfRequestGetInformation(Request));
|
||
}
|
||
|
||
|
||
_IRQL_requires_max_(DISPATCH_LEVEL)
|
||
NTSTATUS
|
||
DeviceSendPowerDownProcessRequest(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine,
|
||
_In_opt_ PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called during processing power down request.
|
||
It is used to send either SYNC CACHE command or STOP UNIT command.
|
||
|
||
Caller should set proper value in deviceExtension->PowerContext.PowerChangeState.PowerDown
|
||
to trigger the correct command be sent.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension -
|
||
|
||
CompletionRoutine - Completion routine that needs to be set for the request
|
||
|
||
Context - Completion context associated with the completion routine
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
BOOLEAN requestSent = FALSE;
|
||
|
||
BOOLEAN shouldRetry = TRUE;
|
||
PCDB cdb = (PCDB)DeviceExtension->PowerContext.Srb.Cdb;
|
||
ULONG timeoutValue = DeviceExtension->TimeOutValue;
|
||
ULONG retryCount = 1;
|
||
|
||
// reset some fields.
|
||
DeviceExtension->PowerContext.RetryIntervalIn100ns = 0;
|
||
status = PowerContextReuseRequest(DeviceExtension);
|
||
RequestClearSendTime(DeviceExtension->PowerContext.PowerRequest);
|
||
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
return status;
|
||
}
|
||
|
||
// set proper timeout value and max retry count.
|
||
switch(DeviceExtension->PowerContext.PowerChangeState.PowerDown)
|
||
{
|
||
case PowerDownDeviceInitial:
|
||
case PowerDownDeviceQuiesced:
|
||
case PowerDownDeviceStopped:
|
||
break;
|
||
|
||
case PowerDownDeviceLocked:
|
||
// Case of issuing SYNC CACHE command. Do not use power irp timeout remaining time in this case
|
||
// as we want to give best try on SYNC CACHE command.
|
||
retryCount = MAXIMUM_RETRIES;
|
||
timeoutValue = DeviceExtension->TimeOutValue;
|
||
break;
|
||
|
||
case PowerDownDeviceFlushed:
|
||
{
|
||
// Case of issuing STOP UNIT command
|
||
// As "Imme" bit is set to '1', this command should be completed in short time.
|
||
// This command is at low importance, failure of this command has very small impact.
|
||
ULONG secondsRemaining = 0;
|
||
|
||
#if (WINVER >= 0x0601)
|
||
// this API is introduced in Windows7
|
||
PoQueryWatchdogTime(DeviceExtension->LowerPdo, &secondsRemaining);
|
||
#endif
|
||
|
||
if (secondsRemaining == 0)
|
||
{
|
||
// not able to retrieve remaining time from PoQueryWatchdogTime API, use default values.
|
||
retryCount = MAXIMUM_RETRIES;
|
||
timeoutValue = SCSI_CDROM_TIMEOUT;
|
||
}
|
||
else
|
||
{
|
||
// plan to leave about 30 seconds to lower level drivers if possible.
|
||
if (secondsRemaining >= 32)
|
||
{
|
||
retryCount = (secondsRemaining - 30)/SCSI_CDROM_TIMEOUT + 1;
|
||
timeoutValue = SCSI_CDROM_TIMEOUT;
|
||
|
||
if (retryCount > MAXIMUM_RETRIES)
|
||
{
|
||
retryCount = MAXIMUM_RETRIES;
|
||
}
|
||
|
||
if (retryCount == 1)
|
||
{
|
||
timeoutValue = secondsRemaining - 30;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// issue the command with minimal timeout value and do not retry on it.
|
||
retryCount = 1;
|
||
timeoutValue = 2;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
NT_ASSERT( FALSE );
|
||
status = STATUS_NOT_IMPLEMENTED;
|
||
return status;
|
||
}
|
||
|
||
DeviceExtension->PowerContext.RetryCount = retryCount;
|
||
|
||
// issue command.
|
||
while (shouldRetry)
|
||
{
|
||
|
||
// set SRB fields.
|
||
DeviceExtension->PowerContext.Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
|
||
SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
|
||
SRB_FLAGS_NO_QUEUE_FREEZE |
|
||
SRB_FLAGS_BYPASS_LOCKED_QUEUE |
|
||
SRB_FLAGS_D3_PROCESSING;
|
||
|
||
DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
DeviceExtension->PowerContext.Srb.TimeOutValue = timeoutValue;
|
||
|
||
if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceInitial)
|
||
{
|
||
DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_LOCK_QUEUE;
|
||
}
|
||
else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceLocked)
|
||
{
|
||
DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_QUIESCE_DEVICE;
|
||
}
|
||
else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced)
|
||
{
|
||
// Case of issuing SYNC CACHE command.
|
||
DeviceExtension->PowerContext.Srb.CdbLength = 10;
|
||
cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
|
||
}
|
||
else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceFlushed)
|
||
{
|
||
// Case of issuing STOP UNIT command.
|
||
DeviceExtension->PowerContext.Srb.CdbLength = 6;
|
||
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
||
cdb->START_STOP.Start = 0;
|
||
cdb->START_STOP.Immediate = 1;
|
||
}
|
||
else if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceStopped)
|
||
{
|
||
DeviceExtension->PowerContext.Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
|
||
}
|
||
|
||
// Set up completion routine and context if requested
|
||
if (CompletionRoutine)
|
||
{
|
||
WdfRequestSetCompletionRoutine(DeviceExtension->PowerContext.PowerRequest,
|
||
CompletionRoutine,
|
||
Context);
|
||
}
|
||
|
||
status = RequestSend(DeviceExtension,
|
||
DeviceExtension->PowerContext.PowerRequest,
|
||
DeviceExtension->IoTarget,
|
||
CompletionRoutine ? 0 : WDF_REQUEST_SEND_OPTION_SYNCHRONOUS,
|
||
&requestSent);
|
||
|
||
if (requestSent)
|
||
{
|
||
if ((CompletionRoutine == NULL) &&
|
||
(SRB_STATUS(DeviceExtension->PowerContext.Srb.SrbStatus) != SRB_STATUS_SUCCESS))
|
||
{
|
||
TracePrint((TRACE_LEVEL_ERROR,
|
||
TRACE_FLAG_POWER,
|
||
"%p\tError occured when issuing %s command to device. Srb %p, Status %x\n",
|
||
DeviceExtension->PowerContext.PowerRequest,
|
||
(DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced) ? "SYNC CACHE" : "STOP UNIT",
|
||
&DeviceExtension->PowerContext.Srb,
|
||
DeviceExtension->PowerContext.Srb.SrbStatus));
|
||
|
||
NT_ASSERT(!(TEST_FLAG(DeviceExtension->PowerContext.Srb.SrbStatus, SRB_STATUS_QUEUE_FROZEN)));
|
||
|
||
shouldRetry = RequestSenseInfoInterpret(DeviceExtension,
|
||
DeviceExtension->PowerContext.PowerRequest,
|
||
&(DeviceExtension->PowerContext.Srb),
|
||
retryCount - DeviceExtension->PowerContext.RetryCount,
|
||
&status,
|
||
&(DeviceExtension->PowerContext.RetryIntervalIn100ns));
|
||
|
||
if (shouldRetry && (DeviceExtension->PowerContext.RetryCount-- == 0))
|
||
{
|
||
shouldRetry = FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// succeeded, do not need to retry.
|
||
shouldRetry = FALSE;
|
||
}
|
||
|
||
}
|
||
else
|
||
{
|
||
// request failed to be sent
|
||
shouldRetry = FALSE;
|
||
}
|
||
|
||
if (shouldRetry)
|
||
{
|
||
LARGE_INTEGER t;
|
||
t.QuadPart = -DeviceExtension->PowerContext.RetryIntervalIn100ns;
|
||
KeDelayExecutionThread(KernelMode, FALSE, &t);
|
||
|
||
status = PowerContextReuseRequest(DeviceExtension);
|
||
if (!NT_SUCCESS(status))
|
||
{
|
||
shouldRetry = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (DeviceExtension->PowerContext.PowerChangeState.PowerDown == PowerDownDeviceQuiesced)
|
||
{
|
||
// record SYNC CACHE command completion time stamp.
|
||
KeQueryTickCount(&DeviceExtension->PowerContext.Step1CompleteTime);
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
RequestSend(
|
||
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
||
_In_ WDFREQUEST Request,
|
||
_In_ WDFIOTARGET IoTarget,
|
||
_In_ ULONG Flags,
|
||
_Out_opt_ PBOOLEAN RequestSent
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Send the request to the target, wake up the device from Zero Power state if necessary.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - device extension
|
||
Request - the request to be sent
|
||
IoTarget - target of the above request
|
||
Flags - flags for the operation
|
||
RequestSent - optional, if the request was sent
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
BOOLEAN requestSent = FALSE;
|
||
WDF_REQUEST_SEND_OPTIONS options;
|
||
|
||
UNREFERENCED_PARAMETER(DeviceExtension);
|
||
|
||
if ((DeviceExtension->ZeroPowerODDInfo != NULL) &&
|
||
(DeviceExtension->ZeroPowerODDInfo->InZeroPowerState != FALSE))
|
||
{
|
||
}
|
||
|
||
// Now send down the request
|
||
if (NT_SUCCESS(status))
|
||
{
|
||
WDF_REQUEST_SEND_OPTIONS_INIT(&options, Flags);
|
||
|
||
RequestSetSentTime(Request);
|
||
|
||
// send request and check status
|
||
|
||
// Disable SDV warning about infinitely waiting in caller's context:
|
||
// 1. Some requests (such as SCSI_PASS_THROUGH, contains buffer from user space) need to be sent down in caller<65>s context.
|
||
// Consequently, these requests wait in caller<65>s context until they are allowed to be sent down.
|
||
// 2. Considering the situation that during sleep, a request can be hold by storage port driver. When system resumes, any time out value (if we set using KMDF time out value) might be expires.
|
||
// This will cause the waiting request being failed (behavior change). We<57>d rather not set time out value.
|
||
|
||
_Analysis_assume_(options.Timeout != 0);
|
||
requestSent = WdfRequestSend(Request, IoTarget, &options);
|
||
_Analysis_assume_(options.Timeout == 0);
|
||
|
||
// If WdfRequestSend fails, or if the WDF_REQUEST_SEND_OPTION_SYNCHRONOUS flag is set,
|
||
// the driver can call WdfRequestGetStatus immediately after calling WdfRequestSend.
|
||
if ((requestSent == FALSE) ||
|
||
(Flags & WDF_REQUEST_SEND_OPTION_SYNCHRONOUS))
|
||
{
|
||
status = WdfRequestGetStatus(Request);
|
||
|
||
if (requestSent == FALSE)
|
||
{
|
||
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_GENERAL,
|
||
"WdfRequestSend failed: %lx\n",
|
||
status
|
||
));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (RequestSent != NULL)
|
||
{
|
||
*RequestSent = requestSent;
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|