mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 04:11:30 +00:00
2743 lines
90 KiB
C
2743 lines
90 KiB
C
/*--
|
|
|
|
Copyright (C) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
init.c
|
|
|
|
Abstract:
|
|
|
|
Initialization routines for CDROM
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "ntddk.h"
|
|
#include "ntddstor.h"
|
|
#include "ntstrsafe.h"
|
|
#include "devpkey.h"
|
|
|
|
#include "cdrom.h"
|
|
#include "scratch.h"
|
|
#include "mmc.h"
|
|
|
|
#ifdef DEBUG_USE_WPP
|
|
#include "init.tmh"
|
|
#endif
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitAllocateBuffers(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceRetrieveScsiAddress(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_ADDRESS ScsiAddress
|
|
);
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceRetrieveDescriptorsAndTransferLength(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
VOID
|
|
DeviceScanSpecialDevices(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitMmcContext(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
);
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceGetMmcSupportInfo(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_Out_ PBOOLEAN IsMmcDevice
|
|
);
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceIsPortable(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_Out_ PBOOLEAN IsPortable
|
|
);
|
|
#endif
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
#pragma alloc_text(PAGE, DeviceClaimRelease)
|
|
#pragma alloc_text(PAGE, DeviceEvtSelfManagedIoInit)
|
|
|
|
#pragma alloc_text(PAGE, DeviceInitReleaseQueueContext)
|
|
#pragma alloc_text(PAGE, DeviceInitAllocateBuffers)
|
|
#pragma alloc_text(PAGE, DeviceInitPowerContext)
|
|
#pragma alloc_text(PAGE, DeviceCreateWellKnownName)
|
|
#pragma alloc_text(PAGE, DeviceRetrieveScsiAddress)
|
|
#pragma alloc_text(PAGE, DeviceRetrieveDescriptorsAndTransferLength)
|
|
#pragma alloc_text(PAGE, DeviceInitializeHotplugInfo)
|
|
#pragma alloc_text(PAGE, DeviceScanSpecialDevices)
|
|
#pragma alloc_text(PAGE, DeviceGetTimeOutValueFromRegistry)
|
|
#pragma alloc_text(PAGE, DeviceGetMmcSupportInfo)
|
|
#pragma alloc_text(PAGE, DeviceRetrieveDescriptor)
|
|
#pragma alloc_text(PAGE, DeviceRetrieveHackFlagsFromRegistry)
|
|
#pragma alloc_text(PAGE, DeviceScanForSpecial)
|
|
#pragma alloc_text(PAGE, DeviceHackFlagsScan)
|
|
#pragma alloc_text(PAGE, DeviceInitMmcContext)
|
|
#pragma alloc_text(PAGE, ScanForSpecialHandler)
|
|
#pragma alloc_text(PAGE, DeviceSetRawReadInfo)
|
|
#pragma alloc_text(PAGE, DeviceInitializeDvd)
|
|
#pragma alloc_text(PAGE, DeviceCacheDeviceInquiryData)
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
|
#pragma alloc_text(PAGE, DeviceIsPortable)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4152) // nonstandard extension, function/data pointer conversion in expression
|
|
#pragma warning(disable:26000) // read overflow reported because of pointer type conversion
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceClaimRelease(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ BOOLEAN Release
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function claims a device in the port driver. The port driver object
|
|
is updated with the correct driver object if the device is successfully
|
|
claimed.
|
|
|
|
Arguments:
|
|
|
|
Device - The WDFDEVICE that needs to be claimed or released.
|
|
|
|
Release - Indicates the logical unit should be released rather than claimed.
|
|
|
|
Return Value:
|
|
|
|
Returns a status indicating success or failure of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
SCSI_REQUEST_BLOCK srb = {0};
|
|
WDF_MEMORY_DESCRIPTOR descriptor;
|
|
WDFREQUEST request;
|
|
WDF_OBJECT_ATTRIBUTES attributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
//Create a request
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
|
|
CDROM_REQUEST_CONTEXT);
|
|
|
|
status = WdfRequestCreate(&attributes,
|
|
DeviceExtension->IoTarget,
|
|
&request);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
//fill up srb structure
|
|
srb.OriginalRequest = WdfRequestWdmGetIrp(request);
|
|
NT_ASSERT(srb.OriginalRequest != NULL);
|
|
|
|
srb.Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
srb.Function = Release
|
|
? SRB_FUNCTION_RELEASE_DEVICE
|
|
: SRB_FUNCTION_CLAIM_DEVICE;
|
|
|
|
|
|
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&descriptor,
|
|
&srb,
|
|
sizeof(srb));
|
|
|
|
status = WdfIoTargetSendInternalIoctlOthersSynchronously(DeviceExtension->IoTarget,
|
|
request,
|
|
IOCTL_SCSI_EXECUTE_NONE,
|
|
&descriptor,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
NT_ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
|
|
|
|
// The request should be deleted.
|
|
WdfObjectDelete(request);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
|
|
"DeviceClaimRelease: Failed to %s device, status: 0x%X\n",
|
|
Release ? "Release" : "Claim",
|
|
status));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TracePrint((TRACE_LEVEL_FATAL, TRACE_FLAG_PNP,
|
|
"DeviceClaimRelease: Failed to create request, status: 0x%X\n",
|
|
status));
|
|
}
|
|
|
|
if (Release)
|
|
{
|
|
// We only release the device when we don't want to manage it.
|
|
// The failure status does not matter.
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return status;
|
|
} // end DeviceClaimRelease()
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
DeviceEvtSelfManagedIoInit(
|
|
_In_ WDFDEVICE Device
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called only once after the device is added in system, so it's used to do
|
|
hardware-dependent device initialization work and resource allocation.
|
|
If this routine fails, DeviceEvtSelfManagedIoCleanup will be invoked by the framework.
|
|
|
|
Arguments:
|
|
|
|
Device - Handle to device object
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCDROM_DEVICE_EXTENSION deviceExtension = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP,
|
|
"DeviceEvtSelfManagedIoInit: WDFDEVICE %p is being started.\n",
|
|
Device));
|
|
|
|
deviceExtension = DeviceGetExtension(Device);
|
|
|
|
// 1. Set/retrieve basic information, some of the following operations may depend on it
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// We do not care if this function fails, SCSI address is mainly for debugging/tracing purposes.
|
|
(VOID) DeviceRetrieveScsiAddress(deviceExtension, &deviceExtension->ScsiAddress);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DeviceRetrieveDescriptorsAndTransferLength(deviceExtension);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// This function should be called after DeviceRetrieveDescriptorsAndTransferLength()
|
|
// It depends on MaxTransferLenth fields.
|
|
status = DeviceInitAllocateBuffers(deviceExtension);
|
|
}
|
|
|
|
// 2. The following functions depend on the allocated buffers.
|
|
|
|
// perf re-enable after failing. Q: Is this one used by cdrom.sys?
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// allow perf to be re-enabled after a given number of failed IOs
|
|
// require this number to be at least CLASS_PERF_RESTORE_MINIMUM
|
|
ULONG t = CLASS_PERF_RESTORE_MINIMUM;
|
|
|
|
DeviceGetParameter(deviceExtension,
|
|
CLASSP_REG_SUBKEY_NAME,
|
|
CLASSP_REG_PERF_RESTORE_VALUE_NAME,
|
|
&t);
|
|
if (t >= CLASS_PERF_RESTORE_MINIMUM)
|
|
{
|
|
deviceExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
|
|
}
|
|
}
|
|
|
|
// 3. Retrieve information about special devices and hack flags.
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceRetrieveHackFlagsFromRegistry(deviceExtension);
|
|
// scan for bad items.
|
|
DeviceScanForSpecial(deviceExtension, CdRomBadItems, DeviceHackFlagsScan);
|
|
// Check to see if it's a special device that needs special error process.
|
|
DeviceScanSpecialDevices(deviceExtension); // may send command to device
|
|
}
|
|
|
|
// 4. Initialize the hotplug information only after the ScanForSpecial routines,
|
|
// as it relies upon the hack flags - deviceExtension->PrivateFdoData->HackFlags.
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DeviceInitializeHotplugInfo(deviceExtension);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// cache the device's inquiry data
|
|
status = DeviceCacheDeviceInquiryData(deviceExtension);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to cache the device's inquiry data, failng %!STATUS!\n",
|
|
status
|
|
));
|
|
}
|
|
}
|
|
|
|
// 5. Initialize MMC context, media change notification stuff and read media capacity
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DeviceInitializeMediaChangeDetection(deviceExtension);
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DeviceInitMmcContext(deviceExtension);
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DeviceInitializeZPODD(deviceExtension);
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Do READ CAPACITY. This SCSI command returns the last sector address
|
|
// on the device and the bytes per sector. These are used to calculate
|
|
// the drive capacity in bytes.
|
|
status = MediaReadCapacity(Device);
|
|
|
|
// If READ CAPACITY succeeded, we can safely conclude that there is a media present
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceSetMediaChangeStateEx(deviceExtension,
|
|
MediaPresent,
|
|
NULL);
|
|
}
|
|
|
|
// READ CAPACITY is not critical for init, ignore all errors occuring during its execution
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
// 6. Perform DVD-specific initialization
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DeviceInitializeDvd(Device);
|
|
}
|
|
|
|
// 7. Miscellaneous initialization actions
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if (deviceExtension->PrivateFdoData != NULL)
|
|
{
|
|
deviceExtension->PrivateFdoData->Perf.OriginalSrbFlags = deviceExtension->SrbFlags;
|
|
}
|
|
|
|
if (deviceExtension->DeviceAdditionalData.Mmc.IsWriter)
|
|
{
|
|
// OPC can really take this long per IMAPIv1 timeout....
|
|
deviceExtension->TimeOutValue = max(deviceExtension->TimeOutValue, SCSI_CDROM_OPC_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
// 8. Enable the main timer, create ARC name as needed
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Device successfully added and initialized, increase CdRomCount.
|
|
IoGetConfigurationInformation()->CdRomCount++;
|
|
|
|
deviceExtension->IsInitialized = TRUE;
|
|
|
|
DeviceEnableMainTimer(deviceExtension);
|
|
|
|
}
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
|
// 9. Set volume interface properties
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
BOOLEAN isCritical = FALSE;
|
|
BOOLEAN isPortable = FALSE;
|
|
BOOLEAN isRemovable = TEST_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA);
|
|
DEVPROP_BOOLEAN propCritical = DEVPROP_FALSE;
|
|
DEVPROP_BOOLEAN propPortable = DEVPROP_FALSE;
|
|
DEVPROP_BOOLEAN propRemovable = DEVPROP_FALSE;
|
|
|
|
status = DeviceIsPortable(deviceExtension, &isPortable);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if (isPortable) {
|
|
SET_FLAG(deviceExtension->DeviceObject->Characteristics, FILE_PORTABLE_DEVICE);
|
|
}
|
|
|
|
propPortable = isPortable ? DEVPROP_TRUE : DEVPROP_FALSE;
|
|
|
|
status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
|
|
&DEVPKEY_Storage_Portable,
|
|
0,
|
|
0,
|
|
DEVPROP_TYPE_BOOLEAN,
|
|
sizeof(DEVPROP_BOOLEAN),
|
|
&propPortable);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
propRemovable = isRemovable ? DEVPROP_TRUE : DEVPROP_FALSE;
|
|
|
|
status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
|
|
&DEVPKEY_Storage_Removable_Media,
|
|
0,
|
|
0,
|
|
DEVPROP_TYPE_BOOLEAN,
|
|
sizeof(DEVPROP_BOOLEAN),
|
|
&propRemovable);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
isCritical = TEST_FLAG(deviceExtension->DeviceObject->Flags,
|
|
(DO_SYSTEM_SYSTEM_PARTITION |
|
|
DO_SYSTEM_BOOT_PARTITION |
|
|
DO_SYSTEM_CRITICAL_PARTITION));
|
|
|
|
propCritical = isCritical ? DEVPROP_TRUE : DEVPROP_FALSE;
|
|
|
|
status = IoSetDeviceInterfacePropertyData(&deviceExtension->MountedDeviceInterfaceName,
|
|
&DEVPKEY_Storage_System_Critical,
|
|
0,
|
|
0,
|
|
DEVPROP_TYPE_BOOLEAN,
|
|
sizeof(DEVPROP_BOOLEAN),
|
|
&propCritical);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitReleaseQueueContext(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Part of device initialize routine. Initialize ReleaseQueue related stuff.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension of WDFDEVICE.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
WDF_OBJECT_ATTRIBUTES attributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
|
|
CDROM_REQUEST_CONTEXT);
|
|
attributes.ParentObject = DeviceExtension->Device;
|
|
|
|
status = WdfRequestCreate(&attributes,
|
|
DeviceExtension->IoTarget,
|
|
&(DeviceExtension->ReleaseQueueRequest));
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "Cannot create the release queue request\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
// Initialize ReleaseQueueInputMemory, a wrapper around ReleaseQueueSrb
|
|
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
|
attributes.ParentObject = DeviceExtension->ReleaseQueueRequest;
|
|
|
|
status = WdfMemoryCreatePreallocated(&attributes,
|
|
&DeviceExtension->ReleaseQueueSrb,
|
|
sizeof(SCSI_REQUEST_BLOCK),
|
|
&DeviceExtension->ReleaseQueueInputMemory);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "Failed to allocate ReleaseQueueSrb.\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
// Preformat the release queue request here to ensure that this call will never
|
|
// fail during an actual release of the queue.
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
|
|
DeviceExtension->ReleaseQueueRequest,
|
|
IOCTL_SCSI_EXECUTE_NONE,
|
|
DeviceExtension->ReleaseQueueInputMemory,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
// Set a CompletionRoutine callback function for ReleaseQueueRequest.
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
WdfRequestSetCompletionRoutine(DeviceExtension->ReleaseQueueRequest,
|
|
DeviceReleaseQueueCompletion,
|
|
DeviceExtension->Device);
|
|
}
|
|
|
|
// Create a spinlock for ReleaseQueueRequest
|
|
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
|
|
attributes.ParentObject = DeviceExtension->Device;
|
|
|
|
status = WdfSpinLockCreate(&attributes,
|
|
&(DeviceExtension->ReleaseQueueSpinLock));
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP,
|
|
"DeviceInitReleaseQueueContext: Cannot create the release queue spinlock\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
// Initialize miscellaneous ReleaseQueue related fields
|
|
DeviceExtension->ReleaseQueueNeeded = FALSE;
|
|
DeviceExtension->ReleaseQueueInProgress = FALSE;
|
|
DeviceExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
return status;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitPowerContext(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Part of device initialize routine. Initialize PowerContext related stuff.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension of WDFDEVICE.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
WDF_OBJECT_ATTRIBUTES attributes;
|
|
|
|
PAGED_CODE();
|
|
|
|
// create request object for Power operations
|
|
|
|
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes,
|
|
CDROM_REQUEST_CONTEXT);
|
|
attributes.ParentObject = DeviceExtension->Device;
|
|
|
|
status = WdfRequestCreate(&attributes,
|
|
DeviceExtension->IoTarget,
|
|
&(DeviceExtension->PowerContext.PowerRequest) );
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "Cannot create the power request object.\n"));
|
|
|
|
return status;
|
|
}
|
|
|
|
// Preformat the power request. With this being done, we never need to worry about
|
|
// WdfIoTargetFormatRequestForInternalIoctlOthers ever failing later.
|
|
status = WdfIoTargetFormatRequestForInternalIoctlOthers(DeviceExtension->IoTarget,
|
|
DeviceExtension->PowerContext.PowerRequest,
|
|
IOCTL_SCSI_EXECUTE_IN,
|
|
NULL, NULL,
|
|
NULL, NULL,
|
|
NULL, NULL);
|
|
return status;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
DeviceCreateWellKnownName(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a symbolic link to the cdrom device object
|
|
under \dosdevices. The number of the cdrom device does not neccessarily
|
|
match between \dosdevices and \device, but usually will be the same.
|
|
|
|
Saves the buffer
|
|
|
|
Arguments:
|
|
|
|
DeviceObject -
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
UNICODE_STRING unicodeLinkName = {0};
|
|
WCHAR wideLinkName[64] = {0};
|
|
PWCHAR savedName;
|
|
|
|
LONG cdromNumber = DeviceExtension->DeviceNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
// if already linked, assert then return
|
|
if (DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer != NULL)
|
|
{
|
|
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
|
|
"DeviceCreateWellKnownName: link already exists %p\n",
|
|
DeviceExtension->DeviceAdditionalData.WellKnownName.Buffer));
|
|
|
|
NT_ASSERT(FALSE);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// find an unused CdRomNN to link to.
|
|
// It's doing this way because the same might be used for other device in another driver.
|
|
do
|
|
{
|
|
status = RtlStringCchPrintfW((NTSTRSAFE_PWSTR)wideLinkName,
|
|
RTL_NUMBER_OF(wideLinkName),
|
|
L"\\DosDevices\\CdRom%d",
|
|
cdromNumber);
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
|
|
"DeviceCreateWellKnownName: Format symbolic link failed with error: 0x%X\n", status));
|
|
return status;
|
|
}
|
|
|
|
RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
|
|
|
|
status = WdfDeviceCreateSymbolicLink(DeviceExtension->Device,
|
|
&unicodeLinkName);
|
|
|
|
cdromNumber++;
|
|
|
|
} while((status == STATUS_OBJECT_NAME_COLLISION) ||
|
|
(status == STATUS_OBJECT_NAME_EXISTS));
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT,
|
|
"DeviceCreateWellKnownName: Error %lx linking %wZ to "
|
|
"device %wZ\n",
|
|
status,
|
|
&unicodeLinkName,
|
|
&(DeviceExtension->DeviceName)));
|
|
return status;
|
|
}
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceCreateWellKnownName: successfully linked %wZ "
|
|
"to device %wZ\n",
|
|
&unicodeLinkName,
|
|
&(DeviceExtension->DeviceName)));
|
|
|
|
// Save away the symbolic link name in the driver data block. We need
|
|
// it so we can delete the link when the device is removed.
|
|
savedName = ExAllocatePoolWithTag(PagedPool,
|
|
unicodeLinkName.MaximumLength,
|
|
CDROM_TAG_STRINGS);
|
|
|
|
if (savedName == NULL)
|
|
{
|
|
// Test Note: test path should excise here to see if the symbolic is deleted by framework.
|
|
// IoDeleteSymbolicLink(&unicodeLinkName);
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"DeviceCreateWellKnownName: unable to allocate memory.\n"));
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(savedName, unicodeLinkName.MaximumLength);
|
|
RtlCopyMemory(savedName, unicodeLinkName.Buffer, unicodeLinkName.MaximumLength);
|
|
|
|
RtlInitUnicodeString(&(DeviceExtension->DeviceAdditionalData.WellKnownName), savedName);
|
|
|
|
// the name was saved and the link created
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceRetrieveScsiAddress(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ PSCSI_ADDRESS ScsiAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
retrieve SCSI address information and put into device extension
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device context.
|
|
ScsiAddress - the buffer to put the scsi address info.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
WDF_MEMORY_DESCRIPTOR outputDescriptor;
|
|
|
|
PAGED_CODE();
|
|
|
|
if ((DeviceExtension == NULL) ||
|
|
(ScsiAddress == NULL))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//Get IOTARGET for sending request to port driver.
|
|
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor,
|
|
(PVOID)ScsiAddress,
|
|
sizeof(SCSI_ADDRESS));
|
|
|
|
status = WdfIoTargetSendIoctlSynchronously(DeviceExtension->IoTarget,
|
|
NULL,
|
|
IOCTL_SCSI_GET_ADDRESS,
|
|
NULL,
|
|
&outputDescriptor,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"DeviceRetrieveScsiAddress: Get Address failed %lx\n",
|
|
status));
|
|
}
|
|
else
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
|
|
ScsiAddress->PortNumber,
|
|
ScsiAddress->PathId,
|
|
ScsiAddress->TargetId,
|
|
ScsiAddress->Lun));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitAllocateBuffers(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Part of device initialize routine.
|
|
Allocate all buffers in Device Extension.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension of WDFDEVICE.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PVOID senseData = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
// allocate a private extension for class data
|
|
if (DeviceExtension->PrivateFdoData == NULL)
|
|
{
|
|
DeviceExtension->PrivateFdoData = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
sizeof(CDROM_PRIVATE_FDO_DATA),
|
|
CDROM_TAG_PRIVATE_DATA);
|
|
}
|
|
|
|
if (DeviceExtension->PrivateFdoData == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
// initialize the struct's various fields.
|
|
RtlZeroMemory(DeviceExtension->PrivateFdoData, sizeof(CDROM_PRIVATE_FDO_DATA));
|
|
}
|
|
|
|
// Allocate request sense buffer.
|
|
senseData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
|
|
SENSE_BUFFER_SIZE,
|
|
CDROM_TAG_SENSE_INFO);
|
|
|
|
if (senseData == NULL)
|
|
{
|
|
// The buffer cannot be allocated.
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
// Set the sense data pointer in the device extension.
|
|
DeviceExtension->SenseData = senseData;
|
|
}
|
|
|
|
// Allocate scratch buffer -- Must occur after determining
|
|
// max transfer size, but before other CD specific detection
|
|
// (which relies upon this buffer).
|
|
if (!ScratchBuffer_Allocate(DeviceExtension))
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Failed to allocate scratch buffer, failing %!STATUS!\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceRetrieveDescriptorsAndTransferLength(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Part of device initialize routine.
|
|
Retrieve Device Descriptor and Adaptor Descriptor.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension of WDFDEVICE.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
STORAGE_PROPERTY_ID propertyId;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Call port driver to get adapter capabilities.
|
|
propertyId = StorageAdapterProperty;
|
|
|
|
status = DeviceRetrieveDescriptor(DeviceExtension->Device,
|
|
&propertyId,
|
|
(PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->AdapterDescriptor);
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Call port driver to get device descriptor.
|
|
propertyId = StorageDeviceProperty;
|
|
|
|
status = DeviceRetrieveDescriptor(DeviceExtension->Device,
|
|
&propertyId,
|
|
(PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->DeviceDescriptor);
|
|
}
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Call port driver to get device power property.
|
|
// Not all port drivers support this property, and it's not fatal if this query fails.
|
|
propertyId = StorageDevicePowerProperty;
|
|
|
|
(void) DeviceRetrieveDescriptor(DeviceExtension->Device,
|
|
&propertyId,
|
|
(PSTORAGE_DESCRIPTOR_HEADER*)&DeviceExtension->PowerDescriptor);
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Determine the maximum page-aligned and non-page-aligned transfer
|
|
// lengths here, so we needn't do this in common READ/WRITE code paths
|
|
|
|
// start with the number of pages the adapter can support
|
|
ULONG maxAlignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages;
|
|
ULONG maxUnalignedTransfer = DeviceExtension->AdapterDescriptor->MaximumPhysicalPages;
|
|
|
|
|
|
// Unaligned buffers could cross a page boundary.
|
|
if (maxUnalignedTransfer > 1)
|
|
{
|
|
maxUnalignedTransfer--;
|
|
}
|
|
|
|
// if we'd overflow multiplying by page size, just max out the
|
|
// transfer length allowed by the number of pages limit.
|
|
if (maxAlignedTransfer >= (((ULONG)-1) / PAGE_SIZE))
|
|
{
|
|
maxAlignedTransfer = (ULONG)-1;
|
|
}
|
|
else
|
|
{
|
|
maxAlignedTransfer *= PAGE_SIZE;
|
|
}
|
|
|
|
if (maxUnalignedTransfer >= (((ULONG)-1) / PAGE_SIZE))
|
|
{
|
|
maxUnalignedTransfer = (ULONG)-1;
|
|
}
|
|
else
|
|
{
|
|
maxUnalignedTransfer *= PAGE_SIZE;
|
|
}
|
|
|
|
// finally, take the smaller of the above and the adapter's
|
|
// reported maximum number of bytes per transfer.
|
|
maxAlignedTransfer = min(maxAlignedTransfer, DeviceExtension->AdapterDescriptor->MaximumTransferLength);
|
|
maxUnalignedTransfer = min(maxUnalignedTransfer, DeviceExtension->AdapterDescriptor->MaximumTransferLength);
|
|
|
|
// Make sure the values are reasonable and not zero.
|
|
maxAlignedTransfer = max(maxAlignedTransfer, PAGE_SIZE);
|
|
maxUnalignedTransfer = max(maxUnalignedTransfer, PAGE_SIZE);
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"Device %p Max aligned/unaligned transfer size is %x/%x\n",
|
|
DeviceExtension->Device,
|
|
maxAlignedTransfer,
|
|
maxUnalignedTransfer
|
|
));
|
|
DeviceExtension->DeviceAdditionalData.MaxPageAlignedTransferBytes = maxAlignedTransfer;
|
|
DeviceExtension->DeviceAdditionalData.MaxUnalignedTransferBytes = maxUnalignedTransfer;
|
|
}
|
|
else
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DeviceRetrieveDescriptorsAndTransferLength failed %lx\n", status));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceRetrieveDescriptor(
|
|
_In_ WDFDEVICE Device,
|
|
_In_ PSTORAGE_PROPERTY_ID PropertyId,
|
|
_Outptr_ PSTORAGE_DESCRIPTOR_HEADER* Descriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will perform a query for the specified property id and will
|
|
allocate a non-paged buffer to store the data in. It is the responsibility
|
|
of the caller to ensure that this buffer is freed.
|
|
|
|
This routine must be run at IRQL_PASSIVE_LEVEL
|
|
|
|
Arguments:
|
|
|
|
Device - the device object
|
|
PropertyId - type of property to retrieve
|
|
Descriptor - buffer allocated in this function to hold the descriptor data
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
WDF_MEMORY_DESCRIPTOR memoryDescriptor;
|
|
|
|
STORAGE_PROPERTY_QUERY query = {0};
|
|
ULONG bufferLength = 0;
|
|
|
|
PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
|
|
PCDROM_DEVICE_EXTENSION deviceExtension = DeviceGetExtension(Device);
|
|
|
|
PAGED_CODE();
|
|
|
|
// Set the passed-in descriptor pointer to NULL as default
|
|
*Descriptor = NULL;
|
|
|
|
// On the first pass we just want to get the first few
|
|
// bytes of the descriptor so we can read it's size
|
|
query.PropertyId = *PropertyId;
|
|
query.QueryType = PropertyStandardQuery;
|
|
|
|
descriptor = (PVOID)&query;
|
|
|
|
NT_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
|
|
|
|
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor,
|
|
(PVOID)&query,
|
|
sizeof(STORAGE_PROPERTY_QUERY));
|
|
|
|
status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget,
|
|
NULL,
|
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|
&memoryDescriptor,
|
|
&memoryDescriptor,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: error %lx trying to "
|
|
"query properties #1\n", status));
|
|
return status;
|
|
}
|
|
|
|
if (descriptor->Size == 0)
|
|
{
|
|
// This DebugPrint is to help third-party driver writers
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: size returned was zero?! (status "
|
|
"%x\n", status));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// This time we know how much data there is so we can
|
|
// allocate a buffer of the correct size
|
|
bufferLength = descriptor->Size;
|
|
NT_ASSERT(bufferLength >= sizeof(STORAGE_PROPERTY_QUERY));
|
|
bufferLength = max(bufferLength, sizeof(STORAGE_PROPERTY_QUERY));
|
|
|
|
descriptor = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_DESCRIPTOR);
|
|
|
|
if(descriptor == NULL)
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: unable to memory for descriptor "
|
|
"(%d bytes)\n", bufferLength));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// setup the query again, as it was overwritten above
|
|
RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
|
|
query.PropertyId = *PropertyId;
|
|
query.QueryType = PropertyStandardQuery;
|
|
|
|
// copy the input to the new outputbuffer
|
|
RtlCopyMemory(descriptor,
|
|
&query,
|
|
sizeof(STORAGE_PROPERTY_QUERY));
|
|
|
|
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&memoryDescriptor,
|
|
(PVOID)descriptor,
|
|
bufferLength);
|
|
|
|
status = WdfIoTargetSendIoctlSynchronously(deviceExtension->IoTarget,
|
|
NULL,
|
|
IOCTL_STORAGE_QUERY_PROPERTY,
|
|
&memoryDescriptor,
|
|
&memoryDescriptor,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "DeviceRetrieveDescriptor: error %lx trying to "
|
|
"query properties #1\n", status));
|
|
FREE_POOL(descriptor);
|
|
|
|
return status;
|
|
}
|
|
|
|
// return the memory we've allocated to the caller
|
|
*Descriptor = descriptor;
|
|
|
|
return status;
|
|
} // end DeviceRetrieveDescriptor()
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
VOID
|
|
DeviceRetrieveHackFlagsFromRegistry(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
try to retrieve hack flages from registry and put the information in
|
|
device extension.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - the device context
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
WDFKEY hardwareKey = NULL;
|
|
WDFKEY subKey = NULL;
|
|
ULONG deviceHacks = 0;
|
|
|
|
DECLARE_CONST_UNICODE_STRING(subKeyName, CLASSP_REG_SUBKEY_NAME);
|
|
DECLARE_CONST_UNICODE_STRING(valueName, CLASSP_REG_HACK_VALUE_NAME);
|
|
|
|
PAGED_CODE();
|
|
|
|
status = WdfDeviceOpenRegistryKey(DeviceExtension->Device,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_READ,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
&hardwareKey);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = WdfRegistryOpenKey(hardwareKey,
|
|
&subKeyName,
|
|
KEY_READ,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
&subKey);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = WdfRegistryQueryULong(subKey,
|
|
&valueName,
|
|
&deviceHacks);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// remove unknown values and save...
|
|
CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
|
|
SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, deviceHacks);
|
|
}
|
|
|
|
WdfRegistryClose(subKey);
|
|
}
|
|
|
|
WdfRegistryClose(hardwareKey);
|
|
}
|
|
|
|
|
|
//
|
|
// we should modify the system hive to include another key for us to grab
|
|
// settings from. in this case: Classpnp\HackFlags
|
|
//
|
|
// the use of a DWORD value for the HackFlags allows 32 hacks w/o
|
|
// significant use of the registry, and also reduces OEM exposure.
|
|
//
|
|
// definition of bit flags:
|
|
// 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
|
|
// cannot actually prevent removal.
|
|
// 0x00000002 -- Device hard-hangs or times out for GESN requests.
|
|
// 0x00000008 -- Device does not support RESERVE(6) and RELEASE(6).
|
|
// 0x00000010 -- Device may incorrecly report operational changes in GESN.
|
|
// 0x00000020 -- Device does not support streaming READ(12) / WRITE(12).
|
|
// 0x00000040 -- Device does not support asynchronous notification.
|
|
// 0xffffff80 -- Currently reserved, may be used later.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
VOID DeviceScanForSpecial(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ CDROM_SCAN_FOR_SPECIAL_INFO DeviceList[],
|
|
_In_ PCDROM_SCAN_FOR_SPECIAL_HANDLER Function)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
scan the list of devices that should be hacked or not supported.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - the device context
|
|
DeviceList - the device list
|
|
Function - function used to scan from the list.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
|
|
PUCHAR vendorId;
|
|
PUCHAR productId;
|
|
PUCHAR productRevision;
|
|
UCHAR nullString[] = "";
|
|
|
|
PAGED_CODE();
|
|
NT_ASSERT(DeviceList);
|
|
NT_ASSERT(Function);
|
|
|
|
if (DeviceList == NULL)
|
|
{
|
|
return;
|
|
}
|
|
if (Function == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
deviceDescriptor = DeviceExtension->DeviceDescriptor;
|
|
|
|
// SCSI sets offsets to -1, ATAPI sets to 0. check for both.
|
|
if (deviceDescriptor->VendorIdOffset != 0 &&
|
|
deviceDescriptor->VendorIdOffset != -1)
|
|
{
|
|
vendorId = ((PUCHAR)deviceDescriptor);
|
|
vendorId += deviceDescriptor->VendorIdOffset;
|
|
}
|
|
else
|
|
{
|
|
vendorId = nullString;
|
|
}
|
|
|
|
if (deviceDescriptor->ProductIdOffset != 0 &&
|
|
deviceDescriptor->ProductIdOffset != -1)
|
|
{
|
|
productId = ((PUCHAR)deviceDescriptor);
|
|
productId += deviceDescriptor->ProductIdOffset;
|
|
}
|
|
else
|
|
{
|
|
productId = nullString;
|
|
}
|
|
|
|
if (deviceDescriptor->ProductRevisionOffset != 0 &&
|
|
deviceDescriptor->ProductRevisionOffset != -1)
|
|
{
|
|
productRevision = ((PUCHAR)deviceDescriptor);
|
|
productRevision += deviceDescriptor->ProductRevisionOffset;
|
|
}
|
|
else
|
|
{
|
|
productRevision = nullString;
|
|
}
|
|
|
|
// loop while the device list is valid (not null-filled)
|
|
for (;(DeviceList->VendorId != NULL ||
|
|
DeviceList->ProductId != NULL ||
|
|
DeviceList->ProductRevision != NULL); DeviceList++)
|
|
{
|
|
if (StringsAreMatched(DeviceList->VendorId, (LPSTR)vendorId) &&
|
|
StringsAreMatched(DeviceList->ProductId, (LPSTR)productId) &&
|
|
StringsAreMatched(DeviceList->ProductRevision, (LPSTR)productRevision)
|
|
)
|
|
{
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: Found matching "
|
|
"controller Ven: %s Prod: %s Rev: %s\n",
|
|
(LPCSTR)vendorId, (LPCSTR)productId, (LPCSTR)productRevision));
|
|
|
|
// pass the context to the call back routine and exit
|
|
(Function)(DeviceExtension, DeviceList->Data);
|
|
|
|
// for CHK builds, try to prevent wierd stacks by having a debug
|
|
// print here. it's a hack, but i know of no other way to prevent
|
|
// the stack from being wrong.
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: "
|
|
"completed callback\n"));
|
|
return;
|
|
|
|
} // else the strings did not match
|
|
|
|
} // none of the devices matched.
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT, "DeviceScanForSpecial: no match found for %p\n",
|
|
DeviceExtension->DeviceObject));
|
|
return;
|
|
|
|
} // end DeviceScanForSpecial()
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
VOID
|
|
DeviceHackFlagsScan(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ ULONG_PTR Data
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
// remove invalid flags and save
|
|
CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
|
|
SET_FLAG(DeviceExtension->PrivateFdoData->HackFlags, Data);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitializeHotplugInfo(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve information into struc STORAGE_HOTPLUG_INFO in DeviceExtension
|
|
initialize the hotplug information only after the ScanForSpecial routines,
|
|
as it relies upon the hack flags - DeviceExtension->PrivateFdoData->HackFlags.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - the device context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCDROM_PRIVATE_FDO_DATA fdoData = DeviceExtension->PrivateFdoData;
|
|
DEVICE_REMOVAL_POLICY deviceRemovalPolicy = 0;
|
|
ULONG resultLength = 0;
|
|
ULONG writeCacheOverride;
|
|
|
|
PAGED_CODE();
|
|
|
|
// start with some default settings
|
|
RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
|
|
|
|
// set the size (aka version)
|
|
fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
|
|
|
|
// set if the device has removable media
|
|
if (DeviceExtension->DeviceDescriptor->RemovableMedia)
|
|
{
|
|
fdoData->HotplugInfo.MediaRemovable = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fdoData->HotplugInfo.MediaRemovable = FALSE;
|
|
}
|
|
|
|
//
|
|
// this refers to devices which, for reasons not yet understood,
|
|
// do not fail PREVENT_MEDIA_REMOVAL requests even though they
|
|
// have no way to lock the media into the drive. this allows
|
|
// the filesystems to turn off delayed-write caching for these
|
|
// devices as well.
|
|
//
|
|
|
|
if (TEST_FLAG(DeviceExtension->PrivateFdoData->HackFlags, FDO_HACK_CANNOT_LOCK_MEDIA))
|
|
{
|
|
fdoData->HotplugInfo.MediaHotplug = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fdoData->HotplugInfo.MediaHotplug = FALSE;
|
|
}
|
|
|
|
// Query the default removal policy from the kernel
|
|
status = WdfDeviceQueryProperty(DeviceExtension->Device,
|
|
DevicePropertyRemovalPolicy,
|
|
sizeof(DEVICE_REMOVAL_POLICY),
|
|
(PVOID)&deviceRemovalPolicy,
|
|
&resultLength);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Look into the registry to see if the user has chosen
|
|
// to override the default setting for the removal policy.
|
|
// User can override only if the default removal policy is
|
|
// orderly or suprise removal.
|
|
|
|
if ((deviceRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
|
|
(deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval))
|
|
{
|
|
DEVICE_REMOVAL_POLICY userRemovalPolicy = 0;
|
|
|
|
DeviceGetParameter(DeviceExtension,
|
|
CLASSP_REG_SUBKEY_NAME,
|
|
CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
|
|
(PULONG)&userRemovalPolicy);
|
|
|
|
// Validate the override value and use it only if it is an
|
|
// allowed value.
|
|
if ((userRemovalPolicy == RemovalPolicyExpectOrderlyRemoval) ||
|
|
(userRemovalPolicy == RemovalPolicyExpectSurpriseRemoval))
|
|
{
|
|
deviceRemovalPolicy = userRemovalPolicy;
|
|
}
|
|
}
|
|
|
|
// use this info to set the DeviceHotplug setting
|
|
// don't rely on DeviceCapabilities, since it can't properly
|
|
// determine device relations, etc. let the kernel figure this
|
|
// stuff out instead.
|
|
if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval)
|
|
{
|
|
fdoData->HotplugInfo.DeviceHotplug = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fdoData->HotplugInfo.DeviceHotplug = FALSE;
|
|
}
|
|
|
|
// this refers to the *filesystem* caching, but has to be included
|
|
// here since it's a per-device setting. this may change to be
|
|
// stored by the system in the future.
|
|
writeCacheOverride = FALSE;
|
|
DeviceGetParameter(DeviceExtension,
|
|
CLASSP_REG_SUBKEY_NAME,
|
|
CLASSP_REG_WRITE_CACHE_VALUE_NAME,
|
|
&writeCacheOverride);
|
|
|
|
if (writeCacheOverride)
|
|
{
|
|
fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_INIT, "Could not initialize hotplug information %lx\n", status));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitMmcContext(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes and populates the internal data structures that are
|
|
used to discover various MMC-defined capabilities of the device.
|
|
|
|
This routine will not clean up allocate resources if it fails - that
|
|
is left for device stop/removal routines
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsWriter = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.Mmc.WriteAllowed = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = FALSE;
|
|
DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_CD_ROM;
|
|
|
|
// Determine if the drive is MMC-Capable
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = DeviceGetMmcSupportInfo(DeviceExtension,
|
|
&DeviceExtension->DeviceAdditionalData.Mmc.IsMmc);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
//Currently, only low resource error comes here.
|
|
//That is a success case for unsupporting this command.
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"DeviceInitMmcContext: Failed to get the support info for GET CONFIGURATION "
|
|
"command, failng %!STATUS!\n", status
|
|
));
|
|
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsMmc = FALSE;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc)
|
|
{
|
|
// the drive supports at least a subset of MMC commands
|
|
// (and therefore supports READ_CD, etc...)
|
|
|
|
// allocate a buffer for all the capabilities and such
|
|
status = DeviceAllocateMmcResources(DeviceExtension->Device);
|
|
}
|
|
|
|
if (NT_SUCCESS(status) && DeviceExtension->DeviceAdditionalData.Mmc.IsMmc)
|
|
{
|
|
PFEATURE_HEADER header = NULL;
|
|
FEATURE_NUMBER validationSchema;
|
|
ULONG blockingFactor;
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceInitMmcContext: FDO %p GET CONFIGURATION buffer %p\n",
|
|
DeviceExtension->Device,
|
|
DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer
|
|
));
|
|
|
|
// Update several properties using the retrieved Configuration Data.
|
|
|
|
// print all the feature pages (DBG only)
|
|
DevicePrintAllFeaturePages(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
|
|
DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize);
|
|
|
|
// if AACS feature exists, enable AACS flag in the driver
|
|
header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
|
|
DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
|
|
FeatureAACS);
|
|
if (header)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceInitMmcContext: Reporting AACS support for device due to "
|
|
"GET CONFIGURATION showing support\n"
|
|
));
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE;
|
|
}
|
|
|
|
#ifdef ENABLE_AACS_TESTING
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsAACS = TRUE; // just force it true for testing
|
|
#endif // ENABLE_AACS_TESTING
|
|
|
|
// Check if it's a DVD device
|
|
header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
|
|
DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
|
|
FeatureDvdRead);
|
|
if (header != NULL)
|
|
{
|
|
DeviceExtension->DeviceAdditionalData.DriveDeviceType = FILE_DEVICE_DVD;
|
|
}
|
|
|
|
// check if drive is writer
|
|
DeviceUpdateMmcWriteCapability(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
|
|
DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
|
|
FALSE, //Check if the drive has the ability to write.
|
|
(PBOOLEAN)&(DeviceExtension->DeviceAdditionalData.Mmc.IsWriter),
|
|
&validationSchema,
|
|
&blockingFactor);
|
|
|
|
// check if there is a CSS protected DVD or CPPM-protected DVDAudio media in drive.
|
|
header = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
|
|
DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
|
|
FeatureDvdCSS);
|
|
|
|
DeviceExtension->DeviceAdditionalData.Mmc.IsCssDvd = (header != NULL) && (header->Current);
|
|
|
|
// Flag the StartIo routine to update its state and hook in the error handler
|
|
DeviceExtension->DeviceAdditionalData.Mmc.UpdateState = CdromMmcUpdateRequired;
|
|
DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForMmc;
|
|
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
|
|
|
// Read the CDROM mode sense page to get additional info for raw read requests.
|
|
// only valid for MMC devices
|
|
DeviceSetRawReadInfo(DeviceExtension);
|
|
}
|
|
|
|
// Set Read-Only device flag for non-MMC device.
|
|
if (!(DeviceExtension->DeviceAdditionalData.Mmc.IsMmc))
|
|
{
|
|
ULONG deviceCharacteristics = WdfDeviceGetCharacteristics(DeviceExtension->Device);
|
|
|
|
deviceCharacteristics |= FILE_READ_ONLY_DEVICE;
|
|
|
|
WdfDeviceSetCharacteristics(DeviceExtension->Device, deviceCharacteristics);
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceInitMmcContext: FDO %p Device is not an MMC compliant device, so setting "
|
|
"to read-only (legacy) mode",
|
|
DeviceExtension->Device
|
|
));
|
|
}
|
|
|
|
// Set DEV_SAFE_START_UNIT flag for newer devices.
|
|
if (DeviceExtension->DeviceAdditionalData.DriveDeviceType == FILE_DEVICE_DVD)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceInitMmcContext: DVD Devices require START UNIT\n"));
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
|
|
|
}
|
|
else if ((DeviceExtension->DeviceDescriptor->BusType != BusTypeScsi) &&
|
|
(DeviceExtension->DeviceDescriptor->BusType != BusTypeAtapi) &&
|
|
(DeviceExtension->DeviceDescriptor->BusType != BusTypeUnknown)
|
|
)
|
|
{
|
|
// devices on the newer busses require START_UNIT
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceInitMmcContext: Devices for newer buses require START UNIT\n"));
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
ULONG
|
|
DeviceGetTimeOutValueFromRegistry()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
get the device time out value from registry
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
ULONG - value of timeout
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
WDFKEY registryKey = NULL;
|
|
ULONG timeOutValue = 0;
|
|
|
|
DECLARE_CONST_UNICODE_STRING(registryValueName, L"TimeOutValue");
|
|
|
|
PAGED_CODE();
|
|
|
|
// open the service key.
|
|
status = WdfDriverOpenParametersRegistryKey(WdfGetDriver(),
|
|
KEY_READ,
|
|
WDF_NO_OBJECT_ATTRIBUTES,
|
|
®istryKey);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
status = WdfRegistryQueryULong(registryKey,
|
|
®istryValueName,
|
|
&timeOutValue);
|
|
|
|
WdfRegistryClose(registryKey);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
timeOutValue = 0;
|
|
}
|
|
|
|
return timeOutValue;
|
|
|
|
} // end DeviceGetTimeOutValueFromRegistry()
|
|
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
VOID
|
|
DeviceScanSpecialDevices(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks to see if an SCSI logical unit requires an special
|
|
initialization or error processing.
|
|
|
|
Arguments:
|
|
|
|
Device - device object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
PAGED_CODE();
|
|
|
|
// set our hack flags
|
|
DeviceScanForSpecial(DeviceExtension, CdromHackItems, ScanForSpecialHandler);
|
|
|
|
//
|
|
// All CDRom's can ignore the queue lock failure for power operations
|
|
// and do not require handling the SpinUp case (unknown result of sending
|
|
// a cdrom a START_UNIT command -- may eject disks?)
|
|
//
|
|
// We send the stop command mostly to stop outstanding asynch operations
|
|
// (like audio playback) from running when the system is powered off.
|
|
// Because of this and the unlikely chance that a PLAY command will be
|
|
// sent in the window between the STOP and the time the machine powers down
|
|
// we don't require queue locks. This is important because without them
|
|
// classpnp's power routines will send the START_STOP_UNIT command to the
|
|
// device whether or not it supports locking (atapi does not support locking
|
|
// and if we requested them we would end up not stopping audio on atapi
|
|
// devices).
|
|
// SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_DISABLE_SPIN_UP);
|
|
// SET_FLAG(deviceExtension->ScanForSpecialFlags, CDROM_SPECIAL_NO_QUEUE_LOCK);
|
|
|
|
if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_TOSHIBA_SD_W1101))
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceScanSpecialDevices: Found Toshiba SD-W1101 DVD-RAM "
|
|
"-- This drive will *NOT* support DVD-ROM playback.\n"));
|
|
}
|
|
else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_HITACHI_GD_2000))
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceScanSpecialDevices: Found Hitachi GD-2000\n"));
|
|
|
|
// Setup an error handler to spin up the drive when it idles out
|
|
// since it seems to like to fail to spin itself back up on its
|
|
// own for a REPORT_KEY command. It may also lose the AGIDs that
|
|
// it has given, which will result in DVD playback failures.
|
|
// This routine will just do what it can...
|
|
DeviceExtension->DeviceAdditionalData.ErrorHandler = DeviceErrorHandlerForHitachiGD2000;
|
|
|
|
// this drive may require START_UNIT commands to spin
|
|
// the drive up when it's spun itself down.
|
|
SET_FLAG(DeviceExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
|
}
|
|
else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_FUJITSU_FMCD_10x))
|
|
{
|
|
// When Read command is issued to FMCD-101 or FMCD-102 and there is a music
|
|
// cd in it. It takes longer time than SCSI_CDROM_TIMEOUT before returning
|
|
// error status.
|
|
DeviceExtension->TimeOutValue = 20;
|
|
}
|
|
else if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_DEC_RRD))
|
|
{
|
|
NTSTATUS status;
|
|
PMODE_PARM_READ_WRITE_DATA modeParameters;
|
|
SCSI_REQUEST_BLOCK srb = {0};
|
|
PCDB cdb;
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_INIT,
|
|
"DeviceScanSpecialDevices: Found DEC RRD.\n"));
|
|
|
|
DeviceExtension->DeviceAdditionalData.IsDecRrd = TRUE;
|
|
|
|
// Setup an error handler to reinitialize the cd rom after it is reset?
|
|
//
|
|
//DeviceExtension->DevInfo->ClassError = DecRrdProcessError;
|
|
|
|
// Found a DEC RRD cd-rom. These devices do not pass MS HCT
|
|
// multi-media tests because the DEC firmware modifieds the block
|
|
// from the PC-standard 2K to 512. Change the block transfer size
|
|
// back to the PC-standard 2K by using a mode select command.
|
|
|
|
modeParameters = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
sizeof(MODE_PARM_READ_WRITE_DATA),
|
|
CDROM_TAG_MODE_DATA);
|
|
if (modeParameters == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory(modeParameters, sizeof(MODE_PARM_READ_WRITE_DATA));
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
// Set the block length to 2K.
|
|
modeParameters->ParameterListHeader.BlockDescriptorLength = sizeof(MODE_PARAMETER_BLOCK);
|
|
|
|
// Set block length to 2K (0x0800) in Parameter Block.
|
|
modeParameters->ParameterListBlock.BlockLength[0] = 0x00; //MSB
|
|
modeParameters->ParameterListBlock.BlockLength[1] = 0x08;
|
|
modeParameters->ParameterListBlock.BlockLength[2] = 0x00; //LSB
|
|
|
|
// Build the mode select CDB.
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = DeviceExtension->TimeOutValue;
|
|
|
|
cdb = (PCDB)srb.Cdb;
|
|
cdb->MODE_SELECT.PFBit = 1;
|
|
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
|
|
cdb->MODE_SELECT.ParameterListLength = HITACHI_MODE_DATA_SIZE;
|
|
|
|
// Send the request to the device.
|
|
status = DeviceSendSrbSynchronously(DeviceExtension->Device,
|
|
&srb,
|
|
modeParameters,
|
|
sizeof(MODE_PARM_READ_WRITE_DATA),
|
|
TRUE,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"DeviceScanSpecialDevices: Setting DEC RRD to 2K block"
|
|
"size failed [%x]\n", status));
|
|
}
|
|
|
|
ExFreePool(modeParameters);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
VOID
|
|
ScanForSpecialHandler(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_In_ ULONG_PTR HackFlags
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
CLEAR_FLAG(HackFlags, CDROM_HACK_INVALID_FLAGS);
|
|
|
|
DeviceExtension->DeviceAdditionalData.HackFlags = HackFlags;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceCacheDeviceInquiryData(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
get inquiry data from device and cache it into device extension
|
|
The first INQUIRY command sent is with 0x24 bytes required data,
|
|
as ATAport driver always sends this to enumerate devices and 0x24
|
|
bytes is the minimum data device should return by spec.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SCSI_REQUEST_BLOCK srb = {0};
|
|
PCDB cdb = (PCDB)(&srb.Cdb);
|
|
PINQUIRYDATA tmpInquiry = NULL;
|
|
|
|
// by spec, device should return at least 36 bytes.
|
|
ULONG requestedInquiryTransferBytes = MINIMUM_CDROM_INQUIRY_SIZE;
|
|
BOOLEAN needResendCommand = TRUE;
|
|
BOOLEAN portDriverHack = FALSE;
|
|
|
|
// this ensures that the strings vendorID, productID, and firmwareRevision
|
|
// are all available in the inquiry data. In addition, MMC spec requires
|
|
// all type 5 devices to have minimum 36 bytes of inquiry.
|
|
static const UCHAR minInquiryAdditionalLength =
|
|
MINIMUM_CDROM_INQUIRY_SIZE -
|
|
RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
|
|
|
|
C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength) <= 8 );
|
|
C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) == MINIMUM_CDROM_INQUIRY_SIZE );
|
|
|
|
PAGED_CODE();
|
|
|
|
// short-circuit here for if already cached for this device
|
|
// required to avoid use of scratch buffer after initialization
|
|
// of MCN code.
|
|
if (DeviceExtension->DeviceAdditionalData.CachedInquiryData != NULL)
|
|
{
|
|
NT_ASSERT(DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount != 0);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// 1. retrieve the inquiry data length
|
|
|
|
// 1.1 allocate inquiry data buffer
|
|
tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
requestedInquiryTransferBytes,
|
|
CDROM_TAG_INQUIRY);
|
|
if (tmpInquiry == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// 1.2 send INQUIRY command
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
srb.CdbLength = 6;
|
|
cdb->AsByte[0] = SCSIOP_INQUIRY;
|
|
cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
|
|
cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
|
|
|
|
status = DeviceSendSrbSynchronously(DeviceExtension->Device,
|
|
&srb,
|
|
tmpInquiry,
|
|
requestedInquiryTransferBytes,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
// 1.3 get required data length
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
if ((requestedInquiryTransferBytes == srb.DataTransferLength) &&
|
|
(requestedInquiryTransferBytes == (tmpInquiry->AdditionalLength +
|
|
RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength))) )
|
|
{
|
|
// device has only 36 bytes of INQUIRY data. do not need to resend the command.
|
|
needResendCommand = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// workaround an ATAPI.SYS bug where additional length field is set to zero
|
|
if (tmpInquiry->AdditionalLength == 0)
|
|
{
|
|
tmpInquiry->AdditionalLength = minInquiryAdditionalLength;
|
|
portDriverHack = TRUE;
|
|
}
|
|
|
|
requestedInquiryTransferBytes =
|
|
tmpInquiry->AdditionalLength +
|
|
RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength);
|
|
|
|
if (requestedInquiryTransferBytes >= MINIMUM_CDROM_INQUIRY_SIZE)
|
|
{
|
|
needResendCommand = TRUE;
|
|
}
|
|
else
|
|
{
|
|
needResendCommand = FALSE;
|
|
//Length is small than minimum length, error out.
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. retrieve the inquiry data if still needed.
|
|
|
|
// 2.1 Clean up.
|
|
if (NT_SUCCESS(status) && needResendCommand)
|
|
{
|
|
FREE_POOL(tmpInquiry);
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
requestedInquiryTransferBytes,
|
|
CDROM_TAG_INQUIRY);
|
|
if (tmpInquiry == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
// 2.2 resend INQUIRY command
|
|
if (NT_SUCCESS(status) && needResendCommand)
|
|
{
|
|
srb.CdbLength = 6;
|
|
cdb->AsByte[0] = SCSIOP_INQUIRY;
|
|
cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
|
|
cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
|
|
|
|
status = DeviceSendSrbSynchronously( DeviceExtension->Device,
|
|
&srb,
|
|
tmpInquiry,
|
|
requestedInquiryTransferBytes,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
// Workaround for drive reports that it has more INQUIRY data than reality.
|
|
if ((srb.SrbStatus == SRB_STATUS_DATA_OVERRUN) &&
|
|
(srb.DataTransferLength < requestedInquiryTransferBytes) &&
|
|
(srb.DataTransferLength >= MINIMUM_CDROM_INQUIRY_SIZE))
|
|
{
|
|
//Port driver says buffer size mismatch (buffer underrun),
|
|
//retry with the real buffer size it could return.
|
|
requestedInquiryTransferBytes = srb.DataTransferLength;
|
|
|
|
FREE_POOL(tmpInquiry);
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
tmpInquiry = ExAllocatePoolWithTag(NonPagedPoolNx,
|
|
requestedInquiryTransferBytes,
|
|
CDROM_TAG_INQUIRY);
|
|
if (tmpInquiry == NULL)
|
|
{
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else
|
|
{
|
|
srb.CdbLength = 6;
|
|
cdb->AsByte[0] = SCSIOP_INQUIRY;
|
|
cdb->AsByte[3] = (UCHAR)( requestedInquiryTransferBytes >> (8*1) );
|
|
cdb->AsByte[4] = (UCHAR)( requestedInquiryTransferBytes >> (8*0) );
|
|
|
|
status = DeviceSendSrbSynchronously(DeviceExtension->Device,
|
|
&srb,
|
|
tmpInquiry,
|
|
requestedInquiryTransferBytes,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check the transferred data length for safe.
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
requestedInquiryTransferBytes = srb.DataTransferLength;
|
|
|
|
if (requestedInquiryTransferBytes < MINIMUM_CDROM_INQUIRY_SIZE)
|
|
{
|
|
// should never occur
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
|
|
// ensure we got some non-zero data....
|
|
// This is done so we don't accidentally work around the
|
|
// ATAPI.SYS bug when no data was transferred.
|
|
if (NT_SUCCESS(status) && portDriverHack)
|
|
{
|
|
PULONG tmp = (PULONG)tmpInquiry;
|
|
ULONG i = MINIMUM_CDROM_INQUIRY_SIZE / sizeof(ULONG);
|
|
C_ASSERT( RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, ProductRevisionLevel) % sizeof(ULONG) == 0 );
|
|
|
|
// wouldn't you know it -- there is no RtlIsMemoryZero() function; Make one up.
|
|
for ( ; i != 0; i--)
|
|
{
|
|
if (*tmp != 0)
|
|
{
|
|
break; // out of this inner FOR loop -- guarantees 'i != 0'
|
|
}
|
|
tmp++;
|
|
}
|
|
|
|
if (i == 0) // all loop'd successfully
|
|
{
|
|
// should never occur to successfully get all zero'd data
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if everything succeeded, then (and only then) modify the device extension
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
DeviceExtension->DeviceAdditionalData.CachedInquiryData = tmpInquiry;
|
|
DeviceExtension->DeviceAdditionalData.CachedInquiryDataByteCount = requestedInquiryTransferBytes;
|
|
}
|
|
else
|
|
{
|
|
FREE_POOL(tmpInquiry);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceGetMmcSupportInfo(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_Out_ PBOOLEAN IsMmcDevice
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
check if the device is MMC capable.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device extension.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
IsMmcDevice - TRUE (MMC capable); FALSE (not MMC device)
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ULONG size;
|
|
ULONG previouslyFailed;
|
|
|
|
PAGED_CODE();
|
|
|
|
*IsMmcDevice = FALSE;
|
|
|
|
// read the registry in case the drive failed previously,
|
|
// and a timeout is occurring.
|
|
previouslyFailed = FALSE;
|
|
DeviceGetParameter(DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_NON_MMC_DRIVE_NAME,
|
|
&previouslyFailed);
|
|
|
|
if (previouslyFailed)
|
|
{
|
|
SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT);
|
|
}
|
|
|
|
// read from the registry in case the drive reports bad profile lengths
|
|
previouslyFailed = FALSE;
|
|
DeviceGetParameter(DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE,
|
|
&previouslyFailed);
|
|
|
|
if (previouslyFailed)
|
|
{
|
|
SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES);
|
|
}
|
|
|
|
// check for the ProfileList feature to determine if the drive is MMC compliant
|
|
// and set the "size" local variable to total GetConfig data size available.
|
|
// NOTE: This will exit this function in some error paths.
|
|
{
|
|
GET_CONFIGURATION_HEADER localHeader = {0};
|
|
ULONG usable = 0;
|
|
|
|
status = DeviceGetConfiguration(DeviceExtension->Device,
|
|
&localHeader,
|
|
sizeof(GET_CONFIGURATION_HEADER),
|
|
&usable,
|
|
FeatureProfileList,
|
|
SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
|
|
|
|
if (status == STATUS_INVALID_DEVICE_REQUEST ||
|
|
status == STATUS_NO_MEDIA_IN_DEVICE ||
|
|
status == STATUS_IO_DEVICE_ERROR ||
|
|
status == STATUS_IO_TIMEOUT)
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"GetConfiguration Failed (%x), device %p not mmc-compliant\n",
|
|
status, DeviceExtension->DeviceObject
|
|
));
|
|
|
|
previouslyFailed = TRUE;
|
|
DeviceSetParameter( DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_NON_MMC_DRIVE_NAME,
|
|
previouslyFailed);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (!NT_SUCCESS(status))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"GetConfiguration Failed, status %x -- defaulting to -ROM\n",
|
|
status));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if (usable < sizeof(GET_CONFIGURATION_HEADER))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"GetConfiguration Failed, returned only %x bytes!\n", usable));
|
|
previouslyFailed = TRUE;
|
|
DeviceSetParameter( DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_NON_MMC_DRIVE_NAME,
|
|
previouslyFailed);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
size = (localHeader.DataLength[0] << 24) |
|
|
(localHeader.DataLength[1] << 16) |
|
|
(localHeader.DataLength[2] << 8) |
|
|
(localHeader.DataLength[3] << 0) ;
|
|
|
|
|
|
if ((size <= 4) || (size + 4 < size))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"GetConfiguration Failed, claims MMC support but doesn't "
|
|
"correctly return config length! (%x)\n",
|
|
size
|
|
));
|
|
previouslyFailed = TRUE;
|
|
DeviceSetParameter( DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_NON_MMC_DRIVE_NAME,
|
|
previouslyFailed);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else if ((size % 4) != 0)
|
|
{
|
|
if ((size % 2) != 0)
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"GetConfiguration Failed, returned odd number of bytes %x!\n",
|
|
size
|
|
));
|
|
previouslyFailed = TRUE;
|
|
DeviceSetParameter( DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_NON_MMC_DRIVE_NAME,
|
|
previouslyFailed);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
if (TEST_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_VENDOR_PROFILES))
|
|
{
|
|
// we've already caught this and ASSERT'd once, so don't do it again
|
|
}
|
|
else
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"GetConfiguration returned a size that is not per spec (%x bytes), this is probably because of a vendor specific data header with a size not divisible by 4.\n",
|
|
size
|
|
));
|
|
previouslyFailed = TRUE;
|
|
DeviceSetParameter(DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_NON_MMC_VENDOR_SPECIFIC_PROFILE,
|
|
previouslyFailed);
|
|
}
|
|
}
|
|
}
|
|
|
|
size += 4; // sizeof the datalength fields
|
|
}
|
|
|
|
*IsMmcDevice = TRUE;
|
|
|
|
// This code doesn't handle total get config size over 64k
|
|
NT_ASSERT( size <= MAXUSHORT );
|
|
|
|
// Check for SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE support in the device.
|
|
// NOTE: This will exit this function in some error paths.
|
|
{
|
|
ULONG featureSize = sizeof(GET_CONFIGURATION_HEADER)+sizeof(FEATURE_HEADER);
|
|
ULONG usable = 0;
|
|
|
|
PGET_CONFIGURATION_HEADER configBuffer = ExAllocatePoolWithTag(
|
|
NonPagedPoolNx,
|
|
featureSize,
|
|
CDROM_TAG_GET_CONFIG);
|
|
|
|
if (configBuffer == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// read the registry in case the drive failed previously,
|
|
// and a timeout is occurring.
|
|
previouslyFailed = FALSE;
|
|
DeviceGetParameter( DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_TYPE_ONE_GET_CONFIG_NAME,
|
|
&previouslyFailed);
|
|
|
|
if (previouslyFailed)
|
|
{
|
|
SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
|
|
FREE_POOL(configBuffer);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// Get only the config and feature header
|
|
status = DeviceGetConfiguration(DeviceExtension->Device,
|
|
configBuffer,
|
|
featureSize,
|
|
&usable,
|
|
FeatureProfileList,
|
|
SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE);
|
|
|
|
if (!NT_SUCCESS(status) || (usable < featureSize))
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Type One GetConfiguration Failed. Usable buffer size: %d\n", usable));
|
|
previouslyFailed = TRUE;
|
|
}
|
|
else
|
|
{
|
|
PFEATURE_HEADER featureHeader;
|
|
ULONG totalAvailableBytes = 0;
|
|
ULONG expectedAvailableBytes = 0;
|
|
|
|
REVERSE_BYTES(&totalAvailableBytes, configBuffer->DataLength);
|
|
totalAvailableBytes += RTL_SIZEOF_THROUGH_FIELD(GET_CONFIGURATION_HEADER, DataLength);
|
|
|
|
featureHeader = (PFEATURE_HEADER) ((PUCHAR)configBuffer + sizeof(GET_CONFIGURATION_HEADER));
|
|
expectedAvailableBytes = sizeof(GET_CONFIGURATION_HEADER) +
|
|
sizeof(FEATURE_HEADER) +
|
|
featureHeader->AdditionalLength;
|
|
|
|
if (totalAvailableBytes > expectedAvailableBytes)
|
|
{
|
|
// Device is returning more than required size. Most likely the device
|
|
// is returning TYPE ALL data. Set the flag to use TYPE ALL for TYPE ONE
|
|
// requets
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
|
|
"Type One GetConfiguration Failed. "
|
|
"Device returned %d bytes instead of %d bytes\n",
|
|
size, featureSize));
|
|
|
|
previouslyFailed = TRUE;
|
|
}
|
|
}
|
|
|
|
FREE_POOL(configBuffer);
|
|
|
|
if (previouslyFailed == TRUE)
|
|
{
|
|
DeviceSetParameter( DeviceExtension,
|
|
CDROM_SUBKEY_NAME,
|
|
CDROM_TYPE_ONE_GET_CONFIG_NAME,
|
|
previouslyFailed);
|
|
|
|
SET_FLAG(DeviceExtension->DeviceAdditionalData.HackFlags, CDROM_HACK_BAD_TYPE_ONE_GET_CONFIG);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
DeviceSetRawReadInfo(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the CDROM capabilities mode page and save information
|
|
in the device extension needed for raw reads.
|
|
NOTE: this function is only valid for MMC device
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device context
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PUCHAR buffer = NULL;
|
|
ULONG count = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
// Check whether the device can return C2 error flag bits and the block
|
|
// error byte. If so, save this info and fill in appropriate flag during
|
|
// raw read requests.
|
|
|
|
// Start by checking the GET_CONFIGURATION data
|
|
{
|
|
PFEATURE_DATA_CD_READ cdReadHeader = NULL;
|
|
ULONG additionalLength = sizeof(FEATURE_DATA_CD_READ) - sizeof(FEATURE_HEADER);
|
|
|
|
cdReadHeader = DeviceFindFeaturePage(DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBuffer,
|
|
DeviceExtension->DeviceAdditionalData.Mmc.CapabilitiesBufferSize,
|
|
FeatureCdRead);
|
|
|
|
if ((cdReadHeader != NULL) &&
|
|
(cdReadHeader->Header.AdditionalLength >= additionalLength) &&
|
|
(cdReadHeader->C2ErrorData)
|
|
)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
|
"DeviceSetRawReadInfo: FDO %p GET_CONFIG shows ability to read C2 error bits\n",
|
|
DeviceExtension->DeviceObject
|
|
));
|
|
DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE; // Device returns C2 error info.
|
|
}
|
|
}
|
|
|
|
// Unfortunately, the only way to check for the ability to read R-W subcode
|
|
// information is via MODE_SENSE. Do so here, and check the C2 bit as well
|
|
// in case the drive has a firmware bug where it fails to report this ability
|
|
// in GET_CONFIG (which some drives do).
|
|
for (count = 0; count < 6; count++)
|
|
{
|
|
SCSI_REQUEST_BLOCK srb = {0};
|
|
PCDB cdb = (PCDB)srb.Cdb;
|
|
ULONG bufferLength = 0;
|
|
|
|
// Build the MODE SENSE CDB. Try 10-byte CDB first.
|
|
if ((count/3) == 0)
|
|
{
|
|
bufferLength = sizeof(CDVD_CAPABILITIES_PAGE) +
|
|
sizeof(MODE_PARAMETER_HEADER10) +
|
|
sizeof(MODE_PARAMETER_BLOCK);
|
|
|
|
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|
cdb->MODE_SENSE10.Dbd = 1;
|
|
cdb->MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES;
|
|
cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(bufferLength >> 8);
|
|
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufferLength >> 0);
|
|
srb.CdbLength = 10;
|
|
}
|
|
else
|
|
{
|
|
bufferLength = sizeof(CDVD_CAPABILITIES_PAGE) +
|
|
sizeof(MODE_PARAMETER_HEADER) +
|
|
sizeof(MODE_PARAMETER_BLOCK);
|
|
|
|
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
|
cdb->MODE_SENSE.Dbd = 1;
|
|
cdb->MODE_SENSE.PageCode = MODE_PAGE_CAPABILITIES;
|
|
cdb->MODE_SENSE.AllocationLength = (UCHAR)bufferLength;
|
|
srb.CdbLength = 6;
|
|
}
|
|
|
|
// Set timeout value from device extension.
|
|
srb.TimeOutValue = DeviceExtension->TimeOutValue;
|
|
|
|
buffer = ExAllocatePoolWithTag(NonPagedPoolNx, bufferLength, CDROM_TAG_MODE_DATA);
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_IOCTL,
|
|
"DeviceSetRawReadInfo: cannot allocate "
|
|
"buffer, so not setting raw read info for FDO %p\n",
|
|
DeviceExtension->DeviceObject
|
|
));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FnExit;
|
|
}
|
|
|
|
RtlZeroMemory(buffer, bufferLength);
|
|
|
|
status = DeviceSendSrbSynchronously(DeviceExtension->Device,
|
|
&srb,
|
|
buffer,
|
|
bufferLength,
|
|
FALSE,
|
|
NULL);
|
|
|
|
if (NT_SUCCESS(status) ||
|
|
(status == STATUS_DATA_OVERRUN) ||
|
|
(status == STATUS_BUFFER_OVERFLOW))
|
|
{
|
|
PCDVD_CAPABILITIES_PAGE capabilities = NULL;
|
|
|
|
// determine where the capabilities page really is
|
|
if ((count/3) == 0)
|
|
{
|
|
PMODE_PARAMETER_HEADER10 p = (PMODE_PARAMETER_HEADER10)buffer;
|
|
capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer +
|
|
sizeof(MODE_PARAMETER_HEADER10) +
|
|
(p->BlockDescriptorLength[0] * 256) +
|
|
p->BlockDescriptorLength[1]);
|
|
}
|
|
else
|
|
{
|
|
PMODE_PARAMETER_HEADER p = (PMODE_PARAMETER_HEADER)buffer;
|
|
capabilities = (PCDVD_CAPABILITIES_PAGE)(buffer +
|
|
sizeof(MODE_PARAMETER_HEADER) +
|
|
p->BlockDescriptorLength);
|
|
}
|
|
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
|
"DeviceSetRawReadInfo: FDO %p CDVD Capabilities buffer %p\n",
|
|
DeviceExtension->DeviceObject,
|
|
buffer
|
|
));
|
|
|
|
if (capabilities->PageCode == MODE_PAGE_CAPABILITIES)
|
|
{
|
|
if (capabilities->C2Pointers)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
|
"DeviceSetRawReadInfo: FDO %p supports C2 error bits in READ_CD command\n",
|
|
DeviceExtension->DeviceObject
|
|
));
|
|
DeviceExtension->DeviceAdditionalData.Mmc.ReadCdC2Pointers = TRUE;
|
|
}
|
|
|
|
if (capabilities->RWSupported)
|
|
{
|
|
TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_IOCTL,
|
|
"DeviceSetRawReadInfo: FDO %p supports raw subcode in READ_CD command\n",
|
|
DeviceExtension->DeviceObject
|
|
));
|
|
DeviceExtension->DeviceAdditionalData.Mmc.ReadCdSubCode = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
|
"DeviceSetRawReadInfo: FDO %p failed %x byte mode sense, status %x\n",
|
|
DeviceExtension->DeviceObject,
|
|
(((count/3) == 0) ? 10 : 6),
|
|
status
|
|
));
|
|
|
|
FREE_POOL(buffer);
|
|
}
|
|
|
|
if (count == 6)
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_IOCTL,
|
|
"DeviceSetRawReadInfo: FDO %p couldn't get mode sense data\n",
|
|
DeviceExtension->DeviceObject
|
|
));
|
|
}
|
|
|
|
FnExit:
|
|
|
|
if (buffer)
|
|
{
|
|
FREE_POOL(buffer);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
_IRQL_requires_max_(APC_LEVEL)
|
|
NTSTATUS
|
|
DeviceInitializeDvd(
|
|
_In_ WDFDEVICE Device
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the region of DVD drive
|
|
NOTE: this routine uses ScratchBuffer, it must be called after ScratchBuffer allocated.
|
|
|
|
Arguments:
|
|
|
|
Device - device object
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PCDROM_DEVICE_EXTENSION deviceExtension;
|
|
PDVD_COPY_PROTECT_KEY copyProtectKey = NULL;
|
|
PDVD_RPC_KEY rpcKey = NULL;
|
|
ULONG bufferLen = 0;
|
|
size_t bytesReturned;
|
|
|
|
PAGED_CODE();
|
|
|
|
deviceExtension = DeviceGetExtension(Device);
|
|
|
|
// check to see if we have a DVD device
|
|
if (deviceExtension->DeviceAdditionalData.DriveDeviceType != FILE_DEVICE_DVD)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// we got a DVD drive.
|
|
bufferLen = DVD_RPC_KEY_LENGTH;
|
|
copyProtectKey = (PDVD_COPY_PROTECT_KEY)ExAllocatePoolWithTag(PagedPool,
|
|
bufferLen,
|
|
DVD_TAG_RPC2_CHECK);
|
|
|
|
if (copyProtectKey == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
// get the device region
|
|
RtlZeroMemory (copyProtectKey, bufferLen);
|
|
copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
|
|
copyProtectKey->KeyType = DvdGetRpcKey;
|
|
|
|
// perform IOCTL_DVD_READ_KEY
|
|
status = DvdStartSessionReadKey(deviceExtension,
|
|
IOCTL_DVD_READ_KEY,
|
|
NULL,
|
|
copyProtectKey,
|
|
DVD_RPC_KEY_LENGTH,
|
|
copyProtectKey,
|
|
DVD_RPC_KEY_LENGTH,
|
|
&bytesReturned);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
rpcKey = (PDVD_RPC_KEY)copyProtectKey->KeyData;
|
|
|
|
// TypeCode of zero means that no region has been set.
|
|
if (rpcKey->TypeCode == 0)
|
|
{
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP,
|
|
"DVD Initialize (%p): must choose DVD region\n",
|
|
Device));
|
|
deviceExtension->DeviceAdditionalData.PickDvdRegion = 1;
|
|
|
|
// set the device region code to be the same as region code on media.
|
|
if (deviceExtension->DeviceAdditionalData.Mmc.IsCssDvd)
|
|
{
|
|
DevicePickDvdRegion(Device);
|
|
}
|
|
}
|
|
}
|
|
|
|
FREE_POOL(copyProtectKey);
|
|
|
|
// return status of IOCTL_DVD_READ_KEY will be ignored.
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
|
_IRQL_requires_max_(PASSIVE_LEVEL)
|
|
NTSTATUS
|
|
DeviceIsPortable(
|
|
_In_ PCDROM_DEVICE_EXTENSION DeviceExtension,
|
|
_Out_ PBOOLEAN IsPortable
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the volume is on a portable storage device.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - device context
|
|
IsPortable - device is portable
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEVPROP_BOOLEAN isInternal = DEVPROP_FALSE;
|
|
BOOLEAN isPortable = FALSE;
|
|
ULONG size = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
DEVPROPTYPE type = DEVPROP_TYPE_EMPTY;
|
|
|
|
PAGED_CODE();
|
|
|
|
*IsPortable = FALSE;
|
|
|
|
// Check to see if the underlying device object is in local machine container
|
|
status = IoGetDevicePropertyData(DeviceExtension->LowerPdo,
|
|
&DEVPKEY_Device_InLocalMachineContainer,
|
|
0,
|
|
0,
|
|
sizeof(isInternal),
|
|
&isInternal,
|
|
&size,
|
|
&type);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
NT_ASSERT(size == sizeof(isInternal));
|
|
NT_ASSERT(type == DEVPROP_TYPE_BOOLEAN);
|
|
|
|
// Volume is hot-pluggable if the disk pdo container id differs from that of root device
|
|
if (isInternal == DEVPROP_TRUE)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
isPortable = TRUE;
|
|
|
|
// Examine the bus type to ensure that this really is a fixed device
|
|
if (DeviceExtension->DeviceDescriptor->BusType == BusTypeFibre ||
|
|
DeviceExtension->DeviceDescriptor->BusType == BusTypeiScsi ||
|
|
DeviceExtension->DeviceDescriptor->BusType == BusTypeRAID)
|
|
{
|
|
isPortable = FALSE;
|
|
}
|
|
|
|
*IsPortable = isPortable;
|
|
|
|
Cleanup:
|
|
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
|
|
#pragma warning(pop) // un-sets any local warning changes
|
|
|