7138 lines
217 KiB
C
7138 lines
217 KiB
C
/*--
|
||
|
||
Copyright (C) Microsoft Corporation, 1991 - 1999
|
||
|
||
Module Name:
|
||
|
||
cdrom.c
|
||
|
||
Abstract:
|
||
|
||
The CDROM class driver tranlates IRPs to SRBs with embedded CDBs
|
||
and sends them to its devices through the port driver.
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
SCSI Tape, CDRom and Disk class drivers share common routines
|
||
that can be found in the CLASS directory (..\ntos\dd\class).
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "stddef.h"
|
||
#include "string.h"
|
||
|
||
#include "ntddk.h"
|
||
|
||
#include "ntddcdvd.h"
|
||
#include "classpnp.h"
|
||
|
||
#include "initguid.h"
|
||
#include "ntddstor.h"
|
||
#include "cdrom.h"
|
||
|
||
#include "cdrom.tmh"
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
#pragma alloc_text(INIT, DriverEntry)
|
||
|
||
#pragma alloc_text(PAGE, CdRomUnload)
|
||
#pragma alloc_text(PAGE, CdRomAddDevice)
|
||
#pragma alloc_text(PAGE, CdRomCreateDeviceObject)
|
||
#pragma alloc_text(PAGE, CdRomStartDevice)
|
||
#pragma alloc_text(PAGE, ScanForSpecial)
|
||
#pragma alloc_text(PAGE, ScanForSpecialHandler)
|
||
#pragma alloc_text(PAGE, CdRomRemoveDevice)
|
||
#pragma alloc_text(PAGE, CdRomGetDeviceType)
|
||
#pragma alloc_text(PAGE, CdRomReadWriteVerification)
|
||
#pragma alloc_text(PAGE, CdRomGetDeviceParameter)
|
||
#pragma alloc_text(PAGE, CdRomSetDeviceParameter)
|
||
#pragma alloc_text(PAGE, CdRomPickDvdRegion)
|
||
#pragma alloc_text(PAGE, CdRomIsPlayActive)
|
||
|
||
#pragma alloc_text(PAGEHITA, HitachiProcessError)
|
||
#pragma alloc_text(PAGEHIT2, HitachiProcessErrorGD2000)
|
||
|
||
#pragma alloc_text(PAGETOSH, ToshibaProcessErrorCompletion)
|
||
#pragma alloc_text(PAGETOSH, ToshibaProcessError)
|
||
|
||
#endif
|
||
|
||
#define IS_WRITE_REQUEST(irpStack) \
|
||
(irpStack->MajorFunction == IRP_MJ_WRITE)
|
||
|
||
#define IS_READ_WRITE_REQUEST(irpStack) \
|
||
((irpStack->MajorFunction == IRP_MJ_READ) || \
|
||
(irpStack->MajorFunction == IRP_MJ_WRITE) || \
|
||
((irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) && \
|
||
(irpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_RAW_READ)))
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the cdrom class driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to driver object created by system.
|
||
|
||
RegistryPath - Pointer to the name of the services node for this driver.
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status from the initialization operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
CLASS_INIT_DATA InitializationData;
|
||
PCDROM_DRIVER_EXTENSION driverExtension;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
WPP_INIT_TRACING(DriverObject, RegistryPath);
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CDROM.SYS DriverObject %p loading\n", DriverObject));
|
||
|
||
status = IoAllocateDriverObjectExtension(DriverObject,
|
||
CDROM_DRIVER_EXTENSION_ID,
|
||
sizeof(CDROM_DRIVER_EXTENSION),
|
||
&driverExtension);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
TraceLog((CdromDebugWarning,
|
||
"DriverEntry !! no DriverObjectExtension %x\n", status));
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// always zero the memory, since we are now reloading the driver.
|
||
//
|
||
|
||
RtlZeroMemory(driverExtension, sizeof(CDROM_DRIVER_EXTENSION));
|
||
|
||
//
|
||
// Zero InitData
|
||
//
|
||
|
||
RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
|
||
|
||
//
|
||
// Set sizes
|
||
//
|
||
|
||
InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
|
||
|
||
InitializationData.FdoData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE;
|
||
|
||
InitializationData.FdoData.DeviceType = FILE_DEVICE_CD_ROM;
|
||
InitializationData.FdoData.DeviceCharacteristics =
|
||
FILE_REMOVABLE_MEDIA | FILE_DEVICE_SECURE_OPEN;
|
||
|
||
//
|
||
// Set entry points
|
||
//
|
||
|
||
InitializationData.FdoData.ClassError = CdRomErrorHandler;
|
||
InitializationData.FdoData.ClassInitDevice = CdRomInitDevice;
|
||
InitializationData.FdoData.ClassStartDevice = CdRomStartDevice;
|
||
InitializationData.FdoData.ClassStopDevice = CdRomStopDevice;
|
||
InitializationData.FdoData.ClassRemoveDevice = CdRomRemoveDevice;
|
||
|
||
InitializationData.FdoData.ClassReadWriteVerification = CdRomReadWriteVerification;
|
||
InitializationData.FdoData.ClassDeviceControl = CdRomDeviceControlDispatch;
|
||
|
||
InitializationData.FdoData.ClassPowerDevice = ClassSpinDownPowerHandler;
|
||
InitializationData.FdoData.ClassShutdownFlush = CdRomShutdownFlush;
|
||
InitializationData.FdoData.ClassCreateClose = NULL;
|
||
|
||
InitializationData.ClassStartIo = CdRomStartIo;
|
||
InitializationData.ClassAddDevice = CdRomAddDevice;
|
||
|
||
InitializationData.ClassTick = CdRomTickHandler;
|
||
InitializationData.ClassUnload = CdRomUnload;
|
||
|
||
//
|
||
// Call the class init routine
|
||
//
|
||
|
||
return ClassInitialize( DriverObject, RegistryPath, &InitializationData);
|
||
|
||
} // end DriverEntry()
|
||
|
||
|
||
VOID
|
||
CdRomUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
UNREFERENCED_PARAMETER(DriverObject);
|
||
TraceLog((CdromDebugTrace,
|
||
"CDROM.SYS DriverObject %p unloading\n", DriverObject));
|
||
WPP_CLEANUP(DriverObject);
|
||
return;
|
||
} // end CdRomUnload()
|
||
|
||
|
||
NTSTATUS
|
||
CdRomAddDevice(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates and initializes a new FDO for the corresponding
|
||
PDO. It may perform property queries on the FDO but cannot do any
|
||
media access operations.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - CDROM class driver object.
|
||
|
||
Pdo - the physical device object we are being added to
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get the address of the count of the number of cdroms already initialized.
|
||
//
|
||
|
||
status = CdRomCreateDeviceObject(DriverObject,
|
||
PhysicalDeviceObject);
|
||
|
||
//
|
||
// Note: this always increments driver extension counter
|
||
// it will eventually wrap, and fail additions
|
||
// if an existing cdrom has the given number.
|
||
// so unlikely that we won't even bother considering
|
||
// this case, since the cure is quite likely worse
|
||
// than the symptoms.
|
||
//
|
||
|
||
if(NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// keep track of the total number of active cdroms in IoGet(),
|
||
// as some programs use this to determine when they have found
|
||
// all the cdroms in the system.
|
||
//
|
||
|
||
TraceLog((CdromDebugTrace, "CDROM.SYS Add succeeded\n"));
|
||
IoGetConfigurationInformation()->CdRomCount++;
|
||
|
||
} else {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CDROM.SYS Add failed! %x\n", status));
|
||
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomCreateDeviceObject(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine creates an object for the device and then calls the
|
||
SCSI port driver for media capacity and sector size.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to driver object created by system.
|
||
PortDeviceObject - to connect to SCSI port driver.
|
||
DeviceCount - Number of previously installed CDROMs.
|
||
PortCapabilities - Pointer to structure returned by SCSI port
|
||
driver describing adapter capabilites (and limitations).
|
||
LunInfo - Pointer to configuration information for this device.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
UCHAR ntNameBuffer[64];
|
||
STRING ntNameString;
|
||
NTSTATUS status;
|
||
|
||
PDEVICE_OBJECT lowerDevice = NULL;
|
||
PDEVICE_OBJECT deviceObject = NULL;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
|
||
PCDROM_DATA cdData = NULL;
|
||
PCDROM_DRIVER_EXTENSION driverExtension = NULL;
|
||
ULONG deviceNumber;
|
||
|
||
CCHAR dosNameBuffer[64];
|
||
CCHAR deviceNameBuffer[64];
|
||
STRING deviceNameString;
|
||
STRING dosString;
|
||
UNICODE_STRING dosUnicodeString;
|
||
UNICODE_STRING unicodeString;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Claim the device. Note that any errors after this
|
||
// will goto the generic handler, where the device will
|
||
// be released.
|
||
//
|
||
|
||
lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
|
||
|
||
status = ClassClaimDevice(lowerDevice, FALSE);
|
||
|
||
if(!NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Someone already had this device - we're in trouble
|
||
//
|
||
|
||
ObDereferenceObject(lowerDevice);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Create device object for this device by first getting a unique name
|
||
// for the device and then creating it.
|
||
//
|
||
|
||
driverExtension = IoGetDriverObjectExtension(DriverObject,
|
||
CDROM_DRIVER_EXTENSION_ID);
|
||
ASSERT(driverExtension != NULL);
|
||
|
||
//
|
||
// InterlockedCdRomCounter is biased by 1.
|
||
//
|
||
|
||
deviceNumber = InterlockedIncrement(&driverExtension->InterlockedCdRomCounter) - 1;
|
||
sprintf(ntNameBuffer, "\\Device\\CdRom%d", deviceNumber);
|
||
|
||
|
||
status = ClassCreateDeviceObject(DriverObject,
|
||
ntNameBuffer,
|
||
PhysicalDeviceObject,
|
||
TRUE,
|
||
&deviceObject);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CreateCdRomDeviceObjects: Can not create device %s\n",
|
||
ntNameBuffer));
|
||
|
||
goto CreateCdRomDeviceObjectExit;
|
||
}
|
||
|
||
//
|
||
// Indicate that IRPs should include MDLs.
|
||
//
|
||
|
||
SET_FLAG(deviceObject->Flags, DO_DIRECT_IO);
|
||
|
||
fdoExtension = deviceObject->DeviceExtension;
|
||
|
||
//
|
||
// Back pointer to device object.
|
||
//
|
||
|
||
fdoExtension->CommonExtension.DeviceObject = deviceObject;
|
||
|
||
//
|
||
// This is the physical device.
|
||
//
|
||
|
||
fdoExtension->CommonExtension.PartitionZeroExtension = fdoExtension;
|
||
|
||
//
|
||
// Initialize lock count to zero. The lock count is used to
|
||
// disable the ejection mechanism when media is mounted.
|
||
//
|
||
|
||
fdoExtension->LockCount = 0;
|
||
|
||
//
|
||
// Save system cdrom number
|
||
//
|
||
|
||
fdoExtension->DeviceNumber = deviceNumber;
|
||
|
||
//
|
||
// Set the alignment requirements for the device based on the
|
||
// host adapter requirements
|
||
//
|
||
|
||
if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) {
|
||
deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement;
|
||
}
|
||
|
||
//
|
||
// Save the device descriptors
|
||
//
|
||
|
||
fdoExtension->AdapterDescriptor = NULL;
|
||
|
||
fdoExtension->DeviceDescriptor = NULL;
|
||
|
||
//
|
||
// Clear the SrbFlags and disable synchronous transfers
|
||
//
|
||
|
||
fdoExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
||
|
||
//
|
||
// Finally, attach to the PDO
|
||
//
|
||
|
||
fdoExtension->LowerPdo = PhysicalDeviceObject;
|
||
|
||
fdoExtension->CommonExtension.LowerDeviceObject =
|
||
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
|
||
|
||
if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {
|
||
|
||
//
|
||
// Uh - oh, we couldn't attach
|
||
// cleanup and return
|
||
//
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
goto CreateCdRomDeviceObjectExit;
|
||
}
|
||
|
||
//
|
||
// CdRom uses an extra stack location for synchronizing it's start io
|
||
// routine
|
||
//
|
||
|
||
deviceObject->StackSize++;
|
||
|
||
//
|
||
// cdData is used a few times below
|
||
//
|
||
|
||
cdData = fdoExtension->CommonExtension.DriverData;
|
||
|
||
//
|
||
// For NTMS to be able to easily determine drives-drv. letter matches.
|
||
//
|
||
|
||
status = CdRomCreateWellKnownName( deviceObject );
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromCreateDeviceObjects: unable to create symbolic "
|
||
"link for device %wZ\n", &fdoExtension->CommonExtension.DeviceName));
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromCreateDeviceObjects: (non-fatal error)\n"));
|
||
}
|
||
|
||
ClassUpdateInformationInRegistry(deviceObject, "CdRom",
|
||
fdoExtension->DeviceNumber, NULL, 0);
|
||
|
||
//
|
||
// from above IoGetAttachedDeviceReference
|
||
//
|
||
|
||
ObDereferenceObject(lowerDevice);
|
||
|
||
//
|
||
// need to init timerlist here in case a remove occurs
|
||
// without a start, since we check the list is empty on remove.
|
||
//
|
||
|
||
cdData->DelayedRetryIrp = NULL;
|
||
cdData->DelayedRetryInterval = 0;
|
||
|
||
//
|
||
// need this to be initialized for RPC Phase 1 drives (rpc0)
|
||
//
|
||
|
||
KeInitializeMutex(&cdData->Rpc0RegionMutex, 0);
|
||
|
||
//
|
||
// The device is initialized properly - mark it as such.
|
||
//
|
||
|
||
CLEAR_FLAG(deviceObject->Flags, DO_DEVICE_INITIALIZING);
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
CreateCdRomDeviceObjectExit:
|
||
|
||
//
|
||
// Release the device since an error occured.
|
||
//
|
||
|
||
// ClassClaimDevice(PortDeviceObject,
|
||
// LunInfo,
|
||
// TRUE,
|
||
// NULL);
|
||
|
||
//
|
||
// from above IoGetAttachedDeviceReference
|
||
//
|
||
|
||
ObDereferenceObject(lowerDevice);
|
||
|
||
if (deviceObject != NULL) {
|
||
IoDeleteDevice(deviceObject);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // end CreateCdRomDeviceObject()
|
||
|
||
|
||
NTSTATUS
|
||
CdRomInitDevice(
|
||
IN PDEVICE_OBJECT Fdo
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will complete the cd-rom initialization. This includes
|
||
allocating sense info buffers and srb s-lists, reading drive capacity
|
||
and setting up Media Change Notification (autorun).
|
||
|
||
This routine will not clean up allocate resources if it fails - that
|
||
is left for device stop/removal
|
||
|
||
Arguments:
|
||
|
||
Fdo - a pointer to the functional device object for this device
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
||
PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(
|
||
Fdo->DriverObject);
|
||
|
||
PVOID senseData = NULL;
|
||
|
||
ULONG timeOut;
|
||
PCDROM_DATA cddata;
|
||
|
||
BOOLEAN changerDevice;
|
||
BOOLEAN isMmcDevice = FALSE;
|
||
|
||
ULONG bps;
|
||
ULONG lastBit;
|
||
|
||
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Build the lookaside list for srb's for the physical disk. Should only
|
||
// need a couple.
|
||
//
|
||
|
||
ClassInitializeSrbLookasideList(&(fdoExtension->CommonExtension),
|
||
CDROM_SRB_LIST_SIZE);
|
||
|
||
//
|
||
// Allocate request sense buffer.
|
||
//
|
||
|
||
senseData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
SENSE_BUFFER_SIZE,
|
||
CDROM_TAG_SENSE_INFO);
|
||
|
||
if (senseData == NULL) {
|
||
|
||
//
|
||
// The buffer cannot be allocated.
|
||
//
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto CdRomInitDeviceExit;
|
||
}
|
||
|
||
//
|
||
// Set the sense data pointer in the device extension.
|
||
//
|
||
|
||
fdoExtension->SenseData = senseData;
|
||
|
||
//
|
||
// CDROMs are not partitionable so starting offset is 0.
|
||
//
|
||
|
||
commonExtension->StartingOffset.LowPart = 0;
|
||
commonExtension->StartingOffset.HighPart = 0;
|
||
|
||
//
|
||
// Set timeout value in seconds.
|
||
//
|
||
|
||
timeOut = ClassQueryTimeOutRegistryValue(Fdo);
|
||
if (timeOut) {
|
||
fdoExtension->TimeOutValue = timeOut;
|
||
} else {
|
||
fdoExtension->TimeOutValue = SCSI_CDROM_TIMEOUT;
|
||
}
|
||
|
||
cddata = (PCDROM_DATA)(commonExtension->DriverData);
|
||
|
||
//
|
||
// Set up media change support defaults.
|
||
//
|
||
|
||
KeInitializeSpinLock(&cddata->DelayedRetrySpinLock);
|
||
|
||
cddata->DelayedRetryIrp = NULL;
|
||
cddata->DelayedRetryInterval = 0;
|
||
cddata->Mmc.WriteAllowed = FALSE;
|
||
|
||
//
|
||
// Scan for controllers that require special processing.
|
||
//
|
||
|
||
ScanForSpecial(Fdo);
|
||
|
||
//
|
||
// Determine if the drive is MMC-Capable
|
||
//
|
||
|
||
CdRomIsDeviceMmcDevice(Fdo, &isMmcDevice);
|
||
|
||
if (!isMmcDevice) {
|
||
|
||
SET_FLAG(Fdo->Characteristics, FILE_READ_ONLY_DEVICE);
|
||
|
||
} else {
|
||
|
||
//
|
||
// the drive supports at least a subset of MMC commands
|
||
// (and therefore supports READ_CD, etc...)
|
||
//
|
||
|
||
cddata->Mmc.IsMmc = TRUE;
|
||
|
||
//
|
||
// allocate a buffer for all the capabilities and such
|
||
//
|
||
|
||
status = CdRomAllocateMmcResources(Fdo);
|
||
if (!NT_SUCCESS(status)) {
|
||
goto CdRomInitDeviceExit;
|
||
}
|
||
|
||
|
||
#if 0
|
||
//
|
||
// determine all the various media types from the profiles feature
|
||
//
|
||
{
|
||
PFEATURE_DATA_PROFILE_LIST profileHeader;
|
||
ULONG mediaTypes = 0;
|
||
ULONG i;
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError,
|
||
"Checking all profiles for media types supported.\n"
|
||
));
|
||
|
||
profileHeader = CdRomFindFeaturePage(cddata->Mmc.CapabilitiesBuffer,
|
||
cddata->Mmc.CapabilitiesBufferSize,
|
||
FeatureProfileList);
|
||
if (profileHeader == NULL) {
|
||
|
||
//
|
||
// if profiles don't exist, there is something seriously
|
||
// wrong with this command -- it's either not a cdrom or
|
||
// one that hasn't implemented the spec correctly. exit
|
||
// now while we have the chance to do so safely.
|
||
//
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError,
|
||
"CdromDevice supports GET_CONFIGURATION, but "
|
||
"doesn't provide profiles for PDO %p!\n",
|
||
fdoExtension->LowerPdo));
|
||
status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
||
goto CdRomInitDeviceExit;
|
||
|
||
}
|
||
|
||
for (i = 0; i < MAX_CDROM_MEDIA_TYPES; i++) {
|
||
|
||
BOOLEAN profileFound;
|
||
CdRomFindProfileInProfiles(profileHeader,
|
||
MediaProfileMatch[i].Profile,
|
||
&profileFound);
|
||
if (profileFound) {
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError,
|
||
"CdromInit -> Found Profile %x => Media %x "
|
||
"(%x total)\n",
|
||
MediaProfileMatch[i].Profile,
|
||
MediaProfileMatch[i].Media,
|
||
mediaTypes + 1
|
||
));
|
||
|
||
cddata->Mmc.MediaProfileMatches[mediaTypes] =
|
||
MediaProfileMatch[i];
|
||
mediaTypes++;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if (mediaTypes == 0) {
|
||
|
||
//
|
||
// if profiles don't exist, there is something seriously
|
||
// wrong with this command -- it's either not a cdrom or
|
||
// one that hasn't implemented the spec correctly. exit
|
||
// now while we have the chance to do so safely.
|
||
//
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError,
|
||
"CdromDevice supports GET_CONFIGURATION, but "
|
||
"doesn't support any of the standard profiles "
|
||
"for PDO %p!\n", fdoExtension->LowerPdo));
|
||
status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
||
goto CdRomInitDeviceExit;
|
||
|
||
}
|
||
|
||
cddata->Mmc.MediaTypes = mediaTypes;
|
||
|
||
|
||
}
|
||
#endif // media checks, and all failure paths due to bad firmware.
|
||
|
||
//
|
||
// if the drive supports target defect management and sector-addressable
|
||
// writes, then we should allow writes to the media.
|
||
//
|
||
|
||
if (CdRomFindFeaturePage(cddata->Mmc.CapabilitiesBuffer,
|
||
cddata->Mmc.CapabilitiesBufferSize,
|
||
FeatureDefectManagement) &&
|
||
CdRomFindFeaturePage(cddata->Mmc.CapabilitiesBuffer,
|
||
cddata->Mmc.CapabilitiesBufferSize,
|
||
FeatureRandomWritable)) {
|
||
|
||
//
|
||
// the drive is target defect managed, and supports random writes
|
||
// on sector-aligment. allow writes to occur by setting the error
|
||
// handler to point to a private media change detection handler.
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"Found a WRITE capable device: %p\n", Fdo));
|
||
|
||
//
|
||
// the write specific pages have been found --
|
||
// set the error handler and set it to require an update!
|
||
//
|
||
|
||
cddata->Mmc.UpdateState = CdromMmcUpdateRequired;
|
||
cddata->ErrorHandler = CdRomMmcErrorHandler;
|
||
|
||
}
|
||
|
||
//
|
||
// ISSUE-2000/4/4-henrygab - mmc-compliant compliant drives should
|
||
// be initialized based upon reported
|
||
// capabilities, such as CSS, Analogue Audio,
|
||
// READ_CD capabilities, and (possibly) even
|
||
// drive capacity information.
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"Defaulting to READ_CD because device %p is MMC compliant\n",
|
||
Fdo));
|
||
SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
||
SET_FLAG(cddata->XAFlags, XA_USE_READ_CD);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Set the default geometry for the cdrom to match what NT 4 used.
|
||
// Classpnp will use these values to compute the cylinder count rather
|
||
// than using it's NT 5.0 defaults.
|
||
//
|
||
|
||
fdoExtension->DiskGeometry.TracksPerCylinder = 0x40;
|
||
fdoExtension->DiskGeometry.SectorsPerTrack = 0x20;
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
// NOTE: This should be change to send the Srb synchronously, then
|
||
// call CdRomInterpretReadCapacity() to properly setup the defaults.
|
||
//
|
||
|
||
status = ClassReadDriveCapacity(Fdo);
|
||
|
||
bps = fdoExtension->DiskGeometry.BytesPerSector;
|
||
|
||
if (!NT_SUCCESS(status) || !bps) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomStartDevice: Can't read capacity for device %wZ\n",
|
||
&(fdoExtension->CommonExtension.DeviceName)));
|
||
|
||
//
|
||
// Set disk geometry to default values (per ISO 9660).
|
||
//
|
||
|
||
bps = 2048;
|
||
fdoExtension->SectorShift = 11;
|
||
commonExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Insure that bytes per sector is a power of 2
|
||
// This corrects a problem with the HP 4020i CDR where it
|
||
// returns an incorrect number for bytes per sector.
|
||
//
|
||
|
||
lastBit = (ULONG) -1;
|
||
while (bps) {
|
||
lastBit++;
|
||
bps = bps >> 1;
|
||
}
|
||
|
||
bps = 1 << lastBit;
|
||
}
|
||
fdoExtension->DiskGeometry.BytesPerSector = bps;
|
||
TraceLog((CdromDebugTrace, "CdRomInitDevice: Calc'd bps = %x\n", bps));
|
||
|
||
|
||
ClassInitializeMediaChangeDetection(fdoExtension, "CdRom");
|
||
|
||
|
||
//
|
||
// test for audio read capabilities
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"Detecting XA_READ capabilities\n"));
|
||
|
||
if (CdRomGetDeviceType(Fdo) == FILE_DEVICE_DVD) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomInitDevice: DVD Devices require START_UNIT\n"));
|
||
|
||
|
||
//
|
||
// all DVD devices must support the READ_CD command
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomDetermineRawReadCapabilities: DVD devices "
|
||
"support READ_CD command for FDO %p\n", Fdo));
|
||
SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
||
SET_FLAG(cddata->XAFlags, XA_USE_READ_CD);
|
||
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else if ((fdoExtension->DeviceDescriptor->BusType != BusTypeScsi) &&
|
||
(fdoExtension->DeviceDescriptor->BusType != BusTypeAta) &&
|
||
(fdoExtension->DeviceDescriptor->BusType != BusTypeAtapi) &&
|
||
(fdoExtension->DeviceDescriptor->BusType != BusTypeUnknown)
|
||
) {
|
||
|
||
//
|
||
// devices on the newer busses must support READ_CD command
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomDetermineRawReadCapabilities: Devices for newer "
|
||
"busses must support READ_CD command for FDO %p, Bus %x\n",
|
||
Fdo, fdoExtension->DeviceDescriptor->BusType));
|
||
SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
||
SET_FLAG(cddata->XAFlags, XA_USE_READ_CD);
|
||
|
||
}
|
||
|
||
//
|
||
// now clear all our READ_CD flags if the drive should have supported
|
||
// it, but we are not sure it actually does. we still won't query
|
||
// the drive more than one time if it supports the command.
|
||
//
|
||
|
||
if (TEST_FLAG(cddata->HackFlags, CDROM_HACK_FORCE_READ_CD_DETECTION)) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"Forcing detection of READ_CD for FDO %p because "
|
||
"testing showed some firmware did not properly support it\n",
|
||
Fdo));
|
||
CLEAR_FLAG(cddata->XAFlags, XA_USE_READ_CD);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// read our READ_CD support in the registry if it was seeded.
|
||
//
|
||
{
|
||
ULONG readCdSupported = 0;
|
||
|
||
ClassGetDeviceParameter(fdoExtension,
|
||
CDROM_SUBKEY_NAME,
|
||
CDROM_READ_CD_NAME,
|
||
&readCdSupported
|
||
);
|
||
|
||
if (readCdSupported != 0) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"Defaulting to READ_CD because previously detected "
|
||
"that the device supports it for Fdo %p.\n",
|
||
Fdo
|
||
));
|
||
SET_FLAG(cddata->XAFlags, XA_USE_READ_CD);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// backwards-compatible hackish attempt to determine if the drive
|
||
// supports any method of reading digital audio from the disc.
|
||
//
|
||
|
||
if (!TEST_FLAG(cddata->XAFlags, XA_USE_READ_CD)) {
|
||
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb;
|
||
ULONG length;
|
||
PUCHAR buffer = NULL;
|
||
ULONG count;
|
||
|
||
//
|
||
// ISSUE-2000/07/05-henrygab - use the mode page to determine
|
||
// READ_CD support, then fall back on the below
|
||
// (unreliable?) hack.
|
||
//
|
||
|
||
//
|
||
// Build the MODE SENSE CDB. The data returned will be kept in the
|
||
// device extension and used to set block size.
|
||
//
|
||
|
||
length = max(sizeof(ERROR_RECOVERY_DATA),sizeof(ERROR_RECOVERY_DATA10));
|
||
|
||
buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
length,
|
||
CDROM_TAG_MODE_DATA);
|
||
|
||
if (!buffer) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomDetermineRawReadCapabilities: cannot allocate "
|
||
"buffer, so leaving for FDO %p\n", Fdo));
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto CdRomInitDeviceExit;
|
||
}
|
||
|
||
for (count = 0; count < 2; count++) {
|
||
|
||
if (count == 0) {
|
||
length = sizeof(ERROR_RECOVERY_DATA);
|
||
} else {
|
||
length = sizeof(ERROR_RECOVERY_DATA10);
|
||
}
|
||
|
||
RtlZeroMemory(buffer, length);
|
||
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
cdb = (PCDB)srb.Cdb;
|
||
|
||
srb.TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
if (count == 0) {
|
||
srb.CdbLength = 6;
|
||
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb->MODE_SENSE.PageCode = 0x1;
|
||
// note: not setting DBD in order to get the block descriptor!
|
||
cdb->MODE_SENSE.AllocationLength = (UCHAR)length;
|
||
} else {
|
||
srb.CdbLength = 10;
|
||
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
||
cdb->MODE_SENSE10.PageCode = 0x1;
|
||
// note: not setting DBD in order to get the block descriptor!
|
||
cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(length >> 8);
|
||
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(length & 0xFF);
|
||
}
|
||
|
||
status = ClassSendSrbSynchronous(Fdo,
|
||
&srb,
|
||
buffer,
|
||
length,
|
||
FALSE);
|
||
|
||
|
||
if (NT_SUCCESS(status) || (status == STATUS_DATA_OVERRUN)) {
|
||
|
||
//
|
||
// STATUS_DATA_OVERRUN means it's a newer drive with more info
|
||
// to tell us, so it's probably able to support READ_CD
|
||
//
|
||
|
||
RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
|
||
|
||
srb.CdbLength = 12;
|
||
cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
|
||
|
||
status = ClassSendSrbSynchronous(Fdo,
|
||
&srb,
|
||
NULL,
|
||
0,
|
||
FALSE);
|
||
|
||
if (NT_SUCCESS(status) ||
|
||
(status == STATUS_NO_MEDIA_IN_DEVICE) ||
|
||
(status == STATUS_NONEXISTENT_SECTOR) ||
|
||
(status == STATUS_UNRECOGNIZED_MEDIA)
|
||
) {
|
||
|
||
//
|
||
// READ_CD works
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomDetermineRawReadCapabilities: Using "
|
||
"READ_CD for FDO %p due to status %x\n",
|
||
Fdo,
|
||
status));
|
||
SET_FLAG(cddata->XAFlags, XA_USE_READ_CD);
|
||
|
||
//
|
||
// ignore errors in saving this info
|
||
//
|
||
|
||
ClassSetDeviceParameter(fdoExtension,
|
||
CDROM_SUBKEY_NAME,
|
||
CDROM_READ_CD_NAME,
|
||
1
|
||
);
|
||
|
||
|
||
break; // out of the for loop
|
||
|
||
}
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomDetermineRawReadCapabilities: Using "
|
||
"%s-byte mode switching for FDO %p due to status "
|
||
"%x returned for READ_CD\n",
|
||
((count == 0) ? "6" : "10"), Fdo, status));
|
||
|
||
if (count == 0) {
|
||
SET_FLAG(cddata->XAFlags, XA_USE_6_BYTE);
|
||
RtlCopyMemory(&cddata->Header,
|
||
buffer,
|
||
sizeof(ERROR_RECOVERY_DATA));
|
||
cddata->Header.ModeDataLength = 0;
|
||
} else {
|
||
SET_FLAG(cddata->XAFlags, XA_USE_10_BYTE);
|
||
RtlCopyMemory(&cddata->Header10,
|
||
buffer,
|
||
sizeof(ERROR_RECOVERY_DATA10));
|
||
cddata->Header10.ModeDataLength[0] = 0;
|
||
cddata->Header10.ModeDataLength[1] = 0;
|
||
}
|
||
break; // out of for loop
|
||
|
||
}
|
||
TraceLog((CdromDebugWarning,
|
||
"FDO %p failed %x byte mode sense, status %x\n",
|
||
Fdo,
|
||
((count == 0) ? 6 : 10),
|
||
status
|
||
));
|
||
|
||
//
|
||
// mode sense failed
|
||
//
|
||
|
||
} // end of for loop to try 6 and 10-byte mode sense
|
||
|
||
if (count == 2) {
|
||
|
||
//
|
||
// nothing worked. we probably cannot support digital
|
||
// audio extraction from this drive
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomDetermineRawReadCapabilities: FDO %p "
|
||
"cannot support READ_CD\n", Fdo));
|
||
CLEAR_FLAG(cddata->XAFlags, XA_PLEXTOR_CDDA);
|
||
CLEAR_FLAG(cddata->XAFlags, XA_NEC_CDDA);
|
||
SET_FLAG(cddata->XAFlags, XA_NOT_SUPPORTED);
|
||
|
||
} // end of count == 2
|
||
|
||
//
|
||
// free our resources
|
||
//
|
||
|
||
ExFreePool(buffer);
|
||
|
||
//
|
||
// set a successful status
|
||
// (in case someone later checks this)
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
//
|
||
// Register interfaces for this device.
|
||
//
|
||
|
||
{
|
||
UNICODE_STRING interfaceName;
|
||
|
||
RtlInitUnicodeString(&interfaceName, NULL);
|
||
|
||
status = IoRegisterDeviceInterface(fdoExtension->LowerPdo,
|
||
(LPGUID) &CdRomClassGuid,
|
||
NULL,
|
||
&interfaceName);
|
||
|
||
if(NT_SUCCESS(status)) {
|
||
|
||
cddata->CdromInterfaceString = interfaceName;
|
||
|
||
status = IoSetDeviceInterfaceState(
|
||
&interfaceName,
|
||
TRUE);
|
||
|
||
if(!NT_SUCCESS(status)) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromInitDevice: Unable to register cdrom "
|
||
"DCA for fdo %p [%lx]\n",
|
||
Fdo, status));
|
||
}
|
||
}
|
||
}
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
CdRomInitDeviceExit:
|
||
|
||
CdRomDeAllocateMmcResources(Fdo);
|
||
RtlZeroMemory(&(cddata->Mmc), sizeof(CDROM_MMC_EXTENSION));
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomStartDevice(
|
||
IN PDEVICE_OBJECT Fdo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine starts the timer for the cdrom
|
||
|
||
Arguments:
|
||
|
||
Fdo - a pointer to the functional device object for this device
|
||
|
||
Return Value:
|
||
|
||
status
|
||
|
||
--*/
|
||
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
||
PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
|
||
PDVD_COPY_PROTECT_KEY copyProtectKey;
|
||
PDVD_RPC_KEY rpcKey;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
ULONG bufferLen;
|
||
|
||
// CdRomCreateWellKnownName(Fdo);
|
||
|
||
//
|
||
// if we have a DVD-ROM
|
||
// if we have a rpc0 device
|
||
// fake a rpc2 device
|
||
// if device does not have a dvd region set
|
||
// select a dvd region for the user
|
||
//
|
||
|
||
cddata->DvdRpc0Device = FALSE;
|
||
|
||
//
|
||
// since StartIo() will call IoStartNextPacket() on error, allowing
|
||
// StartIo() to be non-recursive prevents stack overflow bugchecks in
|
||
// severe error cases (such as fault-injection in the verifier).
|
||
//
|
||
// the only difference is that the thread context may be different
|
||
// in StartIo() than in the caller of IoStartNextPacket().
|
||
//
|
||
|
||
IoSetStartIoAttributes(Fdo, TRUE, TRUE);
|
||
|
||
//
|
||
// check to see if we have a DVD device
|
||
//
|
||
|
||
if (CdRomGetDeviceType(Fdo) != FILE_DEVICE_DVD) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// we got a DVD drive.
|
||
// now, figure out if we have a RPC0 device
|
||
//
|
||
|
||
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;
|
||
|
||
//
|
||
// Build a request for READ_KEY
|
||
//
|
||
ClassSendDeviceIoControlSynchronous(
|
||
IOCTL_DVD_READ_KEY,
|
||
Fdo,
|
||
copyProtectKey,
|
||
DVD_RPC_KEY_LENGTH,
|
||
DVD_RPC_KEY_LENGTH,
|
||
FALSE,
|
||
&ioStatus
|
||
);
|
||
|
||
if (!NT_SUCCESS(ioStatus.Status)) {
|
||
|
||
//
|
||
// we have a rpc0 device
|
||
//
|
||
// NOTE: THIS MODIFIES THE BEHAVIOR OF THE IOCTL
|
||
//
|
||
|
||
cddata->DvdRpc0Device = TRUE;
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromStartDevice (%p): RPC Phase 1 drive detected\n",
|
||
Fdo));
|
||
|
||
//
|
||
// note: we could force this chosen now, but it's better to reduce
|
||
// the number of code paths that could be taken. always delay to
|
||
// increase the percentage code coverage.
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromStartDevice (%p): Delay DVD Region Selection\n",
|
||
Fdo));
|
||
|
||
cddata->Rpc0SystemRegion = 0xff;
|
||
cddata->Rpc0SystemRegionResetCount = DVD_MAX_REGION_RESET_COUNT;
|
||
cddata->PickDvdRegion = 1;
|
||
cddata->Rpc0RetryRegistryCallback = 1;
|
||
ExFreePool(copyProtectKey);
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData;
|
||
|
||
//
|
||
// TypeCode of zero means that no region has been set.
|
||
//
|
||
|
||
if (rpcKey->TypeCode == 0) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromStartDevice (%p): must choose DVD region\n",
|
||
Fdo));
|
||
cddata->PickDvdRegion = 1;
|
||
CdRomPickDvdRegion(Fdo);
|
||
}
|
||
}
|
||
|
||
ExFreePool (copyProtectKey);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomStopDevice(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN UCHAR Type
|
||
)
|
||
{
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdRomStartIo(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
||
|
||
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
||
PIO_STACK_LOCATION irpStack;
|
||
|
||
PIRP irp2 = NULL;
|
||
|
||
ULONG transferPages;
|
||
ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
|
||
LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
|
||
PCDROM_DATA cdData;
|
||
PSCSI_REQUEST_BLOCK srb = NULL;
|
||
PCDB cdb;
|
||
PUCHAR senseBuffer = NULL;
|
||
PVOID dataBuffer;
|
||
NTSTATUS status;
|
||
BOOLEAN use6Byte;
|
||
KIRQL oldIrql;
|
||
|
||
//
|
||
// Mark IRP with status pending.
|
||
//
|
||
|
||
IoMarkIrpPending(Irp);
|
||
|
||
cdData = (PCDROM_DATA)(fdoExtension->CommonExtension.DriverData);
|
||
use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE);
|
||
|
||
//
|
||
// if this test is true, then we will exit the routine within this
|
||
// code block, queueing the irp for later completion.
|
||
//
|
||
|
||
if ((cdData->Mmc.IsMmc) &&
|
||
(cdData->Mmc.UpdateState != CdromMmcUpdateComplete)
|
||
) {
|
||
|
||
ULONG queueDepth;
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdRomStartIo: [%p] Device needs to update capabilities\n",
|
||
Irp));
|
||
ASSERT(cdData->Mmc.IsMmc);
|
||
ASSERT(cdData->Mmc.CapabilitiesIrp != NULL);
|
||
ASSERT(cdData->Mmc.CapabilitiesIrp != Irp);
|
||
|
||
//
|
||
// NOTE - REF #0002
|
||
//
|
||
// the state was either UpdateRequired (which means we will
|
||
// have to start the work item) or UpdateStarted (which means
|
||
// we have already started the work item at least once -- may
|
||
// transparently change to UpdateComplete).
|
||
//
|
||
// if it's update required, we just queue it, change to UpdateStarted,
|
||
// start the workitem, and start the next packet.
|
||
//
|
||
// else, we must queue the item and check the queue depth. if the
|
||
// queue depth is equal to 1, that means the worker item from the
|
||
// previous attempt has already de-queued the items, so we should
|
||
// call this routine again (retry) as an optimization rather than
|
||
// re-add it this irp to the queue. since this is tail recursion,
|
||
// it won't take much/any stack to do this.
|
||
//
|
||
// NOTE: This presumes the following items are true:
|
||
//
|
||
// we only add to the list from CdRomStartIo(), which is serialized.
|
||
// we only set to UpdateStarted from CdRomStartIo(), and only if
|
||
// the state was UpdateRequired.
|
||
// we only set to UpdateRequired from CdRomMmcErrorHandler(), and
|
||
// only if the state was UpdateComplete.
|
||
// we only set to UpdateComplete from the workitem, and assert the
|
||
// state was UpdateStarted.
|
||
// we flush the entire queue in one atomic operation in the workitem,
|
||
// except in the special case described above when we dequeue
|
||
// the request immediately.
|
||
//
|
||
// order of operations is vitally important: queue, then test the depth
|
||
// this will prevent lost irps.
|
||
//
|
||
|
||
KeAcquireSpinLock(&cdData->Mmc.DelayedIrpsLock, &oldIrql);
|
||
InsertTailList(&cdData->Mmc.DelayedIrpsList, &Irp->Tail.Overlay.ListEntry);
|
||
queueDepth = ++cdData->Mmc.NumDelayedIrps;
|
||
KeReleaseSpinLock(&cdData->Mmc.DelayedIrpsLock, oldIrql);
|
||
|
||
if (queueDepth == 1) {
|
||
|
||
if (cdData->Mmc.UpdateState == CdromMmcUpdateRequired) {
|
||
LONG oldState;
|
||
|
||
//
|
||
// should free any old partition list info that
|
||
// we've previously saved away and then start the WorkItem
|
||
//
|
||
|
||
oldState = InterlockedExchange(&cdData->Mmc.UpdateState,
|
||
CdromMmcUpdateStarted);
|
||
ASSERT(oldState == CdromMmcUpdateRequired);
|
||
|
||
IoQueueWorkItem(cdData->Mmc.CapabilitiesWorkItem,
|
||
CdRomUpdateMmcDriveCapabilities,
|
||
DelayedWorkQueue,
|
||
NULL);
|
||
|
||
} else {
|
||
|
||
//
|
||
// they *just* finished updating, so we should flush the list
|
||
// back onto the StartIo queue and start the next packet.
|
||
//
|
||
|
||
CdRompFlushDelayedList(Fdo, &(cdData->Mmc), STATUS_SUCCESS, FALSE);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// start the next packet so we don't deadlock....
|
||
//
|
||
|
||
IoStartNextPacket(Fdo, FALSE);
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// If the flag is set in the device object
|
||
// force a verify for READ, WRITE and RAW_READ requests
|
||
// Note that ioctls are passed through....
|
||
//
|
||
|
||
if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME) &&
|
||
IS_READ_WRITE_REQUEST(currentIrpStack)) {
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomStartIo: [%p] Volume needs verified\n", Irp));
|
||
|
||
if (!(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {
|
||
|
||
if (Irp->Tail.Overlay.Thread) {
|
||
IoSetHardErrorOrVerifyDevice(Irp, Fdo);
|
||
}
|
||
|
||
Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomStartIo: [%p] Calling UpdateCapcity - "
|
||
"ioctl event = %p\n",
|
||
Irp,
|
||
nextIrpStack->Parameters.Others.Argument1
|
||
));
|
||
|
||
//
|
||
// our device control dispatch routine stores an event in the next
|
||
// stack location to signal when startio has completed. We need to
|
||
// pass this in so that the update capacity completion routine can
|
||
// set it rather than completing the Irp.
|
||
//
|
||
|
||
status = CdRomUpdateCapacity(fdoExtension,
|
||
Irp,
|
||
nextIrpStack->Parameters.Others.Argument1
|
||
);
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomStartIo: [%p] UpdateCapacity returned %lx\n",
|
||
Irp, status));
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// fail writes if they are not allowed...
|
||
//
|
||
|
||
if ((currentIrpStack->MajorFunction == IRP_MJ_WRITE) &&
|
||
!(cdData->Mmc.WriteAllowed)) {
|
||
|
||
TraceLog((CdromDebugError,
|
||
"CdRomStartIo: [%p] Device %p failing write request\n",
|
||
Irp, Fdo));
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
if (currentIrpStack->MajorFunction == IRP_MJ_READ ||
|
||
currentIrpStack->MajorFunction == IRP_MJ_WRITE ) {
|
||
|
||
ULONG maximumTransferLength = fdoExtension->AdapterDescriptor->MaximumTransferLength;
|
||
|
||
//
|
||
// Add partition byte offset to make starting byte relative to
|
||
// beginning of disk.
|
||
//
|
||
|
||
currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
|
||
(fdoExtension->CommonExtension.StartingOffset.QuadPart);
|
||
|
||
//
|
||
// Calculate number of pages in this transfer.
|
||
//
|
||
|
||
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
|
||
currentIrpStack->Parameters.Read.Length);
|
||
|
||
//
|
||
// Check if request length is greater than the maximum number of
|
||
// bytes that the hardware can transfer.
|
||
//
|
||
|
||
if (cdData->RawAccess) {
|
||
|
||
//
|
||
// a writable device must be MMC compliant, which supports
|
||
// READ_CD commands.
|
||
//
|
||
|
||
ASSERT(currentIrpStack->MajorFunction != IRP_MJ_WRITE);
|
||
|
||
ASSERT(!TEST_FLAG(cdData->XAFlags, XA_USE_READ_CD));
|
||
|
||
//
|
||
// Fire off a mode select to switch back to cooked sectors.
|
||
//
|
||
|
||
irp2 = IoAllocateIrp((CCHAR)(Fdo->StackSize+1), FALSE);
|
||
|
||
if (!irp2) {
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
srb = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(SCSI_REQUEST_BLOCK),
|
||
CDROM_TAG_SRB);
|
||
if (!srb) {
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
|
||
cdb = (PCDB)srb->Cdb;
|
||
|
||
//
|
||
// Allocate sense buffer.
|
||
//
|
||
|
||
senseBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
SENSE_BUFFER_SIZE,
|
||
CDROM_TAG_SENSE_INFO);
|
||
|
||
if (!senseBuffer) {
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Set up the irp.
|
||
//
|
||
|
||
IoSetNextIrpStackLocation(irp2);
|
||
irp2->IoStatus.Status = STATUS_SUCCESS;
|
||
irp2->IoStatus.Information = 0;
|
||
irp2->Flags = 0;
|
||
irp2->UserBuffer = NULL;
|
||
|
||
//
|
||
// Save the device object and irp in a private stack location.
|
||
//
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation(irp2);
|
||
irpStack->DeviceObject = Fdo;
|
||
irpStack->Parameters.Others.Argument2 = (PVOID) Irp;
|
||
|
||
//
|
||
// The retry count will be in the real Irp, as the retry logic will
|
||
// recreate our private irp.
|
||
//
|
||
|
||
if (!(nextIrpStack->Parameters.Others.Argument1)) {
|
||
|
||
//
|
||
// Only jam this in if it doesn't exist. The completion routines can
|
||
// call StartIo directly in the case of retries and resetting it will
|
||
// cause infinite loops.
|
||
//
|
||
|
||
nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
|
||
}
|
||
|
||
//
|
||
// Construct the IRP stack for the lower level driver.
|
||
//
|
||
|
||
irpStack = IoGetNextIrpStackLocation(irp2);
|
||
irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
|
||
irpStack->Parameters.Scsi.Srb = srb;
|
||
|
||
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
srb->NextSrb = 0;
|
||
srb->OriginalRequest = irp2;
|
||
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
||
srb->SenseInfoBuffer = senseBuffer;
|
||
|
||
transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10);
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
transferByteCount,
|
||
CDROM_TAG_RAW);
|
||
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
transferByteCount,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
|
||
srb->DataBuffer = dataBuffer;
|
||
|
||
//
|
||
// Set the new block size in the descriptor.
|
||
//
|
||
|
||
if (use6Byte) {
|
||
cdData->BlockDescriptor.BlockLength[0] = (UCHAR)(COOKED_SECTOR_SIZE >> 16) & 0xFF;
|
||
cdData->BlockDescriptor.BlockLength[1] = (UCHAR)(COOKED_SECTOR_SIZE >> 8) & 0xFF;
|
||
cdData->BlockDescriptor.BlockLength[2] = (UCHAR)(COOKED_SECTOR_SIZE & 0xFF);
|
||
} else {
|
||
cdData->BlockDescriptor10.BlockLength[0] = (UCHAR)(COOKED_SECTOR_SIZE >> 16) & 0xFF;
|
||
cdData->BlockDescriptor10.BlockLength[1] = (UCHAR)(COOKED_SECTOR_SIZE >> 8) & 0xFF;
|
||
cdData->BlockDescriptor10.BlockLength[2] = (UCHAR)(COOKED_SECTOR_SIZE & 0xFF);
|
||
}
|
||
|
||
//
|
||
// Move error page into dataBuffer.
|
||
//
|
||
|
||
RtlCopyMemory(srb->DataBuffer, &cdData->Header, transferByteCount);
|
||
|
||
//
|
||
// Build and send a mode select to switch into raw mode.
|
||
//
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue * 2;
|
||
|
||
if (use6Byte) {
|
||
srb->CdbLength = 6;
|
||
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
|
||
cdb->MODE_SELECT.PFBit = 1;
|
||
cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount;
|
||
} else {
|
||
srb->CdbLength = 10;
|
||
cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
|
||
cdb->MODE_SELECT10.PFBit = 1;
|
||
cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8);
|
||
cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF);
|
||
}
|
||
|
||
//
|
||
// Update completion routine.
|
||
//
|
||
|
||
IoSetCompletionRoutine(irp2,
|
||
CdRomSwitchModeCompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Request needs to be split. Completion of each portion of the
|
||
// request will fire off the next portion. The final request will
|
||
// signal Io to send a new request.
|
||
//
|
||
|
||
transferPages =
|
||
fdoExtension->AdapterDescriptor->MaximumPhysicalPages - 1;
|
||
|
||
if(maximumTransferLength > (transferPages << PAGE_SHIFT)) {
|
||
maximumTransferLength = transferPages << PAGE_SHIFT;
|
||
}
|
||
|
||
//
|
||
// Check that the maximum transfer size is not zero
|
||
//
|
||
|
||
if(maximumTransferLength == 0) {
|
||
maximumTransferLength = PAGE_SIZE;
|
||
}
|
||
|
||
ClassSplitRequest(Fdo, Irp, maximumTransferLength);
|
||
return;
|
||
|
||
} else if (currentIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
|
||
|
||
//
|
||
// Allocate an irp, srb and associated structures.
|
||
//
|
||
|
||
irp2 = IoAllocateIrp((CCHAR)(Fdo->StackSize+1),
|
||
FALSE);
|
||
|
||
if (!irp2) {
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
srb = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(SCSI_REQUEST_BLOCK),
|
||
CDROM_TAG_SRB);
|
||
if (!srb) {
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
|
||
cdb = (PCDB)srb->Cdb;
|
||
|
||
//
|
||
// Allocate sense buffer.
|
||
//
|
||
|
||
senseBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
SENSE_BUFFER_SIZE,
|
||
CDROM_TAG_SENSE_INFO);
|
||
|
||
if (!senseBuffer) {
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
RtlZeroMemory(senseBuffer, SENSE_BUFFER_SIZE);
|
||
|
||
//
|
||
// Set up the irp.
|
||
//
|
||
|
||
IoSetNextIrpStackLocation(irp2);
|
||
irp2->IoStatus.Status = STATUS_SUCCESS;
|
||
irp2->IoStatus.Information = 0;
|
||
irp2->Flags = 0;
|
||
irp2->UserBuffer = NULL;
|
||
|
||
//
|
||
// Save the device object and irp in a private stack location.
|
||
//
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation(irp2);
|
||
irpStack->DeviceObject = Fdo;
|
||
irpStack->Parameters.Others.Argument2 = (PVOID) Irp;
|
||
|
||
//
|
||
// The retry count will be in the real Irp, as the retry logic will
|
||
// recreate our private irp.
|
||
//
|
||
|
||
if (!(nextIrpStack->Parameters.Others.Argument1)) {
|
||
|
||
//
|
||
// Only jam this in if it doesn't exist. The completion routines can
|
||
// call StartIo directly in the case of retries and resetting it will
|
||
// cause infinite loops.
|
||
//
|
||
|
||
nextIrpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
|
||
}
|
||
|
||
//
|
||
// keep track of the new irp as Argument3
|
||
//
|
||
|
||
nextIrpStack->Parameters.Others.Argument3 = irp2;
|
||
|
||
|
||
//
|
||
// Construct the IRP stack for the lower level driver.
|
||
//
|
||
|
||
irpStack = IoGetNextIrpStackLocation(irp2);
|
||
irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
|
||
irpStack->Parameters.Scsi.Srb = srb;
|
||
|
||
IoSetCompletionRoutine(irp2,
|
||
CdRomDeviceControlCompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
//
|
||
// Setup those fields that are generic to all requests.
|
||
//
|
||
|
||
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
srb->NextSrb = 0;
|
||
srb->OriginalRequest = irp2;
|
||
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
||
srb->SenseInfoBuffer = senseBuffer;
|
||
|
||
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
||
|
||
|
||
case IOCTL_CDROM_RAW_READ: {
|
||
|
||
//
|
||
// Determine whether the drive is currently in raw or cooked mode,
|
||
// and which command to use to read the data.
|
||
//
|
||
|
||
if (!TEST_FLAG(cdData->XAFlags, XA_USE_READ_CD)) {
|
||
|
||
PRAW_READ_INFO rawReadInfo =
|
||
(PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
|
||
ULONG maximumTransferLength;
|
||
ULONG transferPages;
|
||
|
||
if (cdData->RawAccess) {
|
||
|
||
ULONG startingSector;
|
||
UCHAR min, sec, frame;
|
||
|
||
//
|
||
// Free the recently allocated irp, as we don't need it.
|
||
//
|
||
|
||
IoFreeIrp(irp2);
|
||
|
||
cdb = (PCDB)srb->Cdb;
|
||
RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
|
||
|
||
//
|
||
// Calculate starting offset.
|
||
//
|
||
|
||
startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> fdoExtension->SectorShift);
|
||
transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
|
||
maximumTransferLength = fdoExtension->AdapterDescriptor->MaximumTransferLength;
|
||
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(Irp->MdlAddress),
|
||
transferByteCount);
|
||
|
||
//
|
||
// Determine if request is within limits imposed by miniport.
|
||
//
|
||
if (transferByteCount > maximumTransferLength ||
|
||
transferPages > fdoExtension->AdapterDescriptor->MaximumPhysicalPages) {
|
||
|
||
//
|
||
// The claim is that this won't happen, and is backed up by
|
||
// ActiveMovie usage, which does unbuffered XA reads of 0x18000, yet
|
||
// we get only 4 sector requests.
|
||
//
|
||
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
srb->OriginalRequest = Irp;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->CdbLength = 10;
|
||
srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
||
|
||
if (rawReadInfo->TrackMode == CDDA) {
|
||
if (TEST_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA)) {
|
||
|
||
srb->CdbLength = 12;
|
||
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF);
|
||
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8);
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0;
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0;
|
||
|
||
cdb->PLXTR_READ_CDDA.SubCode = 0;
|
||
cdb->PLXTR_READ_CDDA.OperationCode = 0xD8;
|
||
|
||
} else if (TEST_FLAG(cdData->XAFlags, XA_NEC_CDDA)) {
|
||
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF);
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF);
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF);
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF);
|
||
|
||
cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
|
||
cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8);
|
||
|
||
cdb->NEC_READ_CDDA.OperationCode = 0xD4;
|
||
}
|
||
} else {
|
||
|
||
cdb->CDB10.TransferBlocksMsb = (UCHAR) (rawReadInfo->SectorCount >> 8);
|
||
cdb->CDB10.TransferBlocksLsb = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
|
||
|
||
cdb->CDB10.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF);
|
||
cdb->CDB10.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF);
|
||
cdb->CDB10.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF);
|
||
cdb->CDB10.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF);
|
||
|
||
cdb->CDB10.OperationCode = SCSIOP_READ;
|
||
}
|
||
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
|
||
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
||
nextIrpStack->Parameters.Scsi.Srb = srb;
|
||
|
||
// HACKHACK - REF #0001
|
||
|
||
//
|
||
// Set up IoCompletion routine address.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
CdRomXACompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
|
||
return;
|
||
|
||
} else {
|
||
|
||
transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10);
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
transferByteCount,
|
||
CDROM_TAG_RAW );
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
transferByteCount,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
|
||
srb->DataBuffer = dataBuffer;
|
||
|
||
//
|
||
// Set the new block size in the descriptor.
|
||
// This will set the block read size to RAW_SECTOR_SIZE
|
||
// TODO: Set density code, based on operation
|
||
//
|
||
|
||
if (use6Byte) {
|
||
cdData->BlockDescriptor.BlockLength[0] = (UCHAR)(RAW_SECTOR_SIZE >> 16) & 0xFF;
|
||
cdData->BlockDescriptor.BlockLength[1] = (UCHAR)(RAW_SECTOR_SIZE >> 8) & 0xFF;
|
||
cdData->BlockDescriptor.BlockLength[2] = (UCHAR)(RAW_SECTOR_SIZE & 0xFF);
|
||
cdData->BlockDescriptor.DensityCode = 0;
|
||
} else {
|
||
cdData->BlockDescriptor10.BlockLength[0] = (UCHAR)(RAW_SECTOR_SIZE >> 16) & 0xFF;
|
||
cdData->BlockDescriptor10.BlockLength[1] = (UCHAR)(RAW_SECTOR_SIZE >> 8) & 0xFF;
|
||
cdData->BlockDescriptor10.BlockLength[2] = (UCHAR)(RAW_SECTOR_SIZE & 0xFF);
|
||
cdData->BlockDescriptor10.DensityCode = 0;
|
||
}
|
||
|
||
//
|
||
// Move error page into dataBuffer.
|
||
//
|
||
|
||
RtlCopyMemory(srb->DataBuffer, &cdData->Header, transferByteCount);
|
||
|
||
|
||
//
|
||
// Build and send a mode select to switch into raw mode.
|
||
//
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue * 2;
|
||
|
||
if (use6Byte) {
|
||
srb->CdbLength = 6;
|
||
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
|
||
cdb->MODE_SELECT.PFBit = 1;
|
||
cdb->MODE_SELECT.ParameterListLength = (UCHAR)transferByteCount;
|
||
} else {
|
||
|
||
srb->CdbLength = 10;
|
||
cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
|
||
cdb->MODE_SELECT10.PFBit = 1;
|
||
cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(transferByteCount >> 8);
|
||
cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(transferByteCount & 0xFF);
|
||
}
|
||
|
||
//
|
||
// Update completion routine.
|
||
//
|
||
|
||
IoSetCompletionRoutine(irp2,
|
||
CdRomSwitchModeCompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
PRAW_READ_INFO rawReadInfo =
|
||
(PRAW_READ_INFO)currentIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
|
||
ULONG startingSector;
|
||
|
||
//
|
||
// Free the recently allocated irp, as we don't need it.
|
||
//
|
||
|
||
IoFreeIrp(irp2);
|
||
|
||
cdb = (PCDB)srb->Cdb;
|
||
RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
|
||
|
||
|
||
//
|
||
// Calculate starting offset.
|
||
//
|
||
|
||
startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> fdoExtension->SectorShift);
|
||
transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
|
||
|
||
srb->OriginalRequest = Irp;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
||
srb->CdbLength = 12;
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
|
||
//
|
||
// Fill in CDB fields.
|
||
//
|
||
|
||
cdb = (PCDB)srb->Cdb;
|
||
|
||
|
||
cdb->READ_CD.TransferBlocks[2] = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
|
||
cdb->READ_CD.TransferBlocks[1] = (UCHAR) (rawReadInfo->SectorCount >> 8 );
|
||
cdb->READ_CD.TransferBlocks[0] = (UCHAR) (rawReadInfo->SectorCount >> 16);
|
||
|
||
|
||
cdb->READ_CD.StartingLBA[3] = (UCHAR) (startingSector & 0xFF);
|
||
cdb->READ_CD.StartingLBA[2] = (UCHAR) ((startingSector >> 8));
|
||
cdb->READ_CD.StartingLBA[1] = (UCHAR) ((startingSector >> 16));
|
||
cdb->READ_CD.StartingLBA[0] = (UCHAR) ((startingSector >> 24));
|
||
|
||
//
|
||
// Setup cdb depending upon the sector type we want.
|
||
//
|
||
|
||
switch (rawReadInfo->TrackMode) {
|
||
case CDDA:
|
||
|
||
cdb->READ_CD.ExpectedSectorType = CD_DA_SECTOR;
|
||
cdb->READ_CD.IncludeUserData = 1;
|
||
cdb->READ_CD.HeaderCode = 3;
|
||
cdb->READ_CD.IncludeSyncData = 1;
|
||
break;
|
||
|
||
case YellowMode2:
|
||
|
||
cdb->READ_CD.ExpectedSectorType = YELLOW_MODE2_SECTOR;
|
||
cdb->READ_CD.IncludeUserData = 1;
|
||
cdb->READ_CD.HeaderCode = 1;
|
||
cdb->READ_CD.IncludeSyncData = 1;
|
||
break;
|
||
|
||
case XAForm2:
|
||
|
||
cdb->READ_CD.ExpectedSectorType = FORM2_MODE2_SECTOR;
|
||
cdb->READ_CD.IncludeUserData = 1;
|
||
cdb->READ_CD.HeaderCode = 3;
|
||
cdb->READ_CD.IncludeSyncData = 1;
|
||
break;
|
||
|
||
default:
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
|
||
|
||
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
||
nextIrpStack->Parameters.Scsi.Srb = srb;
|
||
|
||
// HACKHACK - REF #0001
|
||
|
||
//
|
||
// Set up IoCompletion routine address.
|
||
//
|
||
|
||
IoSetCompletionRoutine(Irp,
|
||
CdRomXACompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// the _EX version does the same thing on the front end
|
||
//
|
||
|
||
case IOCTL_DISK_GET_LENGTH_INFO:
|
||
case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
|
||
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
||
case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX:
|
||
case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
|
||
|
||
//
|
||
// Issue ReadCapacity to update device extension
|
||
// with information for current media.
|
||
//
|
||
|
||
TraceLog((CdromDebugError,
|
||
"CdRomStartIo: Get drive geometry/length "
|
||
"info (%p)\n", Irp));
|
||
|
||
//
|
||
// setup remaining srb and cdb parameters.
|
||
//
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
sizeof(READ_CAPACITY_DATA),
|
||
CDROM_TAG_READ_CAP);
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
sizeof(READ_CAPACITY_DATA),
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
|
||
srb->DataBuffer = dataBuffer;
|
||
cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_CDROM_GET_CONFIGURATION: {
|
||
|
||
PGET_CONFIGURATION_IOCTL_INPUT inputBuffer;
|
||
|
||
TraceLog((CdromDebugError,
|
||
"CdRomStartIo: Get configuration (%p)\n", Irp));
|
||
|
||
if (!cdData->Mmc.IsMmc) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
transferByteCount = currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
transferByteCount,
|
||
CDROM_TAG_GET_CONFIG);
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
transferByteCount,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(dataBuffer);
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
|
||
//
|
||
// setup remaining srb and cdb parameters
|
||
//
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->DataBuffer = dataBuffer;
|
||
|
||
cdb->GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION;
|
||
cdb->GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(transferByteCount >> 8);
|
||
cdb->GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff);
|
||
|
||
inputBuffer = (PGET_CONFIGURATION_IOCTL_INPUT)Irp->AssociatedIrp.SystemBuffer;
|
||
cdb->GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(inputBuffer->Feature >> 8);
|
||
cdb->GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(inputBuffer->Feature & 0xff);
|
||
cdb->GET_CONFIGURATION.RequestType = (UCHAR)(inputBuffer->RequestType);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_DISK_VERIFY: {
|
||
|
||
PVERIFY_INFORMATION verifyInfo = Irp->AssociatedIrp.SystemBuffer;
|
||
LARGE_INTEGER byteOffset;
|
||
ULONG sectorOffset;
|
||
USHORT sectorCount;
|
||
|
||
if (!cdData->Mmc.WriteAllowed) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED;
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
//
|
||
// Verify sectors
|
||
//
|
||
|
||
srb->CdbLength = 10;
|
||
|
||
cdb->CDB10.OperationCode = SCSIOP_VERIFY;
|
||
|
||
//
|
||
// Add disk offset to starting sector.
|
||
//
|
||
|
||
byteOffset.QuadPart = commonExtension->StartingOffset.QuadPart +
|
||
verifyInfo->StartingOffset.QuadPart;
|
||
|
||
//
|
||
// Convert byte offset to sector offset.
|
||
//
|
||
|
||
sectorOffset = (ULONG)(byteOffset.QuadPart >> fdoExtension->SectorShift);
|
||
|
||
//
|
||
// Convert ULONG byte count to USHORT sector count.
|
||
//
|
||
|
||
sectorCount = (USHORT)(verifyInfo->Length >> fdoExtension->SectorShift);
|
||
|
||
//
|
||
// Move little endian values into CDB in big endian format.
|
||
//
|
||
|
||
cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)§orOffset)->Byte3;
|
||
cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)§orOffset)->Byte2;
|
||
cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)§orOffset)->Byte1;
|
||
cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)§orOffset)->Byte0;
|
||
|
||
cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)§orCount)->Byte1;
|
||
cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)§orCount)->Byte0;
|
||
|
||
//
|
||
// The verify command is used by the NT FORMAT utility and
|
||
// requests are sent down for 5% of the volume size. The
|
||
// request timeout value is calculated based on the number of
|
||
// sectors verified.
|
||
//
|
||
|
||
srb->TimeOutValue = ((sectorCount + 0x7F) >> 7) *
|
||
fdoExtension->TimeOutValue;
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_STORAGE_CHECK_VERIFY:
|
||
case IOCTL_DISK_CHECK_VERIFY:
|
||
case IOCTL_CDROM_CHECK_VERIFY: {
|
||
|
||
//
|
||
// Since a test unit ready is about to be performed, reset the
|
||
// timer value to decrease the opportunities for it to race with
|
||
// this code.
|
||
//
|
||
|
||
ClassResetMediaChangeTimer(fdoExtension);
|
||
|
||
//
|
||
// Set up the SRB/CDB
|
||
//
|
||
|
||
srb->CdbLength = 6;
|
||
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue * 2;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
||
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomStartIo: [%p] Sending CHECK_VERIFY irp %p\n",
|
||
Irp, irp2));
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_DVD_READ_STRUCTURE: {
|
||
|
||
CdRomDeviceControlDvdReadStructure(Fdo, Irp, irp2, srb);
|
||
return;
|
||
|
||
}
|
||
|
||
case IOCTL_DVD_END_SESSION: {
|
||
CdRomDeviceControlDvdEndSession(Fdo, Irp, irp2, srb);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_DVD_START_SESSION:
|
||
case IOCTL_DVD_READ_KEY: {
|
||
|
||
CdRomDeviceControlDvdStartSessionReadKey(Fdo, Irp, irp2, srb);
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
case IOCTL_DVD_SEND_KEY:
|
||
case IOCTL_DVD_SEND_KEY2: {
|
||
|
||
CdRomDeviceControlDvdSendKey (Fdo, Irp, irp2, srb);
|
||
return;
|
||
|
||
|
||
}
|
||
|
||
case IOCTL_CDROM_READ_TOC_EX: {
|
||
|
||
PCDROM_READ_TOC_EX inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
transferByteCount = currentIrpStack->Parameters.Read.Length;
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
transferByteCount,
|
||
CDROM_TAG_TOC);
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
transferByteCount,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// setup the request per user request
|
||
// do validity checking in devctl dispatch, not here
|
||
//
|
||
|
||
cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
|
||
cdb->READ_TOC.Msf = inputBuffer->Msf;
|
||
cdb->READ_TOC.Format2 = inputBuffer->Format;
|
||
cdb->READ_TOC.StartingTrack = inputBuffer->SessionTrack;
|
||
cdb->READ_TOC.AllocationLength[0] = (UCHAR)(transferByteCount >> 8);
|
||
cdb->READ_TOC.AllocationLength[1] = (UCHAR)(transferByteCount & 0xff);
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
|
||
//
|
||
// do the standard stuff....
|
||
//
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->DataBuffer = dataBuffer;
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_CDROM_GET_LAST_SESSION:
|
||
case IOCTL_CDROM_READ_TOC: {
|
||
|
||
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
|
||
IOCTL_CDROM_GET_LAST_SESSION) {
|
||
|
||
//
|
||
// Set format to return first and last session numbers.
|
||
//
|
||
|
||
cdb->READ_TOC.Format = CDROM_READ_TOC_EX_FORMAT_SESSION;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Use MSF addressing
|
||
//
|
||
|
||
cdb->READ_TOC.Msf = 1;
|
||
|
||
}
|
||
|
||
|
||
transferByteCount =
|
||
currentIrpStack->Parameters.Read.Length >
|
||
sizeof(CDROM_TOC) ? sizeof(CDROM_TOC):
|
||
currentIrpStack->Parameters.Read.Length;
|
||
|
||
//
|
||
// Set size of TOC structure.
|
||
//
|
||
|
||
cdb->READ_TOC.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
|
||
cdb->READ_TOC.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF);
|
||
|
||
//
|
||
// setup remaining srb and cdb parameters.
|
||
//
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
transferByteCount,
|
||
CDROM_TAG_TOC);
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
transferByteCount,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
|
||
srb->DataBuffer = dataBuffer;
|
||
cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
|
||
}
|
||
|
||
case IOCTL_CDROM_PLAY_AUDIO_MSF: {
|
||
|
||
PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Set up the SRB/CDB
|
||
//
|
||
|
||
srb->CdbLength = 10;
|
||
cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;
|
||
|
||
cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
|
||
cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
|
||
cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;
|
||
|
||
cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
|
||
cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
|
||
cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;
|
||
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
|
||
}
|
||
|
||
case IOCTL_CDROM_READ_Q_CHANNEL: {
|
||
|
||
PSUB_Q_CHANNEL_DATA userChannelData =
|
||
Irp->AssociatedIrp.SystemBuffer;
|
||
PCDROM_SUB_Q_DATA_FORMAT inputBuffer =
|
||
Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Allocate buffer for subq channel information.
|
||
//
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
sizeof(SUB_Q_CHANNEL_DATA),
|
||
CDROM_TAG_SUB_Q);
|
||
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
sizeof(SUB_Q_CHANNEL_DATA),
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
|
||
srb->DataBuffer = dataBuffer;
|
||
|
||
//
|
||
// Always logical unit 0, but only use MSF addressing
|
||
// for IOCTL_CDROM_CURRENT_POSITION
|
||
//
|
||
|
||
if (inputBuffer->Format==IOCTL_CDROM_CURRENT_POSITION)
|
||
cdb->SUBCHANNEL.Msf = CDB_USE_MSF;
|
||
|
||
//
|
||
// Return subchannel data
|
||
//
|
||
|
||
cdb->SUBCHANNEL.SubQ = CDB_SUBCHANNEL_BLOCK;
|
||
|
||
//
|
||
// Specify format of informatin to return
|
||
//
|
||
|
||
cdb->SUBCHANNEL.Format = inputBuffer->Format;
|
||
|
||
//
|
||
// Specify which track to access (only used by Track ISRC reads)
|
||
//
|
||
|
||
if (inputBuffer->Format==IOCTL_CDROM_TRACK_ISRC) {
|
||
cdb->SUBCHANNEL.TrackNumber = inputBuffer->Track;
|
||
}
|
||
|
||
//
|
||
// Set size of channel data -- however, this is dependent on
|
||
// what information we are requesting (which Format)
|
||
//
|
||
|
||
switch( inputBuffer->Format ) {
|
||
|
||
case IOCTL_CDROM_CURRENT_POSITION:
|
||
transferByteCount = sizeof(SUB_Q_CURRENT_POSITION);
|
||
break;
|
||
|
||
case IOCTL_CDROM_MEDIA_CATALOG:
|
||
transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER);
|
||
break;
|
||
|
||
case IOCTL_CDROM_TRACK_ISRC:
|
||
transferByteCount = sizeof(SUB_Q_TRACK_ISRC);
|
||
break;
|
||
}
|
||
|
||
cdb->SUBCHANNEL.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
|
||
cdb->SUBCHANNEL.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF);
|
||
cdb->SUBCHANNEL.OperationCode = SCSIOP_READ_SUB_CHANNEL;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
|
||
}
|
||
|
||
case IOCTL_CDROM_PAUSE_AUDIO: {
|
||
|
||
cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
|
||
cdb->PAUSE_RESUME.Action = CDB_AUDIO_PAUSE;
|
||
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_CDROM_RESUME_AUDIO: {
|
||
|
||
cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
|
||
cdb->PAUSE_RESUME.Action = CDB_AUDIO_RESUME;
|
||
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_CDROM_SEEK_AUDIO_MSF: {
|
||
|
||
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
ULONG logicalBlockAddress;
|
||
|
||
logicalBlockAddress = MSF_TO_LBA(inputBuffer->M, inputBuffer->S, inputBuffer->F);
|
||
|
||
cdb->SEEK.OperationCode = SCSIOP_SEEK;
|
||
cdb->SEEK.LogicalBlockAddress[0] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3;
|
||
cdb->SEEK.LogicalBlockAddress[1] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2;
|
||
cdb->SEEK.LogicalBlockAddress[2] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1;
|
||
cdb->SEEK.LogicalBlockAddress[3] = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0;
|
||
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
|
||
}
|
||
|
||
case IOCTL_CDROM_STOP_AUDIO: {
|
||
|
||
cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
||
cdb->START_STOP.Immediate = 1;
|
||
cdb->START_STOP.Start = 0;
|
||
cdb->START_STOP.LoadEject = 0;
|
||
|
||
srb->CdbLength = 6;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_CDROM_GET_CONTROL: {
|
||
|
||
PAUDIO_OUTPUT audioOutput;
|
||
PCDROM_AUDIO_CONTROL audioControl = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
//
|
||
// Allocate buffer for volume control information.
|
||
//
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
MODE_DATA_SIZE,
|
||
CDROM_TAG_VOLUME);
|
||
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
MODE_DATA_SIZE,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
srb->DataBuffer = dataBuffer;
|
||
|
||
RtlZeroMemory(dataBuffer, MODE_DATA_SIZE);
|
||
|
||
//
|
||
// Setup for either 6 or 10 byte CDBs.
|
||
//
|
||
|
||
if (use6Byte) {
|
||
|
||
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE;
|
||
cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;
|
||
|
||
//
|
||
// Disable block descriptors.
|
||
//
|
||
|
||
cdb->MODE_SENSE.Dbd = TRUE;
|
||
|
||
srb->CdbLength = 6;
|
||
} else {
|
||
|
||
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
||
cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE;
|
||
cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8);
|
||
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF);
|
||
|
||
//
|
||
// Disable block descriptors.
|
||
//
|
||
|
||
cdb->MODE_SENSE10.Dbd = TRUE;
|
||
|
||
srb->CdbLength = 10;
|
||
}
|
||
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->DataTransferLength = MODE_DATA_SIZE;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
|
||
}
|
||
|
||
case IOCTL_CDROM_GET_VOLUME:
|
||
case IOCTL_CDROM_SET_VOLUME: {
|
||
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
MODE_DATA_SIZE,
|
||
CDROM_TAG_VOLUME);
|
||
|
||
if (!dataBuffer) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
MODE_DATA_SIZE,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp2->MdlAddress) {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp2);
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
BAIL_OUT(Irp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
|
||
srb->DataBuffer = dataBuffer;
|
||
|
||
RtlZeroMemory(dataBuffer, MODE_DATA_SIZE);
|
||
|
||
|
||
if (use6Byte) {
|
||
|
||
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb->MODE_SENSE.PageCode = CDROM_AUDIO_CONTROL_PAGE;
|
||
cdb->MODE_SENSE.AllocationLength = MODE_DATA_SIZE;
|
||
|
||
srb->CdbLength = 6;
|
||
|
||
} else {
|
||
|
||
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
||
cdb->MODE_SENSE10.PageCode = CDROM_AUDIO_CONTROL_PAGE;
|
||
cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(MODE_DATA_SIZE >> 8);
|
||
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(MODE_DATA_SIZE & 0xFF);
|
||
|
||
srb->CdbLength = 10;
|
||
}
|
||
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->DataTransferLength = MODE_DATA_SIZE;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
|
||
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_SET_VOLUME) {
|
||
|
||
//
|
||
// Setup a different completion routine as the mode sense data is needed in order
|
||
// to send the mode select.
|
||
//
|
||
|
||
IoSetCompletionRoutine(irp2,
|
||
CdRomSetVolumeIntermediateCompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
}
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
|
||
}
|
||
|
||
case IOCTL_STORAGE_SET_READ_AHEAD: {
|
||
|
||
PSTORAGE_SET_READ_AHEAD readAhead = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
ULONG blockAddress;
|
||
PFOUR_BYTE fourByte = (PFOUR_BYTE) &blockAddress;
|
||
|
||
//
|
||
// setup the SRB for a set readahead command
|
||
//
|
||
|
||
cdb->SET_READ_AHEAD.OperationCode = SCSIOP_SET_READ_AHEAD;
|
||
|
||
blockAddress = (ULONG) (readAhead->TriggerAddress.QuadPart >>
|
||
fdoExtension->SectorShift);
|
||
|
||
cdb->SET_READ_AHEAD.TriggerLBA[0] = fourByte->Byte3;
|
||
cdb->SET_READ_AHEAD.TriggerLBA[1] = fourByte->Byte2;
|
||
cdb->SET_READ_AHEAD.TriggerLBA[2] = fourByte->Byte1;
|
||
cdb->SET_READ_AHEAD.TriggerLBA[3] = fourByte->Byte0;
|
||
|
||
blockAddress = (ULONG) (readAhead->TargetAddress.QuadPart >>
|
||
fdoExtension->SectorShift);
|
||
|
||
cdb->SET_READ_AHEAD.ReadAheadLBA[0] = fourByte->Byte3;
|
||
cdb->SET_READ_AHEAD.ReadAheadLBA[1] = fourByte->Byte2;
|
||
cdb->SET_READ_AHEAD.ReadAheadLBA[2] = fourByte->Byte1;
|
||
cdb->SET_READ_AHEAD.ReadAheadLBA[3] = fourByte->Byte0;
|
||
|
||
srb->CdbLength = 12;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp2);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_DISK_GET_DRIVE_LAYOUT:
|
||
case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
|
||
case IOCTL_DISK_GET_PARTITION_INFO:
|
||
case IOCTL_DISK_GET_PARTITION_INFO_EX: {
|
||
|
||
ASSERT(irp2);
|
||
ASSERT(senseBuffer);
|
||
ASSERT(srb);
|
||
|
||
ExFreePool(srb);
|
||
ExFreePool(senseBuffer);
|
||
IoFreeIrp(irp2);
|
||
|
||
//
|
||
// NOTE: should probably update the media's capacity first...
|
||
//
|
||
|
||
CdromFakePartitionInfo(commonExtension, Irp);
|
||
return;
|
||
}
|
||
|
||
case IOCTL_DISK_IS_WRITABLE: {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomStartIo: DiskIsWritable (%p) - returning %s\n",
|
||
Irp, (cdData->Mmc.WriteAllowed ? "TRUE" : "false")));
|
||
|
||
ASSERT(irp2);
|
||
ASSERT(senseBuffer);
|
||
ASSERT(srb);
|
||
|
||
ExFreePool(srb);
|
||
ExFreePool(senseBuffer);
|
||
IoFreeIrp(irp2);
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
if (cdData->Mmc.WriteAllowed) {
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
} else {
|
||
Irp->IoStatus.Status = STATUS_MEDIA_WRITE_PROTECTED;
|
||
}
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, Irp);
|
||
return;
|
||
}
|
||
|
||
default: {
|
||
|
||
UCHAR uniqueAddress;
|
||
|
||
//
|
||
// Just complete the request - CdRomClassIoctlCompletion will take
|
||
// care of it for us
|
||
//
|
||
// NOTE: THIS IS A SYNCHRONIZATION METHOD!!!
|
||
//
|
||
|
||
//
|
||
// Acquire a new copy of the lock so that ClassCompleteRequest
|
||
// doesn't get confused when we complete the other request while
|
||
// holding the lock.
|
||
//
|
||
|
||
//
|
||
// NOTE: CdRomDeviceControlDispatch/CdRomDeviceControlCompletion
|
||
// wait for the event and eventually calls
|
||
// IoStartNextPacket()
|
||
//
|
||
|
||
ASSERT(irp2);
|
||
ASSERT(senseBuffer);
|
||
ASSERT(srb);
|
||
|
||
ExFreePool(srb);
|
||
ExFreePool(senseBuffer);
|
||
IoFreeIrp(irp2);
|
||
|
||
|
||
|
||
ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddress);
|
||
ClassReleaseRemoveLock(Fdo, Irp);
|
||
ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
|
||
ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddress);
|
||
return;
|
||
}
|
||
|
||
} // end switch()
|
||
} else if (currentIrpStack->MajorFunction == IRP_MJ_SHUTDOWN ||
|
||
currentIrpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) {
|
||
|
||
currentIrpStack->Parameters.Others.Argument1 = 0;
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
CdRomShutdownFlushCompletion(Fdo, NULL, Irp);
|
||
return;
|
||
|
||
}
|
||
|
||
//
|
||
// If a read or an unhandled IRP_MJ_XX, end up here. The unhandled IRP_MJ's
|
||
// are expected and composed of AutoRun Irps, at present.
|
||
//
|
||
|
||
IoCallDriver(commonExtension->LowerDeviceObject, Irp);
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomReadWriteVerification(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the entry called by the I/O system for read requests.
|
||
It builds the SRB and sends it to the port driver.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the system object for the device.
|
||
Irp - IRP involved.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
|
||
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
|
||
LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
|
||
|
||
PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData);
|
||
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb = (PCDB)srb.Cdb;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// note: we are no longer failing write commands immediately
|
||
// they are now failed in StartIo based upon media ability
|
||
//
|
||
|
||
//
|
||
// If the cd is playing music then reject this request.
|
||
//
|
||
|
||
if (PLAY_ACTIVE(fdoExtension)) {
|
||
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
||
return STATUS_DEVICE_BUSY;
|
||
}
|
||
|
||
//
|
||
// Verify parameters of this request.
|
||
// Check that ending sector is on disc and
|
||
// that number of bytes to transfer is a multiple of
|
||
// the sector size.
|
||
//
|
||
|
||
startingOffset.QuadPart = currentIrpStack->Parameters.Read.ByteOffset.QuadPart +
|
||
transferByteCount;
|
||
|
||
if (!fdoExtension->DiskGeometry.BytesPerSector) {
|
||
fdoExtension->DiskGeometry.BytesPerSector = 2048;
|
||
}
|
||
|
||
if ((startingOffset.QuadPart > commonExtension->PartitionLength.QuadPart) ||
|
||
(transferByteCount & fdoExtension->DiskGeometry.BytesPerSector - 1)) {
|
||
|
||
//
|
||
// Fail request with status of invalid parameters.
|
||
//
|
||
|
||
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // end CdRomReadWriteVerification()
|
||
|
||
|
||
NTSTATUS
|
||
CdRomSwitchModeCompletion(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN PVOID Context
|
||
)
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
|
||
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData);
|
||
BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE);
|
||
PIO_STACK_LOCATION realIrpStack;
|
||
PIO_STACK_LOCATION realIrpNextStack;
|
||
PSCSI_REQUEST_BLOCK srb = Context;
|
||
PIRP realIrp = NULL;
|
||
NTSTATUS status;
|
||
BOOLEAN retry;
|
||
|
||
//
|
||
// Extract the 'real' irp from the irpstack.
|
||
//
|
||
|
||
realIrp = (PIRP) irpStack->Parameters.Others.Argument2;
|
||
realIrpStack = IoGetCurrentIrpStackLocation(realIrp);
|
||
realIrpNextStack = IoGetNextIrpStackLocation(realIrp);
|
||
|
||
//
|
||
// Check SRB status for success of completing request.
|
||
//
|
||
|
||
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
||
|
||
ULONG retryInterval;
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomSetVolumeIntermediateCompletion: Irp %p, Srb %p, Real Irp %p\n",
|
||
Irp,
|
||
srb,
|
||
realIrp));
|
||
|
||
//
|
||
// Release the queue if it is frozen.
|
||
//
|
||
|
||
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
||
ClassReleaseQueue(DeviceObject);
|
||
}
|
||
|
||
|
||
retry = ClassInterpretSenseInfo(DeviceObject,
|
||
srb,
|
||
irpStack->MajorFunction,
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode,
|
||
MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1),
|
||
&status,
|
||
&retryInterval);
|
||
|
||
//
|
||
// If the status is verified required and the this request
|
||
// should bypass verify required then retry the request.
|
||
//
|
||
|
||
if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
|
||
status == STATUS_VERIFY_REQUIRED) {
|
||
|
||
status = STATUS_IO_DEVICE_ERROR;
|
||
retry = TRUE;
|
||
}
|
||
|
||
if (retry && ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)--) {
|
||
|
||
if (((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) {
|
||
|
||
//
|
||
// Retry request.
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"Retry request %p - Calling StartIo\n", Irp));
|
||
|
||
|
||
ExFreePool(srb->SenseInfoBuffer);
|
||
ExFreePool(srb->DataBuffer);
|
||
ExFreePool(srb);
|
||
if (Irp->MdlAddress) {
|
||
IoFreeMdl(Irp->MdlAddress);
|
||
}
|
||
|
||
IoFreeIrp(Irp);
|
||
|
||
//
|
||
// Call StartIo directly since IoStartNextPacket hasn't been called,
|
||
// the serialisation is still intact.
|
||
//
|
||
|
||
CdRomRetryRequest(fdoExtension,
|
||
realIrp,
|
||
retryInterval,
|
||
FALSE);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
}
|
||
|
||
//
|
||
// Exhausted retries. Fall through and complete the request with the appropriate status.
|
||
//
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Set status for successful request.
|
||
//
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
ULONG sectorSize, startingSector, transferByteCount;
|
||
PCDB cdb;
|
||
|
||
//
|
||
// Update device ext. to show which mode we are currently using.
|
||
//
|
||
|
||
sectorSize = cdData->BlockDescriptor.BlockLength[0] << 16;
|
||
sectorSize |= (cdData->BlockDescriptor.BlockLength[1] << 8);
|
||
sectorSize |= (cdData->BlockDescriptor.BlockLength[2]);
|
||
|
||
cdData->RawAccess = (sectorSize == RAW_SECTOR_SIZE) ? TRUE : FALSE;
|
||
|
||
//
|
||
// Free the old data buffer, mdl.
|
||
// reuse the SenseInfoBuffer and Srb
|
||
//
|
||
|
||
ExFreePool(srb->DataBuffer);
|
||
IoFreeMdl(Irp->MdlAddress);
|
||
IoFreeIrp(Irp);
|
||
|
||
//
|
||
// rebuild the srb.
|
||
//
|
||
|
||
cdb = (PCDB)srb->Cdb;
|
||
RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
|
||
|
||
|
||
if (cdData->RawAccess) {
|
||
|
||
PRAW_READ_INFO rawReadInfo =
|
||
(PRAW_READ_INFO)realIrpStack->Parameters.DeviceIoControl.Type3InputBuffer;
|
||
|
||
ULONG maximumTransferLength;
|
||
ULONG transferPages;
|
||
UCHAR min, sec, frame;
|
||
|
||
//
|
||
// Calculate starting offset.
|
||
//
|
||
|
||
startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> fdoExtension->SectorShift);
|
||
transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
|
||
maximumTransferLength = fdoExtension->AdapterDescriptor->MaximumTransferLength;
|
||
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(realIrp->MdlAddress),
|
||
transferByteCount);
|
||
|
||
//
|
||
// Determine if request is within limits imposed by miniport.
|
||
// If the request is larger than the miniport's capabilities, split it.
|
||
//
|
||
|
||
if (transferByteCount > maximumTransferLength ||
|
||
transferPages > fdoExtension->AdapterDescriptor->MaximumPhysicalPages) {
|
||
|
||
|
||
ExFreePool(srb->SenseInfoBuffer);
|
||
ExFreePool(srb);
|
||
realIrp->IoStatus.Information = 0;
|
||
realIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
||
|
||
BAIL_OUT(realIrp);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp);
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
srb->OriginalRequest = realIrp;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
|
||
srb->DataTransferLength = transferByteCount;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->CdbLength = 10;
|
||
srb->DataBuffer = MmGetMdlVirtualAddress(realIrp->MdlAddress);
|
||
|
||
if (rawReadInfo->TrackMode == CDDA) {
|
||
if (TEST_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA)) {
|
||
|
||
srb->CdbLength = 12;
|
||
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF);
|
||
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte3 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte2 = (UCHAR) (rawReadInfo->SectorCount >> 8);
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte1 = 0;
|
||
cdb->PLXTR_READ_CDDA.TransferBlockByte0 = 0;
|
||
|
||
cdb->PLXTR_READ_CDDA.SubCode = 0;
|
||
cdb->PLXTR_READ_CDDA.OperationCode = 0xD8;
|
||
|
||
} else if (TEST_FLAG(cdData->XAFlags, XA_NEC_CDDA)) {
|
||
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF);
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF);
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF);
|
||
cdb->NEC_READ_CDDA.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF);
|
||
|
||
cdb->NEC_READ_CDDA.TransferBlockByte1 = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
|
||
cdb->NEC_READ_CDDA.TransferBlockByte0 = (UCHAR) (rawReadInfo->SectorCount >> 8);
|
||
|
||
cdb->NEC_READ_CDDA.OperationCode = 0xD4;
|
||
}
|
||
} else {
|
||
cdb->CDB10.TransferBlocksMsb = (UCHAR) (rawReadInfo->SectorCount >> 8);
|
||
cdb->CDB10.TransferBlocksLsb = (UCHAR) (rawReadInfo->SectorCount & 0xFF);
|
||
|
||
cdb->CDB10.LogicalBlockByte3 = (UCHAR) (startingSector & 0xFF);
|
||
cdb->CDB10.LogicalBlockByte2 = (UCHAR) ((startingSector >> 8) & 0xFF);
|
||
cdb->CDB10.LogicalBlockByte1 = (UCHAR) ((startingSector >> 16) & 0xFF);
|
||
cdb->CDB10.LogicalBlockByte0 = (UCHAR) ((startingSector >> 24) & 0xFF);
|
||
|
||
cdb->CDB10.OperationCode = SCSIOP_READ;
|
||
}
|
||
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
|
||
|
||
irpStack = IoGetNextIrpStackLocation(realIrp);
|
||
irpStack->MajorFunction = IRP_MJ_SCSI;
|
||
irpStack->Parameters.Scsi.Srb = srb;
|
||
|
||
if (!(irpStack->Parameters.Others.Argument1)) {
|
||
|
||
//
|
||
// Only jam this in if it doesn't exist. The completion routines can
|
||
// call StartIo directly in the case of retries and resetting it will
|
||
// cause infinite loops.
|
||
//
|
||
|
||
irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
|
||
}
|
||
|
||
//
|
||
// Set up IoCompletion routine address.
|
||
//
|
||
|
||
IoSetCompletionRoutine(realIrp,
|
||
CdRomXACompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
} else {
|
||
|
||
PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor;
|
||
ULONG maximumTransferLength;
|
||
ULONG transferPages;
|
||
|
||
//
|
||
// a writable device must be MMC compliant, which supports
|
||
// READ_CD commands, so writes and mode switching should
|
||
// never occur on the same device.
|
||
//
|
||
|
||
ASSERT(realIrpStack->MajorFunction != IRP_MJ_WRITE);
|
||
|
||
//
|
||
// free the SRB and SenseInfoBuffer since they aren't used
|
||
// by either ClassBuildRequest() nor ClassSplitRequest().
|
||
//
|
||
|
||
ExFreePool(srb->SenseInfoBuffer);
|
||
ExFreePool(srb);
|
||
|
||
//
|
||
// Back to cooked sectors. Build and send a normal read.
|
||
// The real work for setting offsets was done in startio.
|
||
//
|
||
|
||
adapterDescriptor =
|
||
commonExtension->PartitionZeroExtension->AdapterDescriptor;
|
||
maximumTransferLength = adapterDescriptor->MaximumTransferLength;
|
||
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
||
MmGetMdlVirtualAddress(realIrp->MdlAddress),
|
||
realIrpStack->Parameters.Read.Length);
|
||
|
||
if ((realIrpStack->Parameters.Read.Length > maximumTransferLength) ||
|
||
(transferPages > adapterDescriptor->MaximumPhysicalPages)) {
|
||
|
||
ULONG maxPages = adapterDescriptor->MaximumPhysicalPages;
|
||
|
||
if (maxPages != 0) {
|
||
maxPages --; // to account for page boundaries
|
||
}
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdromSwitchModeCompletion: Request greater than "
|
||
" maximum\n"));
|
||
TraceLog((CdromDebugTrace,
|
||
"CdromSwitchModeCompletion: Maximum is %lx\n",
|
||
maximumTransferLength));
|
||
TraceLog((CdromDebugTrace,
|
||
"CdromSwitchModeCompletion: Byte count is %lx\n",
|
||
realIrpStack->Parameters.Read.Length));
|
||
|
||
//
|
||
// Check that the maximum transfer length fits within
|
||
// the maximum number of pages the device can handle.
|
||
//
|
||
|
||
if (maximumTransferLength > maxPages << PAGE_SHIFT) {
|
||
maximumTransferLength = maxPages << PAGE_SHIFT;
|
||
}
|
||
|
||
//
|
||
// Check that maximum transfer size is not zero
|
||
//
|
||
|
||
if (maximumTransferLength == 0) {
|
||
maximumTransferLength = PAGE_SIZE;
|
||
}
|
||
|
||
//
|
||
// Request needs to be split. Completion of each portion
|
||
// of the request will fire off the next portion. The final
|
||
// request will signal Io to send a new request.
|
||
//
|
||
|
||
ClassSplitRequest(DeviceObject, realIrp, maximumTransferLength);
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Build SRB and CDB for this IRP.
|
||
//
|
||
|
||
ClassBuildRequest(DeviceObject, realIrp);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Call the port driver.
|
||
//
|
||
|
||
IoCallDriver(commonExtension->LowerDeviceObject, realIrp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
//
|
||
// Update device Extension flags to indicate that XA isn't supported.
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"Device Cannot Support CDDA (but tested positive) "
|
||
"Now Clearing CDDA flags for FDO %p\n", DeviceObject));
|
||
SET_FLAG(cdData->XAFlags, XA_NOT_SUPPORTED);
|
||
CLEAR_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA);
|
||
CLEAR_FLAG(cdData->XAFlags, XA_NEC_CDDA);
|
||
|
||
//
|
||
// Deallocate srb and sense buffer.
|
||
//
|
||
|
||
if (srb) {
|
||
if (srb->DataBuffer) {
|
||
ExFreePool(srb->DataBuffer);
|
||
}
|
||
if (srb->SenseInfoBuffer) {
|
||
ExFreePool(srb->SenseInfoBuffer);
|
||
}
|
||
ExFreePool(srb);
|
||
}
|
||
|
||
if (Irp->PendingReturned) {
|
||
IoMarkIrpPending(Irp);
|
||
}
|
||
|
||
if (realIrp->PendingReturned) {
|
||
IoMarkIrpPending(realIrp);
|
||
}
|
||
|
||
if (Irp->MdlAddress) {
|
||
IoFreeMdl(Irp->MdlAddress);
|
||
}
|
||
|
||
IoFreeIrp(Irp);
|
||
|
||
//
|
||
// Set status in completing IRP.
|
||
//
|
||
|
||
realIrp->IoStatus.Status = status;
|
||
|
||
//
|
||
// Set the hard error if necessary.
|
||
//
|
||
|
||
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
|
||
|
||
//
|
||
// Store DeviceObject for filesystem, and clear
|
||
// in IoStatus.Information field.
|
||
//
|
||
|
||
if (realIrp->Tail.Overlay.Thread) {
|
||
IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
|
||
}
|
||
realIrp->IoStatus.Information = 0;
|
||
}
|
||
|
||
CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
VOID
|
||
ScanForSpecialHandler(
|
||
PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
||
ULONG_PTR HackFlags
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension;
|
||
PCDROM_DATA cdData;
|
||
|
||
PAGED_CODE();
|
||
|
||
CLEAR_FLAG(HackFlags, CDROM_HACK_INVALID_FLAGS);
|
||
|
||
commonExtension = &(FdoExtension->CommonExtension);
|
||
cdData = (PCDROM_DATA)(commonExtension->DriverData);
|
||
cdData->HackFlags = HackFlags;
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
ScanForSpecial(
|
||
PDEVICE_OBJECT DeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function checks to see if an SCSI logical unit requires an special
|
||
initialization or error processing.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies the device object to be tested.
|
||
|
||
InquiryData - Supplies the inquiry data returned by the device of interest.
|
||
|
||
PortCapabilities - Supplies the capabilities of the device object.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension;
|
||
PCDROM_DATA cdData;
|
||
|
||
PAGED_CODE();
|
||
|
||
fdoExtension = DeviceObject->DeviceExtension;
|
||
commonExtension = DeviceObject->DeviceExtension;
|
||
cdData = (PCDROM_DATA)(commonExtension->DriverData);
|
||
|
||
|
||
//
|
||
// set our hack flags
|
||
//
|
||
|
||
ClassScanForSpecial(fdoExtension, 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(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_SPIN_UP);
|
||
SET_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_NO_QUEUE_LOCK);
|
||
|
||
if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_1750)
|
||
&& ( fdoExtension->AdapterDescriptor->AdapterUsesPio )
|
||
) {
|
||
|
||
//
|
||
// Read-ahead must be disabled in order to get this cdrom drive
|
||
// to work on scsi adapters that use PIO.
|
||
//
|
||
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRom ScanForSpecial: Found Hitachi CDR-1750S.\n"));
|
||
|
||
//
|
||
// Setup an error handler to reinitialize the cd rom after it is reset.
|
||
//
|
||
|
||
cdData->ErrorHandler = HitachiProcessError;
|
||
|
||
//
|
||
// Lock down the hitachi error processing code.
|
||
//
|
||
|
||
MmLockPagableCodeSection(HitachiProcessError);
|
||
SET_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES);
|
||
|
||
|
||
} else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_TOSHIBA_SD_W1101)) {
|
||
|
||
TraceLog((CdromDebugError,
|
||
"CdRom ScanForSpecial: Found Toshiba SD-W1101 DVD-RAM "
|
||
"-- This drive will *NOT* support DVD-ROM playback.\n"));
|
||
|
||
} else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_GD_2000)) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRom ScanForSpecial: 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...
|
||
//
|
||
|
||
cdData->ErrorHandler = HitachiProcessErrorGD2000;
|
||
|
||
//
|
||
// this drive may require START_UNIT commands to spin
|
||
// the drive up when it's spun itself down.
|
||
//
|
||
|
||
SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
|
||
|
||
//
|
||
// Lock down the hitachi error processing code.
|
||
//
|
||
|
||
MmLockPagableCodeSection(HitachiProcessErrorGD2000);
|
||
SET_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES);
|
||
|
||
} else if (TEST_FLAG(cdData->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.
|
||
//
|
||
|
||
fdoExtension->TimeOutValue = 20;
|
||
|
||
} else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_DEC_RRD)) {
|
||
|
||
PMODE_PARM_READ_WRITE_DATA modeParameters;
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb;
|
||
NTSTATUS status;
|
||
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRom ScanForSpecial: Found DEC RRD.\n"));
|
||
|
||
cdData->IsDecRrd = TRUE;
|
||
|
||
//
|
||
// Setup an error handler to reinitialize the cd rom after it is reset?
|
||
//
|
||
//commonExtension->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(NonPagedPool,
|
||
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 = fdoExtension->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 = ClassSendSrbSynchronous(DeviceObject,
|
||
&srb,
|
||
modeParameters,
|
||
sizeof(MODE_PARM_READ_WRITE_DATA),
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRom ScanForSpecial: Setting DEC RRD to 2K block"
|
||
"size failed [%x]\n", status));
|
||
}
|
||
ExFreePool(modeParameters);
|
||
|
||
} else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_TOSHIBA_XM_3xx)) {
|
||
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb;
|
||
ULONG length;
|
||
PUCHAR buffer;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Set the density code and the error handler.
|
||
//
|
||
|
||
length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH);
|
||
|
||
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
|
||
//
|
||
// Build the MODE SENSE CDB.
|
||
//
|
||
|
||
srb.CdbLength = 6;
|
||
cdb = (PCDB)srb.Cdb;
|
||
|
||
//
|
||
// Set timeout value from device extension.
|
||
//
|
||
|
||
srb.TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb->MODE_SENSE.PageCode = 0x1;
|
||
// NOTE: purposely not setting DBD because it is what is needed.
|
||
cdb->MODE_SENSE.AllocationLength = (UCHAR)length;
|
||
|
||
buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
(sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH),
|
||
CDROM_TAG_MODE_DATA);
|
||
if (!buffer) {
|
||
return;
|
||
}
|
||
|
||
status = ClassSendSrbSynchronous(DeviceObject,
|
||
&srb,
|
||
buffer,
|
||
length,
|
||
FALSE);
|
||
|
||
((PERROR_RECOVERY_DATA)buffer)->BlockDescriptor.DensityCode = 0x83;
|
||
((PERROR_RECOVERY_DATA)buffer)->Header.ModeDataLength = 0x0;
|
||
|
||
RtlCopyMemory(&cdData->Header, buffer, sizeof(ERROR_RECOVERY_DATA));
|
||
|
||
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
|
||
//
|
||
// Build the MODE SENSE CDB.
|
||
//
|
||
|
||
srb.CdbLength = 6;
|
||
cdb = (PCDB)srb.Cdb;
|
||
|
||
//
|
||
// Set timeout value from device extension.
|
||
//
|
||
|
||
srb.TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
|
||
cdb->MODE_SELECT.PFBit = 1;
|
||
cdb->MODE_SELECT.ParameterListLength = (UCHAR)length;
|
||
|
||
status = ClassSendSrbSynchronous(DeviceObject,
|
||
&srb,
|
||
buffer,
|
||
length,
|
||
TRUE);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
TraceLog((CdromDebugWarning,
|
||
"Cdrom.ScanForSpecial: Setting density code on Toshiba failed [%x]\n",
|
||
status));
|
||
}
|
||
|
||
cdData->ErrorHandler = ToshibaProcessError;
|
||
|
||
//
|
||
// Lock down the toshiba error section.
|
||
//
|
||
|
||
MmLockPagableCodeSection(ToshibaProcessError);
|
||
SET_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES);
|
||
|
||
ExFreePool(buffer);
|
||
|
||
}
|
||
|
||
//
|
||
// Determine special CD-DA requirements.
|
||
//
|
||
|
||
if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_READ_CD_SUPPORTED)) {
|
||
|
||
SET_FLAG(cdData->XAFlags, XA_USE_READ_CD);
|
||
|
||
} else if (!TEST_FLAG(cdData->XAFlags, XA_USE_READ_CD)) {
|
||
|
||
if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_PLEXTOR_CDDA)) {
|
||
SET_FLAG(cdData->XAFlags, XA_PLEXTOR_CDDA);
|
||
} else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_NEC_CDDA)) {
|
||
SET_FLAG(cdData->XAFlags, XA_NEC_CDDA);
|
||
}
|
||
|
||
}
|
||
|
||
if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES)) {
|
||
KdPrintEx((DPFLTR_SYSTEM_ID, DPFLTR_ERROR_LEVEL,
|
||
"Locking pages for error handler\n"));
|
||
}
|
||
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
HitachiProcessErrorGD2000(
|
||
PDEVICE_OBJECT Fdo,
|
||
PSCSI_REQUEST_BLOCK OriginalSrb,
|
||
NTSTATUS *Status,
|
||
BOOLEAN *Retry
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks the type of error. If the error suggests that the
|
||
drive has spun down and cannot reinitialize itself, send a
|
||
START_UNIT or READ to the device. This will force the drive to spin
|
||
up. This drive also loses the AGIDs it has granted when it spins down,
|
||
which may result in playback failure the first time around.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies a pointer to the device object.
|
||
|
||
Srb - Supplies a pointer to the failing Srb.
|
||
|
||
Status - return the final status for this command?
|
||
|
||
Retry - return if the command should be retried.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PSENSE_DATA senseBuffer = OriginalSrb->SenseInfoBuffer;
|
||
|
||
UNREFERENCED_PARAMETER(Status);
|
||
UNREFERENCED_PARAMETER(Retry);
|
||
|
||
if (!TEST_FLAG(OriginalSrb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
|
||
return;
|
||
}
|
||
|
||
if (((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_HARDWARE_ERROR) &&
|
||
(senseBuffer->AdditionalSenseCode == 0x44)) {
|
||
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
PIRP irp;
|
||
PIO_STACK_LOCATION irpStack;
|
||
PCOMPLETION_CONTEXT context;
|
||
PSCSI_REQUEST_BLOCK newSrb;
|
||
PCDB cdb;
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"HitachiProcessErrorGD2000 (%p) => Internal Target "
|
||
"Failure Detected -- spinning up drive\n", Fdo));
|
||
|
||
//
|
||
// the request should be retried because the device isn't ready
|
||
//
|
||
|
||
*Retry = TRUE;
|
||
*Status = STATUS_DEVICE_NOT_READY;
|
||
|
||
//
|
||
// send a START_STOP unit to spin up the drive
|
||
// NOTE: this temporarily violates the StartIo serialization
|
||
// mechanism, but the completion routine on this will NOT
|
||
// call StartNextPacket(), so it's a temporary disruption
|
||
// of the serialization only.
|
||
//
|
||
|
||
ClassSendStartUnit(Fdo);
|
||
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
HitachiProcessError(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
NTSTATUS *Status,
|
||
BOOLEAN *Retry
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks the type of error. If the error indicates CD-ROM the
|
||
CD-ROM needs to be reinitialized then a Mode sense command is sent to the
|
||
device. This command disables read-ahead for the device.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies a pointer to the device object.
|
||
|
||
Srb - Supplies a pointer to the failing Srb.
|
||
|
||
Status - Not used.
|
||
|
||
Retry - Not used.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
||
LARGE_INTEGER largeInt;
|
||
PUCHAR modePage;
|
||
PIO_STACK_LOCATION irpStack;
|
||
PIRP irp;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
PCOMPLETION_CONTEXT context;
|
||
PCDB cdb;
|
||
ULONG_PTR alignment;
|
||
|
||
UNREFERENCED_PARAMETER(Status);
|
||
UNREFERENCED_PARAMETER(Retry);
|
||
|
||
largeInt.QuadPart = (LONGLONG) 1;
|
||
|
||
//
|
||
// Check the status. The initialization command only needs to be sent
|
||
// if UNIT ATTENTION is returned.
|
||
//
|
||
|
||
if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) {
|
||
|
||
//
|
||
// The drive does not require reinitialization.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Found an HITACHI cd-rom that does not work with PIO
|
||
// adapters when read-ahead is enabled. Read-ahead is disabled by
|
||
// a mode select command. The mode select page code is zero and the
|
||
// length is 6 bytes. All of the other bytes should be zero.
|
||
//
|
||
|
||
if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"HitachiProcessError: Reinitializing the CD-ROM.\n"));
|
||
|
||
//
|
||
// Send the special mode select command to disable read-ahead
|
||
// on the CD-ROM reader.
|
||
//
|
||
|
||
alignment = DeviceObject->AlignmentRequirement ?
|
||
DeviceObject->AlignmentRequirement : 1;
|
||
|
||
context = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(COMPLETION_CONTEXT) + HITACHI_MODE_DATA_SIZE + (ULONG)alignment,
|
||
CDROM_TAG_HITACHI_ERROR
|
||
);
|
||
|
||
if (context == NULL) {
|
||
|
||
//
|
||
// If there is not enough memory to fulfill this request,
|
||
// simply return. A subsequent retry will fail and another
|
||
// chance to start the unit.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
context->DeviceObject = DeviceObject;
|
||
srb = &context->Srb;
|
||
|
||
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
||
|
||
//
|
||
// Write length to SRB.
|
||
//
|
||
|
||
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
|
||
//
|
||
// Set up SCSI bus address.
|
||
//
|
||
|
||
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
//
|
||
// Set the transfer length.
|
||
//
|
||
|
||
srb->DataTransferLength = HITACHI_MODE_DATA_SIZE;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
|
||
//
|
||
// The data buffer must be aligned.
|
||
//
|
||
|
||
srb->DataBuffer = (PVOID) (((ULONG_PTR) (context + 1) + (alignment - 1)) &
|
||
~(alignment - 1));
|
||
|
||
|
||
//
|
||
// Build the HITACHI read-ahead mode select CDB.
|
||
//
|
||
|
||
srb->CdbLength = 6;
|
||
cdb = (PCDB)srb->Cdb;
|
||
cdb->MODE_SENSE.LogicalUnitNumber = srb->Lun;
|
||
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SELECT;
|
||
cdb->MODE_SENSE.AllocationLength = HITACHI_MODE_DATA_SIZE;
|
||
|
||
//
|
||
// Initialize the mode sense data.
|
||
//
|
||
|
||
modePage = srb->DataBuffer;
|
||
|
||
RtlZeroMemory(modePage, HITACHI_MODE_DATA_SIZE);
|
||
|
||
//
|
||
// Set the page length field to 6.
|
||
//
|
||
|
||
modePage[5] = 6;
|
||
|
||
//
|
||
// Build the asynchronous request to be sent to the port driver.
|
||
//
|
||
|
||
irp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE,
|
||
DeviceObject,
|
||
srb->DataBuffer,
|
||
srb->DataTransferLength,
|
||
&largeInt,
|
||
NULL);
|
||
|
||
if (irp == NULL) {
|
||
|
||
//
|
||
// If there is not enough memory to fulfill this request,
|
||
// simply return. A subsequent retry will fail and another
|
||
// chance to start the unit.
|
||
//
|
||
|
||
ExFreePool(context);
|
||
return;
|
||
}
|
||
|
||
ClassAcquireRemoveLock(DeviceObject, irp);
|
||
|
||
IoSetCompletionRoutine(irp,
|
||
(PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
|
||
context,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
irpStack = IoGetNextIrpStackLocation(irp);
|
||
|
||
irpStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
srb->OriginalRequest = irp;
|
||
|
||
//
|
||
// Save SRB address in next stack for port driver.
|
||
//
|
||
|
||
irpStack->Parameters.Scsi.Srb = (PVOID)srb;
|
||
|
||
//
|
||
// Set up IRP Address.
|
||
//
|
||
|
||
(VOID)IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ToshibaProcessErrorCompletion(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp,
|
||
PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Completion routine for the ClassError routine to handle older Toshiba units
|
||
that require setting the density code.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies a pointer to the device object.
|
||
|
||
Irp - Pointer to irp created to set the density code.
|
||
|
||
Context - Supplies a pointer to the Mode Select Srb.
|
||
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PSCSI_REQUEST_BLOCK srb = Context;
|
||
|
||
//
|
||
// Free all of the allocations.
|
||
//
|
||
|
||
ClassReleaseRemoveLock(DeviceObject, Irp);
|
||
|
||
ExFreePool(srb->DataBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeMdl(Irp->MdlAddress);
|
||
IoFreeIrp(Irp);
|
||
|
||
//
|
||
// Indicate the I/O system should stop processing the Irp completion.
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
VOID
|
||
ToshibaProcessError(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
NTSTATUS *Status,
|
||
BOOLEAN *Retry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks the type of error. If the error indicates a unit attention,
|
||
the density code needs to be set via a Mode select command.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Supplies a pointer to the device object.
|
||
|
||
Srb - Supplies a pointer to the failing Srb.
|
||
|
||
Status - Not used.
|
||
|
||
Retry - Not used.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
|
||
PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData);
|
||
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
||
PIO_STACK_LOCATION irpStack;
|
||
PIRP irp;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
ULONG length;
|
||
PCDB cdb;
|
||
PUCHAR dataBuffer;
|
||
|
||
|
||
if (!(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// The Toshiba's require the density code to be set on power up and media changes.
|
||
//
|
||
|
||
if ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_UNIT_ATTENTION) {
|
||
|
||
|
||
irp = IoAllocateIrp((CCHAR)(DeviceObject->StackSize+1),
|
||
FALSE);
|
||
|
||
if (!irp) {
|
||
return;
|
||
}
|
||
|
||
srb = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(SCSI_REQUEST_BLOCK),
|
||
CDROM_TAG_TOSHIBA_ERROR);
|
||
if (!srb) {
|
||
IoFreeIrp(irp);
|
||
return;
|
||
}
|
||
|
||
|
||
length = sizeof(ERROR_RECOVERY_DATA);
|
||
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
length,
|
||
CDROM_TAG_TOSHIBA_ERROR);
|
||
if (!dataBuffer) {
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp);
|
||
return;
|
||
}
|
||
|
||
irp->MdlAddress = IoAllocateMdl(dataBuffer,
|
||
length,
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (!irp->MdlAddress) {
|
||
ExFreePool(srb);
|
||
ExFreePool(dataBuffer);
|
||
IoFreeIrp(irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp->MdlAddress);
|
||
|
||
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
|
||
srb->DataBuffer = dataBuffer;
|
||
cdb = (PCDB)srb->Cdb;
|
||
|
||
//
|
||
// Set up the irp.
|
||
//
|
||
|
||
IoSetNextIrpStackLocation(irp);
|
||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||
irp->IoStatus.Information = 0;
|
||
irp->Flags = 0;
|
||
irp->UserBuffer = NULL;
|
||
|
||
//
|
||
// Save the device object and irp in a private stack location.
|
||
//
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||
irpStack->DeviceObject = DeviceObject;
|
||
|
||
//
|
||
// Construct the IRP stack for the lower level driver.
|
||
//
|
||
|
||
irpStack = IoGetNextIrpStackLocation(irp);
|
||
irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_OUT;
|
||
irpStack->Parameters.Scsi.Srb = srb;
|
||
|
||
IoSetCompletionRoutine(irp,
|
||
ToshibaProcessErrorCompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
ClassAcquireRemoveLock(DeviceObject, irp);
|
||
|
||
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
srb->NextSrb = 0;
|
||
srb->OriginalRequest = irp;
|
||
srb->SenseInfoBufferLength = 0;
|
||
|
||
//
|
||
// Set the transfer length.
|
||
//
|
||
|
||
srb->DataTransferLength = length;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
|
||
|
||
|
||
srb->CdbLength = 6;
|
||
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
|
||
cdb->MODE_SELECT.PFBit = 1;
|
||
cdb->MODE_SELECT.ParameterListLength = (UCHAR)length;
|
||
|
||
//
|
||
// Copy the Mode page into the databuffer.
|
||
//
|
||
|
||
RtlCopyMemory(srb->DataBuffer, &cdData->Header, length);
|
||
|
||
//
|
||
// Set the density code.
|
||
//
|
||
|
||
((PERROR_RECOVERY_DATA)srb->DataBuffer)->BlockDescriptor.DensityCode = 0x83;
|
||
|
||
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
CdRomIsPlayActive(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the cd is currently playing music.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Device object to test.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the device is playing music.
|
||
|
||
--*/
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
IO_STATUS_BLOCK ioStatus;
|
||
PSUB_Q_CURRENT_POSITION currentBuffer;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// if we don't think it is playing audio, don't bother checking.
|
||
//
|
||
|
||
if (!PLAY_ACTIVE(fdoExtension)) {
|
||
return(FALSE);
|
||
}
|
||
|
||
currentBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
sizeof(SUB_Q_CURRENT_POSITION),
|
||
CDROM_TAG_PLAY_ACTIVE);
|
||
|
||
if (currentBuffer == NULL) {
|
||
return(FALSE);
|
||
}
|
||
|
||
((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
|
||
((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;
|
||
|
||
//
|
||
// Build the synchronous request to be sent to ourself
|
||
// to perform the request.
|
||
//
|
||
|
||
ClassSendDeviceIoControlSynchronous(
|
||
IOCTL_CDROM_READ_Q_CHANNEL,
|
||
DeviceObject,
|
||
currentBuffer,
|
||
sizeof(CDROM_SUB_Q_DATA_FORMAT),
|
||
sizeof(SUB_Q_CURRENT_POSITION),
|
||
FALSE,
|
||
&ioStatus);
|
||
|
||
if (!NT_SUCCESS(ioStatus.Status)) {
|
||
ExFreePool(currentBuffer);
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// should update the playactive flag here.
|
||
//
|
||
|
||
if (currentBuffer->Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
|
||
PLAY_ACTIVE(fdoExtension) = TRUE;
|
||
} else {
|
||
PLAY_ACTIVE(fdoExtension) = FALSE;
|
||
}
|
||
|
||
ExFreePool(currentBuffer);
|
||
|
||
return(PLAY_ACTIVE(fdoExtension));
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
CdRomTickHandler(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the once per second timer provided by the
|
||
Io subsystem. It is used to do delayed retries for cdroms.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - what to check.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
|
||
ULONG isRemoved;
|
||
|
||
KIRQL oldIrql;
|
||
|
||
PIRP irp;
|
||
PIRP heldIrpList;
|
||
PIRP nextIrp;
|
||
PLIST_ENTRY listEntry;
|
||
PCDROM_DATA cddata;
|
||
PIO_STACK_LOCATION irpStack;
|
||
UCHAR uniqueAddress;
|
||
|
||
isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP) &uniqueAddress);
|
||
|
||
//
|
||
// We stop the timer before deleting the device. It's safe to keep going
|
||
// if the flag value is REMOVE_PENDING because the removal thread will be
|
||
// blocked trying to stop the timer.
|
||
//
|
||
|
||
ASSERT(isRemoved != REMOVE_COMPLETE);
|
||
|
||
//
|
||
// This routine is reasonably safe even if the device object has a pending
|
||
// remove
|
||
|
||
cddata = commonExtension->DriverData;
|
||
|
||
//
|
||
// Since cdrom is completely synchronized there can never be more than one
|
||
// irp delayed for retry at any time.
|
||
//
|
||
|
||
KeAcquireSpinLock(&(cddata->DelayedRetrySpinLock), &oldIrql);
|
||
|
||
if(cddata->DelayedRetryIrp != NULL) {
|
||
|
||
PIRP irp = cddata->DelayedRetryIrp;
|
||
|
||
//
|
||
// If we've got a delayed retry at this point then there had beter
|
||
// be an interval for it.
|
||
//
|
||
|
||
ASSERT(cddata->DelayedRetryInterval != 0);
|
||
cddata->DelayedRetryInterval--;
|
||
|
||
if(isRemoved) {
|
||
|
||
//
|
||
// This device is removed - flush the timer queue
|
||
//
|
||
|
||
cddata->DelayedRetryIrp = NULL;
|
||
cddata->DelayedRetryInterval = 0;
|
||
|
||
KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql);
|
||
|
||
ClassReleaseRemoveLock(DeviceObject, irp);
|
||
ClassCompleteRequest(DeviceObject, irp, IO_CD_ROM_INCREMENT);
|
||
|
||
} else if (cddata->DelayedRetryInterval == 0) {
|
||
|
||
//
|
||
// Submit this IRP to the lower driver. This IRP does not
|
||
// need to be remembered here. It will be handled again when
|
||
// it completes.
|
||
//
|
||
|
||
cddata->DelayedRetryIrp = NULL;
|
||
|
||
KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql);
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomTickHandler: Reissuing request %p (thread = %p)\n",
|
||
irp,
|
||
irp->Tail.Overlay.Thread));
|
||
|
||
//
|
||
// feed this to the appropriate port driver
|
||
//
|
||
|
||
CdRomRerunRequest(fdoExtension, irp, cddata->DelayedRetryResend);
|
||
} else {
|
||
KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql);
|
||
}
|
||
} else {
|
||
KeReleaseSpinLock(&(cddata->DelayedRetrySpinLock), oldIrql);
|
||
}
|
||
|
||
ClassReleaseRemoveLock(DeviceObject, (PIRP) &uniqueAddress);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomUpdateGeometryCompletion(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PIRP Irp,
|
||
PVOID Context
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine andles the completion of the test unit ready irps
|
||
used to determine if the media has changed. If the media has
|
||
changed, this code signals the named event to wake up other
|
||
system services that react to media change (aka AutoPlay).
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the object for the completion
|
||
Irp - the IRP being completed
|
||
Context - the SRB from the IRP
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension;
|
||
|
||
PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context;
|
||
PREAD_CAPACITY_DATA readCapacityBuffer;
|
||
PIO_STACK_LOCATION irpStack;
|
||
NTSTATUS status;
|
||
BOOLEAN retry;
|
||
ULONG retryCount;
|
||
ULONG lastSector;
|
||
PIRP originalIrp;
|
||
PCDROM_DATA cddata;
|
||
UCHAR uniqueAddress;
|
||
|
||
//
|
||
// Get items saved in the private IRP stack location.
|
||
//
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
retryCount = (ULONG)(ULONG_PTR) irpStack->Parameters.Others.Argument1;
|
||
originalIrp = (PIRP) irpStack->Parameters.Others.Argument2;
|
||
|
||
if (!DeviceObject) {
|
||
DeviceObject = irpStack->DeviceObject;
|
||
}
|
||
ASSERT(DeviceObject);
|
||
|
||
fdoExtension = DeviceObject->DeviceExtension;
|
||
commonExtension = DeviceObject->DeviceExtension;
|
||
cddata = commonExtension->DriverData;
|
||
readCapacityBuffer = srb->DataBuffer;
|
||
|
||
if ((NT_SUCCESS(Irp->IoStatus.Status)) && (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)) {
|
||
|
||
CdRomInterpretReadCapacity(DeviceObject, readCapacityBuffer);
|
||
|
||
} else {
|
||
|
||
ULONG retryInterval;
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomUpdateGeometryCompletion: [%p] unsuccessful "
|
||
"completion of buddy-irp %p (status - %lx)\n",
|
||
originalIrp, Irp, Irp->IoStatus.Status));
|
||
|
||
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
||
ClassReleaseQueue(DeviceObject);
|
||
}
|
||
|
||
retry = ClassInterpretSenseInfo(DeviceObject,
|
||
srb,
|
||
IRP_MJ_SCSI,
|
||
0,
|
||
retryCount,
|
||
&status,
|
||
&retryInterval);
|
||
if (retry) {
|
||
retryCount--;
|
||
if ((retryCount) && (commonExtension->IsRemoved == NO_REMOVE)) {
|
||
PCDB cdb;
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomUpdateGeometryCompletion: [%p] Retrying "
|
||
"request %p .. thread is %p\n",
|
||
originalIrp, Irp, Irp->Tail.Overlay.Thread));
|
||
|
||
//
|
||
// set up a one shot timer to get this process started over
|
||
//
|
||
|
||
irpStack->Parameters.Others.Argument1 = ULongToPtr( retryCount );
|
||
irpStack->Parameters.Others.Argument2 = (PVOID) originalIrp;
|
||
irpStack->Parameters.Others.Argument3 = (PVOID) 2;
|
||
|
||
//
|
||
// Setup the IRP to be submitted again in the timer routine.
|
||
//
|
||
|
||
irpStack = IoGetNextIrpStackLocation(Irp);
|
||
irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
|
||
irpStack->Parameters.Scsi.Srb = srb;
|
||
IoSetCompletionRoutine(Irp,
|
||
CdRomUpdateGeometryCompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
|
||
//
|
||
// Set up the SRB for read capacity.
|
||
//
|
||
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
srb->NextSrb = 0;
|
||
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
|
||
|
||
//
|
||
// Set up the CDB
|
||
//
|
||
|
||
cdb = (PCDB) &srb->Cdb[0];
|
||
cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
|
||
|
||
//
|
||
// Requests queued onto this list will be sent to the
|
||
// lower level driver during CdRomTickHandler
|
||
//
|
||
|
||
CdRomRetryRequest(fdoExtension, Irp, retryInterval, TRUE);
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
if (commonExtension->IsRemoved != NO_REMOVE) {
|
||
|
||
//
|
||
// We cannot retry the request. Fail it.
|
||
//
|
||
|
||
originalIrp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This has been bounced for a number of times. Error the
|
||
// original request.
|
||
//
|
||
|
||
originalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
RtlZeroMemory(&(fdoExtension->DiskGeometry),
|
||
sizeof(DISK_GEOMETRY));
|
||
fdoExtension->DiskGeometry.BytesPerSector = 2048;
|
||
fdoExtension->SectorShift = 11;
|
||
commonExtension->PartitionLength.QuadPart =
|
||
(LONGLONG)(0x7fffffff);
|
||
fdoExtension->DiskGeometry.MediaType = RemovableMedia;
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// Set up reasonable defaults
|
||
//
|
||
|
||
RtlZeroMemory(&(fdoExtension->DiskGeometry),
|
||
sizeof(DISK_GEOMETRY));
|
||
fdoExtension->DiskGeometry.BytesPerSector = 2048;
|
||
fdoExtension->SectorShift = 11;
|
||
commonExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
|
||
fdoExtension->DiskGeometry.MediaType = RemovableMedia;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Free resources held.
|
||
//
|
||
|
||
ExFreePool(srb->SenseInfoBuffer);
|
||
ExFreePool(srb->DataBuffer);
|
||
ExFreePool(srb);
|
||
if (Irp->MdlAddress) {
|
||
IoFreeMdl(Irp->MdlAddress);
|
||
}
|
||
IoFreeIrp(Irp);
|
||
Irp = NULL;
|
||
|
||
if (originalIrp->Tail.Overlay.Thread) {
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomUpdateGeometryCompletion: [%p] completing "
|
||
"original IRP\n", originalIrp));
|
||
|
||
} else {
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError,
|
||
"CdRomUpdateGeometryCompletion: completing irp %p which has "
|
||
"no thread\n", originalIrp));
|
||
|
||
}
|
||
|
||
{
|
||
// NOTE: should the original irp be sent down to the device object?
|
||
// it probably should if the SL_OVERRIDER_VERIFY_VOLUME flag
|
||
// is set!
|
||
KIRQL oldIrql;
|
||
PIO_STACK_LOCATION realIrpStack;
|
||
|
||
realIrpStack = IoGetCurrentIrpStackLocation(originalIrp);
|
||
oldIrql = KeRaiseIrqlToDpcLevel();
|
||
|
||
if (TEST_FLAG(realIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)) {
|
||
CdRomStartIo(DeviceObject, originalIrp);
|
||
} else {
|
||
originalIrp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
|
||
originalIrp->IoStatus.Information = 0;
|
||
CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, originalIrp);
|
||
}
|
||
KeLowerIrql(oldIrql);
|
||
}
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomUpdateCapacity(
|
||
IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension,
|
||
IN PIRP IrpToComplete,
|
||
IN OPTIONAL PKEVENT IoctlEvent
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine updates the capacity of the disk as recorded in the device extension.
|
||
It also completes the IRP given with STATUS_VERIFY_REQUIRED. This routine is called
|
||
when a media change has occurred and it is necessary to determine the capacity of the
|
||
new media prior to the next access.
|
||
|
||
Arguments:
|
||
|
||
DeviceExtension - the device to update
|
||
IrpToComplete - the request that needs to be completed when done.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = (PCOMMON_DEVICE_EXTENSION) DeviceExtension;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION) DeviceExtension;
|
||
|
||
PCDB cdb;
|
||
PIRP irp;
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
PREAD_CAPACITY_DATA capacityBuffer;
|
||
PIO_STACK_LOCATION irpStack;
|
||
PUCHAR senseBuffer;
|
||
NTSTATUS status;
|
||
|
||
irp = IoAllocateIrp((CCHAR)(commonExtension->DeviceObject->StackSize+1),
|
||
FALSE);
|
||
|
||
if (irp) {
|
||
|
||
srb = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(SCSI_REQUEST_BLOCK),
|
||
CDROM_TAG_UPDATE_CAP);
|
||
if (srb) {
|
||
capacityBuffer = ExAllocatePoolWithTag(
|
||
NonPagedPoolCacheAligned,
|
||
sizeof(READ_CAPACITY_DATA),
|
||
CDROM_TAG_UPDATE_CAP);
|
||
|
||
if (capacityBuffer) {
|
||
|
||
|
||
senseBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
SENSE_BUFFER_SIZE,
|
||
CDROM_TAG_UPDATE_CAP);
|
||
|
||
if (senseBuffer) {
|
||
|
||
irp->MdlAddress = IoAllocateMdl(capacityBuffer,
|
||
sizeof(READ_CAPACITY_DATA),
|
||
FALSE,
|
||
FALSE,
|
||
(PIRP) NULL);
|
||
|
||
if (irp->MdlAddress) {
|
||
|
||
//
|
||
// Have all resources. Set up the IRP to send for the capacity.
|
||
//
|
||
|
||
IoSetNextIrpStackLocation(irp);
|
||
irp->IoStatus.Status = STATUS_SUCCESS;
|
||
irp->IoStatus.Information = 0;
|
||
irp->Flags = 0;
|
||
irp->UserBuffer = NULL;
|
||
|
||
//
|
||
// Save the device object and retry count in a private stack location.
|
||
//
|
||
|
||
irpStack = IoGetCurrentIrpStackLocation(irp);
|
||
irpStack->DeviceObject = commonExtension->DeviceObject;
|
||
irpStack->Parameters.Others.Argument1 = (PVOID) MAXIMUM_RETRIES;
|
||
irpStack->Parameters.Others.Argument2 = (PVOID) IrpToComplete;
|
||
|
||
//
|
||
// Construct the IRP stack for the lower level driver.
|
||
//
|
||
|
||
irpStack = IoGetNextIrpStackLocation(irp);
|
||
irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
|
||
irpStack->Parameters.Scsi.Srb = srb;
|
||
IoSetCompletionRoutine(irp,
|
||
CdRomUpdateGeometryCompletion,
|
||
srb,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE);
|
||
//
|
||
// Prepare the MDL
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool(irp->MdlAddress);
|
||
|
||
|
||
//
|
||
// Set up the SRB for read capacity.
|
||
//
|
||
|
||
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
||
RtlZeroMemory(senseBuffer, SENSE_BUFFER_SIZE);
|
||
srb->CdbLength = 10;
|
||
srb->TimeOutValue = DeviceExtension->TimeOutValue;
|
||
srb->SrbStatus = srb->ScsiStatus = 0;
|
||
srb->NextSrb = 0;
|
||
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
srb->SrbFlags = DeviceExtension->SrbFlags;
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
|
||
SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
|
||
srb->DataBuffer = capacityBuffer;
|
||
srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
|
||
srb->OriginalRequest = irp;
|
||
srb->SenseInfoBuffer = senseBuffer;
|
||
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
||
|
||
//
|
||
// Set up the CDB
|
||
//
|
||
|
||
cdb = (PCDB) &srb->Cdb[0];
|
||
cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
|
||
|
||
//
|
||
// Set the return value in the IRP that will be completed
|
||
// upon completion of the read capacity.
|
||
//
|
||
|
||
IrpToComplete->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
|
||
IoMarkIrpPending(IrpToComplete);
|
||
|
||
IoCallDriver(commonExtension->LowerDeviceObject, irp);
|
||
|
||
//
|
||
// status is not checked because the completion routine for this
|
||
// IRP will always get called and it will free the resources.
|
||
//
|
||
|
||
return STATUS_PENDING;
|
||
|
||
} else {
|
||
ExFreePool(senseBuffer);
|
||
ExFreePool(capacityBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp);
|
||
}
|
||
} else {
|
||
ExFreePool(capacityBuffer);
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp);
|
||
}
|
||
} else {
|
||
ExFreePool(srb);
|
||
IoFreeIrp(irp);
|
||
}
|
||
} else {
|
||
IoFreeIrp(irp);
|
||
}
|
||
}
|
||
|
||
//
|
||
// complete the original irp with a failure.
|
||
// ISSUE-2000/07/05-henrygab - find a way to avoid failure.
|
||
//
|
||
|
||
RtlZeroMemory(&(fdoExtension->DiskGeometry),
|
||
sizeof(DISK_GEOMETRY));
|
||
fdoExtension->DiskGeometry.BytesPerSector = 2048;
|
||
fdoExtension->SectorShift = 11;
|
||
commonExtension->PartitionLength.QuadPart =
|
||
(LONGLONG)(0x7fffffff);
|
||
fdoExtension->DiskGeometry.MediaType = RemovableMedia;
|
||
|
||
IrpToComplete->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
IrpToComplete->IoStatus.Information = 0;
|
||
|
||
BAIL_OUT(IrpToComplete);
|
||
CdRomCompleteIrpAndStartNextPacketSafely(commonExtension->DeviceObject,
|
||
IrpToComplete);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomRemoveDevice(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN UCHAR Type
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is responsible for releasing any resources in use by the
|
||
cdrom driver and shutting down it's timer routine. This routine is called
|
||
when all outstanding requests have been completed and the device has
|
||
disappeared - no requests may be issued to the lower drivers.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the device object being removed
|
||
|
||
Return Value:
|
||
|
||
none - this routine may not fail
|
||
|
||
--*/
|
||
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION deviceExtension =
|
||
DeviceObject->DeviceExtension;
|
||
|
||
PCDROM_DATA cdData = deviceExtension->CommonExtension.DriverData;
|
||
|
||
PAGED_CODE();
|
||
|
||
if((Type == IRP_MN_QUERY_REMOVE_DEVICE) ||
|
||
(Type == IRP_MN_CANCEL_REMOVE_DEVICE)) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
if(cdData->DelayedRetryIrp != NULL) {
|
||
cdData->DelayedRetryInterval = 1;
|
||
CdRomTickHandler(DeviceObject);
|
||
}
|
||
|
||
CdRomDeAllocateMmcResources(DeviceObject);
|
||
|
||
if (deviceExtension->DeviceDescriptor) {
|
||
ExFreePool(deviceExtension->DeviceDescriptor);
|
||
deviceExtension->DeviceDescriptor = NULL;
|
||
}
|
||
|
||
if (deviceExtension->AdapterDescriptor) {
|
||
ExFreePool(deviceExtension->AdapterDescriptor);
|
||
deviceExtension->AdapterDescriptor = NULL;
|
||
}
|
||
|
||
if (deviceExtension->SenseData) {
|
||
ExFreePool(deviceExtension->SenseData);
|
||
deviceExtension->SenseData = NULL;
|
||
}
|
||
|
||
ClassDeleteSrbLookasideList(&deviceExtension->CommonExtension);
|
||
|
||
if(cdData->CdromInterfaceString.Buffer != NULL) {
|
||
IoSetDeviceInterfaceState(
|
||
&(cdData->CdromInterfaceString),
|
||
FALSE);
|
||
RtlFreeUnicodeString(&(cdData->CdromInterfaceString));
|
||
RtlInitUnicodeString(&(cdData->CdromInterfaceString), NULL);
|
||
}
|
||
|
||
if(cdData->VolumeInterfaceString.Buffer != NULL) {
|
||
IoSetDeviceInterfaceState(
|
||
&(cdData->VolumeInterfaceString),
|
||
FALSE);
|
||
RtlFreeUnicodeString(&(cdData->VolumeInterfaceString));
|
||
RtlInitUnicodeString(&(cdData->VolumeInterfaceString), NULL);
|
||
}
|
||
|
||
CdRomDeleteWellKnownName(DeviceObject);
|
||
|
||
ASSERT(cdData->DelayedRetryIrp == NULL);
|
||
|
||
if(Type == IRP_MN_REMOVE_DEVICE) {
|
||
|
||
if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_LOCKED_PAGES)) {
|
||
|
||
//
|
||
// unlock locked pages by locking (to get Mm pointer)
|
||
// and then unlocking twice.
|
||
//
|
||
|
||
PVOID locked;
|
||
|
||
if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_1750)) {
|
||
|
||
locked = MmLockPagableCodeSection(HitachiProcessError);
|
||
|
||
} else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_HITACHI_GD_2000)) {
|
||
|
||
locked = MmLockPagableCodeSection(HitachiProcessErrorGD2000);
|
||
|
||
} else if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_TOSHIBA_XM_3xx )) {
|
||
|
||
locked = MmLockPagableCodeSection(ToshibaProcessError);
|
||
|
||
} else {
|
||
|
||
// this is a problem!
|
||
// workaround by locking this twice, once for us and
|
||
// once for the non-existant locker from ScanForSpecial
|
||
ASSERT(!"hack flags show locked section, but none exists?");
|
||
locked = MmLockPagableCodeSection(CdRomRemoveDevice);
|
||
locked = MmLockPagableCodeSection(CdRomRemoveDevice);
|
||
|
||
|
||
}
|
||
|
||
MmUnlockPagableImageSection(locked);
|
||
MmUnlockPagableImageSection(locked);
|
||
|
||
}
|
||
|
||
//
|
||
// keep the system-wide count accurate, as
|
||
// programs use this info to know when they
|
||
// have found all the cdroms in a system.
|
||
//
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CDROM.SYS Remove device\n"));
|
||
IoGetConfigurationInformation()->CdRomCount--;
|
||
}
|
||
|
||
//
|
||
// so long, and thanks for all the fish!
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
DEVICE_TYPE
|
||
CdRomGetDeviceType(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine figures out the real device type
|
||
by checking CDVD_CAPABILITIES_PAGE
|
||
|
||
Arguments:
|
||
|
||
DeviceObject -
|
||
|
||
Return Value:
|
||
|
||
FILE_DEVICE_CD_ROM or FILE_DEVICE_DVD
|
||
|
||
|
||
--*/
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
|
||
PCDROM_DATA cdromExtension;
|
||
ULONG bufLength;
|
||
SCSI_REQUEST_BLOCK srb;
|
||
PCDB cdb;
|
||
PMODE_PARAMETER_HEADER10 modePageHeader;
|
||
PCDVD_CAPABILITIES_PAGE capPage;
|
||
ULONG capPageOffset;
|
||
DEVICE_TYPE deviceType;
|
||
NTSTATUS status;
|
||
BOOLEAN use6Byte;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// NOTE: don't cache this until understand how it affects GetMediaTypes()
|
||
//
|
||
|
||
//
|
||
// default device type
|
||
//
|
||
|
||
deviceType = FILE_DEVICE_CD_ROM;
|
||
|
||
fdoExtension = DeviceObject->DeviceExtension;
|
||
|
||
cdromExtension = fdoExtension->CommonExtension.DriverData;
|
||
|
||
use6Byte = TEST_FLAG(cdromExtension->XAFlags, XA_USE_6_BYTE);
|
||
|
||
RtlZeroMemory(&srb, sizeof(srb));
|
||
cdb = (PCDB)srb.Cdb;
|
||
|
||
//
|
||
// Build the MODE SENSE CDB. The data returned will be kept in the
|
||
// device extension and used to set block size.
|
||
//
|
||
if (use6Byte) {
|
||
|
||
bufLength = sizeof(CDVD_CAPABILITIES_PAGE) +
|
||
sizeof(MODE_PARAMETER_HEADER);
|
||
|
||
capPageOffset = sizeof(MODE_PARAMETER_HEADER);
|
||
|
||
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
|
||
cdb->MODE_SENSE.Dbd = 1;
|
||
cdb->MODE_SENSE.PageCode = MODE_PAGE_CAPABILITIES;
|
||
cdb->MODE_SENSE.AllocationLength = (UCHAR)bufLength;
|
||
srb.CdbLength = 6;
|
||
} else {
|
||
|
||
bufLength = sizeof(CDVD_CAPABILITIES_PAGE) +
|
||
sizeof(MODE_PARAMETER_HEADER10);
|
||
|
||
capPageOffset = sizeof(MODE_PARAMETER_HEADER10);
|
||
|
||
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)(bufLength >> 8);
|
||
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(bufLength >> 0);
|
||
srb.CdbLength = 10;
|
||
}
|
||
|
||
//
|
||
// Set timeout value from device extension.
|
||
//
|
||
srb.TimeOutValue = fdoExtension->TimeOutValue;
|
||
|
||
modePageHeader = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
|
||
bufLength,
|
||
CDROM_TAG_MODE_DATA);
|
||
if (modePageHeader) {
|
||
|
||
RtlZeroMemory(modePageHeader, bufLength);
|
||
|
||
status = ClassSendSrbSynchronous(
|
||
DeviceObject,
|
||
&srb,
|
||
modePageHeader,
|
||
bufLength,
|
||
FALSE);
|
||
|
||
if (NT_SUCCESS(status) ||
|
||
(status == STATUS_DATA_OVERRUN) ||
|
||
(status == STATUS_BUFFER_OVERFLOW)
|
||
) {
|
||
|
||
capPage = (PCDVD_CAPABILITIES_PAGE) (((PUCHAR) modePageHeader) + capPageOffset);
|
||
|
||
if ((capPage->PageCode == MODE_PAGE_CAPABILITIES) &&
|
||
(capPage->DVDROMRead || capPage->DVDRRead ||
|
||
capPage->DVDRAMRead || capPage->DVDRWrite ||
|
||
capPage->DVDRAMWrite)) {
|
||
|
||
deviceType = FILE_DEVICE_DVD;
|
||
}
|
||
}
|
||
ExFreePool (modePageHeader);
|
||
}
|
||
|
||
return deviceType;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomCreateWellKnownName(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
/*++
|
||
|
||
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
|
||
|
||
--*/
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
PCDROM_DATA cdromData = commonExtension->DriverData;
|
||
|
||
UNICODE_STRING unicodeLinkName;
|
||
WCHAR wideLinkName[64];
|
||
PWCHAR savedName;
|
||
|
||
LONG cdromNumber = fdoExtension->DeviceNumber;
|
||
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// if already linked, assert then return
|
||
//
|
||
|
||
if (cdromData->WellKnownName.Buffer != NULL) {
|
||
|
||
TraceLog((CdromDebugError,
|
||
"CdRomCreateWellKnownName: link already exists %p\n",
|
||
cdromData->WellKnownName.Buffer));
|
||
ASSERT(FALSE);
|
||
return STATUS_UNSUCCESSFUL;
|
||
|
||
}
|
||
|
||
//
|
||
// find an unused CdRomNN to link to
|
||
//
|
||
|
||
do {
|
||
|
||
swprintf(wideLinkName, L"\\DosDevices\\CdRom%d", cdromNumber);
|
||
RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
|
||
status = IoCreateSymbolicLink(&unicodeLinkName,
|
||
&(commonExtension->DeviceName));
|
||
|
||
cdromNumber++;
|
||
|
||
} while((status == STATUS_OBJECT_NAME_COLLISION) ||
|
||
(status == STATUS_OBJECT_NAME_EXISTS));
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomCreateWellKnownName: Error %lx linking %wZ to "
|
||
"device %wZ\n",
|
||
status,
|
||
&unicodeLinkName,
|
||
&(commonExtension->DeviceName)));
|
||
return status;
|
||
|
||
}
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomCreateWellKnownName: successfully linked %wZ "
|
||
"to device %wZ\n",
|
||
&unicodeLinkName,
|
||
&(commonExtension->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) {
|
||
IoDeleteSymbolicLink(&unicodeLinkName);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(savedName,
|
||
unicodeLinkName.Buffer,
|
||
unicodeLinkName.MaximumLength);
|
||
|
||
RtlInitUnicodeString(&(cdromData->WellKnownName), savedName);
|
||
|
||
//
|
||
// the name was saved and the link created
|
||
//
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
CdRomDeleteWellKnownName(
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
PCDROM_DATA cdromData = commonExtension->DriverData;
|
||
|
||
if(cdromData->WellKnownName.Buffer != NULL) {
|
||
|
||
IoDeleteSymbolicLink(&(cdromData->WellKnownName));
|
||
ExFreePool(cdromData->WellKnownName.Buffer);
|
||
cdromData->WellKnownName.Buffer = NULL;
|
||
cdromData->WellKnownName.Length = 0;
|
||
cdromData->WellKnownName.MaximumLength = 0;
|
||
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomGetDeviceParameter (
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PWSTR ParameterName,
|
||
IN OUT PULONG ParameterValue
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
retrieve a devnode registry parameter
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Cdrom Device Object
|
||
|
||
ParameterName - parameter name to look up
|
||
|
||
ParameterValuse - default parameter value
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
NTSTATUS status;
|
||
HANDLE deviceParameterHandle;
|
||
RTL_QUERY_REGISTRY_TABLE queryTable[2];
|
||
ULONG defaultParameterValue;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// open the given parameter
|
||
//
|
||
status = IoOpenDeviceRegistryKey(fdoExtension->LowerPdo,
|
||
PLUGPLAY_REGKEY_DRIVER,
|
||
KEY_READ,
|
||
&deviceParameterHandle);
|
||
|
||
if(NT_SUCCESS(status)) {
|
||
|
||
RtlZeroMemory(queryTable, sizeof(queryTable));
|
||
|
||
defaultParameterValue = *ParameterValue;
|
||
|
||
queryTable->Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED;
|
||
queryTable->Name = ParameterName;
|
||
queryTable->EntryContext = ParameterValue;
|
||
queryTable->DefaultType = REG_NONE;
|
||
queryTable->DefaultData = NULL;
|
||
queryTable->DefaultLength = 0;
|
||
|
||
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
||
(PWSTR) deviceParameterHandle,
|
||
queryTable,
|
||
NULL,
|
||
NULL);
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
*ParameterValue = defaultParameterValue;
|
||
}
|
||
|
||
//
|
||
// close what we open
|
||
//
|
||
ZwClose(deviceParameterHandle);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // CdRomGetDeviceParameter
|
||
|
||
|
||
NTSTATUS
|
||
CdRomSetDeviceParameter (
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PWSTR ParameterName,
|
||
IN ULONG ParameterValue
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
save a devnode registry parameter
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Cdrom Device Object
|
||
|
||
ParameterName - parameter name
|
||
|
||
ParameterValuse - parameter value
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
{
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
NTSTATUS status;
|
||
HANDLE deviceParameterHandle;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// open the given parameter
|
||
//
|
||
status = IoOpenDeviceRegistryKey(fdoExtension->LowerPdo,
|
||
PLUGPLAY_REGKEY_DRIVER,
|
||
KEY_READ | KEY_WRITE,
|
||
&deviceParameterHandle);
|
||
|
||
if(NT_SUCCESS(status)) {
|
||
|
||
status = RtlWriteRegistryValue(
|
||
RTL_REGISTRY_HANDLE,
|
||
(PWSTR) deviceParameterHandle,
|
||
ParameterName,
|
||
REG_DWORD,
|
||
&ParameterValue,
|
||
sizeof (ParameterValue));
|
||
|
||
//
|
||
// close what we open
|
||
//
|
||
ZwClose(deviceParameterHandle);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // CdromSetDeviceParameter
|
||
|
||
|
||
VOID
|
||
CdRomPickDvdRegion(
|
||
IN PDEVICE_OBJECT Fdo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
pick a default dvd region
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Cdrom Device Object
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
||
PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
|
||
|
||
//
|
||
// 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;
|
||
|
||
IO_STATUS_BLOCK ioStatus;
|
||
ULONG bufferLen;
|
||
UCHAR mediaRegion;
|
||
ULONG pickDvdRegion;
|
||
ULONG defaultDvdRegion;
|
||
ULONG dvdRegion;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ((pickDvdRegion = InterlockedExchange(&cddata->PickDvdRegion, 0)) == 0) {
|
||
|
||
//
|
||
// it was non-zero, so either another thread will do this, or
|
||
// we no longer need to pick a region
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
//
|
||
// short-circuit if license agreement violated
|
||
//
|
||
|
||
if (cddata->DvdRpc0LicenseFailure) {
|
||
TraceLog((CdromDebugWarning,
|
||
"DVD License failure. Refusing to pick a region\n"));
|
||
InterlockedExchange(&cddata->PickDvdRegion, 0);
|
||
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(&cddata->PickDvdRegion, pickDvdRegion);
|
||
return;
|
||
}
|
||
|
||
if (cddata->DvdRpc0Device && cddata->Rpc0RetryRegistryCallback) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): now retrying RPC0 callback\n",
|
||
Fdo));
|
||
|
||
//
|
||
// get the registry settings again
|
||
//
|
||
|
||
ioStatus.Status = CdRomGetRpc0Settings(Fdo);
|
||
|
||
if (ioStatus.Status == STATUS_LICENSE_VIOLATION) {
|
||
|
||
//
|
||
// if this is the returned error, then
|
||
// the routine should have set this!
|
||
//
|
||
|
||
ASSERT(cddata->DvdRpc0LicenseFailure);
|
||
cddata->DvdRpc0LicenseFailure = 1;
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): "
|
||
"setting to fail all dvd ioctls due to CSS licensing "
|
||
"failure.\n", Fdo));
|
||
|
||
pickDvdRegion = 0;
|
||
goto getout;
|
||
|
||
}
|
||
|
||
//
|
||
// get the device region, again
|
||
//
|
||
|
||
copyProtectKey = (PDVD_COPY_PROTECT_KEY)dvdReadStructure;
|
||
RtlZeroMemory(copyProtectKey, bufferLen);
|
||
copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
|
||
copyProtectKey->KeyType = DvdGetRpcKey;
|
||
|
||
//
|
||
// Build a request for READ_KEY
|
||
//
|
||
|
||
ClassSendDeviceIoControlSynchronous(
|
||
IOCTL_DVD_READ_KEY,
|
||
Fdo,
|
||
copyProtectKey,
|
||
DVD_RPC_KEY_LENGTH,
|
||
DVD_RPC_KEY_LENGTH,
|
||
FALSE,
|
||
&ioStatus);
|
||
|
||
if (!NT_SUCCESS(ioStatus.Status)) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion: Unable to get "
|
||
"device RPC data (%x)\n", ioStatus.Status));
|
||
pickDvdRegion = 0;
|
||
goto getout;
|
||
}
|
||
|
||
//
|
||
// now that we have gotten the device's RPC data,
|
||
// we have set the device extension to usable data.
|
||
// no need to call back into this section of code again
|
||
//
|
||
|
||
cddata->Rpc0RetryRegistryCallback = 0;
|
||
|
||
|
||
rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData;
|
||
|
||
//
|
||
// TypeCode of zero means that no region has been set.
|
||
//
|
||
|
||
if (rpcKey->TypeCode != 0) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): DVD Region already "
|
||
"chosen\n", Fdo));
|
||
pickDvdRegion = 0;
|
||
goto getout;
|
||
}
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): must choose initial DVD "
|
||
" Region\n", Fdo));
|
||
}
|
||
|
||
|
||
|
||
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
|
||
//
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomPickDvdRegion (%p): Getting Copyright Descriptor\n",
|
||
Fdo));
|
||
|
||
ClassSendDeviceIoControlSynchronous(
|
||
IOCTL_DVD_READ_STRUCTURE,
|
||
Fdo,
|
||
dvdReadStructure,
|
||
sizeof(DVD_READ_STRUCTURE),
|
||
sizeof (DVD_DESCRIPTOR_HEADER) +
|
||
sizeof(DVD_COPYRIGHT_DESCRIPTOR),
|
||
FALSE,
|
||
&ioStatus
|
||
);
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomPickDvdRegion (%p): Got Copyright Descriptor %x\n",
|
||
Fdo, ioStatus.Status));
|
||
|
||
if ((NT_SUCCESS(ioStatus.Status)) &&
|
||
(dvdCopyRight->CopyrightProtectionType == 0x01)
|
||
) {
|
||
|
||
//
|
||
// keep the media region bitmap around
|
||
// a 1 means ok to play
|
||
//
|
||
|
||
if (dvdCopyRight->RegionManagementInformation == 0xff) {
|
||
TraceLog((CdromDebugError,
|
||
"CdRomPickDvdRegion (%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",
|
||
Fdo));
|
||
dvdCopyRight->RegionManagementInformation = 0;
|
||
}
|
||
|
||
|
||
mediaRegion = ~dvdCopyRight->RegionManagementInformation;
|
||
|
||
} else {
|
||
|
||
//
|
||
// could be media, can't set the device region
|
||
//
|
||
|
||
if (!cddata->DvdRpc0Device) {
|
||
|
||
//
|
||
// can't automatically pick a default region on a rpc2 drive
|
||
// without media, so just exit
|
||
//
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): failed to auto-choose "
|
||
"a region due to status %x getting copyright "
|
||
"descriptor\n", Fdo, ioStatus.Status));
|
||
goto getout;
|
||
|
||
} else {
|
||
|
||
//
|
||
// for an RPC0 drive, we can try to pick a region for
|
||
// the drive
|
||
//
|
||
|
||
mediaRegion = 0x0;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// 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
|
||
//
|
||
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomPickDvdRegion (%p): Getting RpcKey\n",
|
||
Fdo));
|
||
ClassSendDeviceIoControlSynchronous(
|
||
IOCTL_DVD_READ_KEY,
|
||
Fdo,
|
||
copyProtectKey,
|
||
DVD_RPC_KEY_LENGTH,
|
||
DVD_RPC_KEY_LENGTH,
|
||
FALSE,
|
||
&ioStatus
|
||
);
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomPickDvdRegion (%p): Got RpcKey %x\n",
|
||
Fdo, ioStatus.Status));
|
||
|
||
if (!NT_SUCCESS(ioStatus.Status)) {
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): failed to get RpcKey from "
|
||
"a DVD Device\n", Fdo));
|
||
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) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): not picking a region since "
|
||
"it is already chosen\n", Fdo));
|
||
goto getout;
|
||
}
|
||
|
||
if (rpcKey->UserResetsAvailable <= 1) {
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): not picking a region since "
|
||
"only one change remains\n", Fdo));
|
||
goto getout;
|
||
}
|
||
|
||
defaultDvdRegion = 0;
|
||
|
||
//
|
||
// the proppage dvd class installer sets
|
||
// this key based upon the system locale
|
||
//
|
||
|
||
CdRomGetDeviceParameter (
|
||
Fdo,
|
||
DVD_DEFAULT_REGION,
|
||
&defaultDvdRegion
|
||
);
|
||
|
||
if (defaultDvdRegion > DVD_MAX_REGION) {
|
||
|
||
//
|
||
// the registry has a bogus default
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): registry has a bogus default "
|
||
"region value of %x\n", Fdo, 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));
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): Choice #1: media matches "
|
||
"drive's default, chose region %x\n", Fdo, dvdRegion));
|
||
|
||
|
||
} else if (mediaRegion) {
|
||
|
||
//
|
||
// second choice:
|
||
// pick the lowest region number
|
||
// from the media
|
||
//
|
||
|
||
UCHAR mask;
|
||
|
||
mask = 1;
|
||
dvdRegion = 0;
|
||
while (mediaRegion && !dvdRegion) {
|
||
|
||
//
|
||
// pick the lowest bit
|
||
//
|
||
dvdRegion = mediaRegion & mask;
|
||
mask <<= 1;
|
||
}
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): Choice #2: choosing lowest "
|
||
"media region %x\n", Fdo, dvdRegion));
|
||
|
||
} else if (defaultDvdRegion) {
|
||
|
||
//
|
||
// third choice:
|
||
// default dvd region from the dvd class installer
|
||
//
|
||
|
||
dvdRegion = (1 << (defaultDvdRegion - 1));
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): Choice #3: using default "
|
||
"region for this install %x\n", Fdo, 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
|
||
//
|
||
TraceLog((CdromDebugWarning,
|
||
"CdRomPickDvdRegion (%p): Choice #4: failed to choose "
|
||
"a media region\n", Fdo));
|
||
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
|
||
//
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomPickDvdRegion (%p): Sending new Rpc Key to region %x\n",
|
||
Fdo, dvdRegion));
|
||
|
||
ClassSendDeviceIoControlSynchronous(
|
||
IOCTL_DVD_SEND_KEY2,
|
||
Fdo,
|
||
copyProtectKey,
|
||
DVD_SET_RPC_KEY_LENGTH,
|
||
0,
|
||
FALSE,
|
||
&ioStatus);
|
||
TraceLog((CdromDebugTrace,
|
||
"CdRomPickDvdRegion (%p): Sent new Rpc Key %x\n",
|
||
Fdo, ioStatus.Status));
|
||
|
||
if (!NT_SUCCESS(ioStatus.Status)) {
|
||
DebugPrint ((1, "CdRomPickDvdRegion (%p): unable to set dvd initial "
|
||
" region code (%p)\n", Fdo, ioStatus.Status));
|
||
} else {
|
||
DebugPrint ((1, "CdRomPickDvdRegion (%p): Successfully set dvd "
|
||
"initial region\n", Fdo));
|
||
pickDvdRegion = 0;
|
||
}
|
||
|
||
getout:
|
||
if (dvdReadStructure) {
|
||
ExFreePool (dvdReadStructure);
|
||
}
|
||
|
||
//
|
||
// update the new PickDvdRegion value
|
||
//
|
||
|
||
InterlockedExchange(&cddata->PickDvdRegion, pickDvdRegion);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomRetryRequest(
|
||
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
||
IN PIRP Irp,
|
||
IN ULONG Delay,
|
||
IN BOOLEAN ResendIrp
|
||
)
|
||
{
|
||
PCDROM_DATA cdData;
|
||
KIRQL oldIrql;
|
||
|
||
if(Delay == 0) {
|
||
return CdRomRerunRequest(FdoExtension, Irp, ResendIrp);
|
||
}
|
||
|
||
cdData = FdoExtension->CommonExtension.DriverData;
|
||
|
||
KeAcquireSpinLock(&(cdData->DelayedRetrySpinLock), &oldIrql);
|
||
|
||
ASSERT(cdData->DelayedRetryIrp == NULL);
|
||
ASSERT(cdData->DelayedRetryInterval == 0);
|
||
|
||
cdData->DelayedRetryIrp = Irp;
|
||
cdData->DelayedRetryInterval = Delay;
|
||
cdData->DelayedRetryResend = ResendIrp;
|
||
|
||
KeReleaseSpinLock(&(cdData->DelayedRetrySpinLock), oldIrql);
|
||
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
CdRomRerunRequest(
|
||
IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
|
||
IN OPTIONAL PIRP Irp,
|
||
IN BOOLEAN ResendIrp
|
||
)
|
||
{
|
||
if(ResendIrp) {
|
||
return IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject,
|
||
Irp);
|
||
} else {
|
||
KIRQL oldIrql;
|
||
|
||
oldIrql = KeRaiseIrqlToDpcLevel();
|
||
CdRomStartIo(FdoExtension->DeviceObject, Irp);
|
||
KeLowerIrql(oldIrql);
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
}
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine just checks for media change sense/asc/ascq and
|
||
also for other events, such as bus resets. this is used to
|
||
determine if the device behaviour has changed, to allow for
|
||
read and write operations to be allowed and/or disallowed.
|
||
|
||
Arguments:
|
||
|
||
ISSUE-2000/3/30-henrygab - not fully doc'd
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
NTSTATUS
|
||
CdRomMmcErrorHandler(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PSCSI_REQUEST_BLOCK Srb,
|
||
OUT PNTSTATUS Status,
|
||
OUT PBOOLEAN Retry
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
||
BOOLEAN queryCapabilities = FALSE;
|
||
|
||
if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
|
||
|
||
PCDROM_DATA cddata = (PCDROM_DATA)commonExtension->DriverData;
|
||
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
||
|
||
//
|
||
// the following sense keys could indicate a change in
|
||
// capabilities.
|
||
//
|
||
|
||
//
|
||
// we used to expect this to be serialized, and only hit from our
|
||
// own routine. we now allow some requests to continue during our
|
||
// processing of the capabilities update in order to allow
|
||
// IoReadPartitionTable() to succeed.
|
||
//
|
||
|
||
switch (senseBuffer->SenseKey & 0xf) {
|
||
|
||
case SCSI_SENSE_NOT_READY: {
|
||
if (senseBuffer->AdditionalSenseCode ==
|
||
SCSI_ADSENSE_NO_MEDIA_IN_DEVICE) {
|
||
|
||
if (cddata->Mmc.WriteAllowed) {
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: media removed, writes will be "
|
||
"failed until new media detected\n"));
|
||
}
|
||
|
||
// NOTE - REF #0002
|
||
cddata->Mmc.WriteAllowed = FALSE;
|
||
} else
|
||
if ((senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
|
||
(senseBuffer->AdditionalSenseCodeQualifier ==
|
||
SCSI_SENSEQ_BECOMING_READY)) {
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: media becoming ready, "
|
||
"SHOULD notify shell of change time by sending "
|
||
"GESN request immediately!\n"));
|
||
}
|
||
break;
|
||
} // end SCSI_SENSE_NOT_READY
|
||
|
||
case SCSI_SENSE_UNIT_ATTENTION: {
|
||
switch (senseBuffer->AdditionalSenseCode) {
|
||
case SCSI_ADSENSE_MEDIUM_CHANGED: {
|
||
|
||
//
|
||
// always update if the medium may have changed
|
||
//
|
||
|
||
// NOTE - REF #0002
|
||
cddata->Mmc.WriteAllowed = FALSE;
|
||
InterlockedCompareExchange(&(cddata->Mmc.UpdateState),
|
||
CdromMmcUpdateRequired,
|
||
CdromMmcUpdateComplete);
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: media change detected, need to "
|
||
"update drive capabilities\n"));
|
||
break;
|
||
|
||
} // end SCSI_ADSENSE_MEDIUM_CHANGED
|
||
|
||
case SCSI_ADSENSE_BUS_RESET: {
|
||
|
||
// NOTE - REF #0002
|
||
cddata->Mmc.WriteAllowed = FALSE;
|
||
InterlockedCompareExchange(&(cddata->Mmc.UpdateState),
|
||
CdromMmcUpdateRequired,
|
||
CdromMmcUpdateComplete);
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: bus reset detected, need to "
|
||
"update drive capabilities\n"));
|
||
break;
|
||
|
||
} // end SCSI_ADSENSE_BUS_RESET
|
||
|
||
case SCSI_ADSENSE_OPERATOR_REQUEST: {
|
||
|
||
BOOLEAN b = FALSE;
|
||
|
||
switch (senseBuffer->AdditionalSenseCodeQualifier) {
|
||
case SCSI_SENSEQ_MEDIUM_REMOVAL: {
|
||
|
||
//
|
||
// eject notification currently handled by classpnp
|
||
//
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: Eject requested by user\n"));
|
||
*Retry = TRUE;
|
||
*Status = STATUS_DEVICE_BUSY;
|
||
break;
|
||
}
|
||
|
||
case SCSI_SENSEQ_WRITE_PROTECT_DISABLE:
|
||
b = TRUE;
|
||
case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
|
||
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: Write protect %s requested "
|
||
"by user\n",
|
||
(b ? "disable" : "enable")));
|
||
*Retry = TRUE;
|
||
*Status = STATUS_DEVICE_BUSY;
|
||
// NOTE - REF #0002
|
||
cddata->Mmc.WriteAllowed = FALSE;
|
||
InterlockedCompareExchange(&(cddata->Mmc.UpdateState),
|
||
CdromMmcUpdateRequired,
|
||
CdromMmcUpdateComplete);
|
||
|
||
}
|
||
|
||
} // end of AdditionalSenseCodeQualifier switch
|
||
|
||
|
||
break;
|
||
|
||
} // end SCSI_ADSENSE_OPERATOR_REQUEST
|
||
|
||
default: {
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: Unit attention %02x/%02x\n",
|
||
senseBuffer->AdditionalSenseCode,
|
||
senseBuffer->AdditionalSenseCodeQualifier));
|
||
break;
|
||
}
|
||
|
||
} // end of AdditionSenseCode switch
|
||
break;
|
||
|
||
} // end SCSI_SENSE_UNIT_ATTENTION
|
||
|
||
case SCSI_SENSE_ILLEGAL_REQUEST: {
|
||
if (senseBuffer->AdditionalSenseCode ==
|
||
SCSI_ADSENSE_WRITE_PROTECT) {
|
||
|
||
if (cddata->Mmc.WriteAllowed) {
|
||
KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
|
||
"CdromErrorHandler: media was writable, but "
|
||
"failed request with WRITE_PROTECT error...\n"));
|
||
}
|
||
// NOTE - REF #0002
|
||
// do not update all the capabilities just because
|
||
// we can't write to the disc.
|
||
cddata->Mmc.WriteAllowed = FALSE;
|
||
}
|
||
break;
|
||
} // end SCSI_SENSE_ILLEGAL_REQUEST
|
||
|
||
} // end of SenseKey switch
|
||
|
||
} // end of SRB_STATUS_AUTOSENSE_VALID
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks for a device-specific error handler
|
||
and calls it if it exists. This allows multiple drives
|
||
that require their own error handler to co-exist.
|
||
|
||
--*/
|
||
VOID
|
||
CdRomErrorHandler(
|
||
PDEVICE_OBJECT DeviceObject,
|
||
PSCSI_REQUEST_BLOCK Srb,
|
||
NTSTATUS *Status,
|
||
BOOLEAN *Retry
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
||
PCDROM_DATA cddata = (PCDROM_DATA)commonExtension->DriverData;
|
||
PSENSE_DATA sense = Srb->SenseInfoBuffer;
|
||
|
||
if ((Srb->SenseInfoBufferLength >=
|
||
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA,AdditionalSenseCodeQualifier)) &&
|
||
TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
|
||
|
||
//
|
||
// Many non-WHQL certified drives (mostly CD-RW) return
|
||
// 2/4/0 when they have no media instead of the obvious
|
||
// choice of:
|
||
//
|
||
// SCSI_SENSE_NOT_READY/SCSI_ADSENSE_NO_MEDIA_IN_DEVICE
|
||
//
|
||
// These drives should not pass WHQL certification due
|
||
// to this discrepency.
|
||
//
|
||
// However, we have to retry on 2/4/0 (Not ready, LUN not ready,
|
||
// no info) and also 3/2/0 (no seek complete).
|
||
//
|
||
// These conditions occur when the shell tries to examine an
|
||
// injected CD (e.g. for autoplay) before the CD is spun up.
|
||
//
|
||
// The drive should be returning an ASCQ of SCSI_SENSEQ_BECOMING_READY
|
||
// (0x01) in order to comply with WHQL standards.
|
||
//
|
||
// The default retry timeout of one second is acceptable to balance
|
||
// these discrepencies. don't modify the status, though....
|
||
//
|
||
|
||
if (((sense->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) &&
|
||
(sense->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
|
||
(sense->AdditionalSenseCodeQualifier == SCSI_SENSEQ_CAUSE_NOT_REPORTABLE)
|
||
) {
|
||
|
||
*Retry = TRUE;
|
||
|
||
} else if (((sense->SenseKey & 0xf) == SCSI_SENSE_MEDIUM_ERROR) &&
|
||
(sense->AdditionalSenseCode == 0x2) &&
|
||
(sense->AdditionalSenseCodeQualifier == 0x0)
|
||
) {
|
||
|
||
*Retry = TRUE;
|
||
|
||
} else if ((sense->AdditionalSenseCode == 0x57) &&
|
||
(sense->AdditionalSenseCodeQualifier == 0x00)
|
||
) {
|
||
|
||
//
|
||
// UNABLE_TO_RECOVER_TABLE_OF_CONTENTS
|
||
// the Matshita CR-585 returns this for all read commands
|
||
// on blank CD-R and CD-RW media, and we need to handle
|
||
// this for READ_CD detection ability.
|
||
//
|
||
|
||
*Retry = FALSE;
|
||
*Status = STATUS_UNRECOGNIZED_MEDIA;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// tail recursion in both cases takes no stack
|
||
//
|
||
|
||
if (cddata->ErrorHandler) {
|
||
cddata->ErrorHandler(DeviceObject, Srb, Status, Retry);
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for a shutdown and flush IRPs.
|
||
These are sent by the system before it actually shuts
|
||
down or when the file system does a flush.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to device object to being shutdown by system.
|
||
|
||
Irp - IRP involved.
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
NTSTATUS
|
||
CdRomShutdownFlush(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
IoMarkIrpPending(Irp);
|
||
IoStartPacket(DeviceObject, Irp, NULL, NULL);
|
||
return STATUS_PENDING;
|
||
|
||
}
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called for intermediate work a shutdown or
|
||
flush IRPs would need to do. We just want to free our resources
|
||
and return STATUS_MORE_PROCESSING_REQUIRED.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - NULL?
|
||
|
||
Irp - IRP to free
|
||
|
||
Context - NULL
|
||
|
||
Return Value:
|
||
|
||
NT Status
|
||
|
||
--*/
|
||
NTSTATUS
|
||
CdRomShutdownFlushCompletion(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PIRP NewIrp,
|
||
IN PIRP OriginalIrp
|
||
)
|
||
{
|
||
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
|
||
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
|
||
PIO_STACK_LOCATION originalIrpStack;
|
||
ULONG_PTR iteration;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
ASSERT(OriginalIrp);
|
||
|
||
originalIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp);
|
||
|
||
//
|
||
// always use a new irp so we can call
|
||
// CdRomCompleteIrpAndStartNextPacketSafely() from this routine.
|
||
//
|
||
|
||
if (NewIrp != NULL) {
|
||
status = NewIrp->IoStatus.Status;
|
||
IoFreeIrp(NewIrp);
|
||
NewIrp = NULL;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
BAIL_OUT(OriginalIrp);
|
||
goto SafeExit;
|
||
}
|
||
|
||
//
|
||
// the current irpstack saves the counter which states
|
||
// what part of the multi-part shutdown or flush we are in.
|
||
//
|
||
|
||
iteration = (ULONG_PTR)originalIrpStack->Parameters.Others.Argument1;
|
||
iteration++;
|
||
originalIrpStack->Parameters.Others.Argument1 = (PVOID)iteration;
|
||
|
||
switch (iteration) {
|
||
case 2:
|
||
if (originalIrpStack->MajorFunction != IRP_MJ_SHUTDOWN) {
|
||
//
|
||
// then we don't want to send the unlock command
|
||
// the incrementing of the state was done above.
|
||
// return the completion routine's result.
|
||
//
|
||
return CdRomShutdownFlushCompletion(Fdo, NULL, OriginalIrp);
|
||
}
|
||
// else fall through....
|
||
|
||
case 1: {
|
||
|
||
PIRP newIrp = NULL;
|
||
PSCSI_REQUEST_BLOCK newSrb = NULL;
|
||
PCDB newCdb = NULL;
|
||
PIO_STACK_LOCATION newIrpStack = NULL;
|
||
ULONG isRemoved;
|
||
|
||
newIrp = IoAllocateIrp((CCHAR)(Fdo->StackSize+1), FALSE);
|
||
if (newIrp == NULL) {
|
||
BAIL_OUT(OriginalIrp);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto SafeExit;
|
||
}
|
||
newSrb = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(SCSI_REQUEST_BLOCK),
|
||
CDROM_TAG_SRB);
|
||
if (newSrb == NULL) {
|
||
IoFreeIrp(newIrp);
|
||
BAIL_OUT(OriginalIrp);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto SafeExit;
|
||
}
|
||
|
||
//
|
||
// ClassIoComplete will free the SRB, but we need a routine
|
||
// that will free the irp. then just call ClassSendAsync,
|
||
// and don't care about the return value, since the completion
|
||
// routine will be called anyways.
|
||
//
|
||
|
||
IoSetNextIrpStackLocation(newIrp);
|
||
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
|
||
newIrpStack->DeviceObject = Fdo;
|
||
IoSetCompletionRoutine(newIrp,
|
||
CdRomShutdownFlushCompletion,
|
||
OriginalIrp,
|
||
TRUE, TRUE, TRUE);
|
||
IoSetNextIrpStackLocation(newIrp);
|
||
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
|
||
newIrpStack->DeviceObject = Fdo;
|
||
|
||
//
|
||
// setup the request
|
||
//
|
||
|
||
RtlZeroMemory(newSrb, sizeof(SCSI_REQUEST_BLOCK));
|
||
newCdb = (PCDB)(newSrb->Cdb);
|
||
|
||
newSrb->QueueTag = SP_UNTAGGED;
|
||
newSrb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
||
newSrb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
||
|
||
//
|
||
// tell classpnp not to call StartNextPacket()
|
||
//
|
||
|
||
newSrb->SrbFlags = SRB_FLAGS_DONT_START_NEXT_PACKET;
|
||
|
||
if (iteration == 1) {
|
||
|
||
//
|
||
// first synchronize the cache
|
||
//
|
||
|
||
newSrb->TimeOutValue = fdoExtension->TimeOutValue * 4;
|
||
newSrb->CdbLength = 10;
|
||
newCdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
|
||
|
||
} else if (iteration == 2) {
|
||
|
||
//
|
||
// then unlock the medium
|
||
//
|
||
|
||
ASSERT( originalIrpStack->MajorFunction == IRP_MJ_SHUTDOWN );
|
||
|
||
newSrb->TimeOutValue = fdoExtension->TimeOutValue;
|
||
newSrb->CdbLength = 6;
|
||
newCdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
|
||
newCdb->MEDIA_REMOVAL.Prevent = FALSE;
|
||
|
||
}
|
||
|
||
|
||
isRemoved = ClassAcquireRemoveLock(Fdo, newIrp);
|
||
if (isRemoved) {
|
||
IoFreeIrp(newIrp);
|
||
ExFreePool(newSrb);
|
||
ClassReleaseRemoveLock(Fdo, newIrp);
|
||
BAIL_OUT(OriginalIrp);
|
||
status = STATUS_DEVICE_DOES_NOT_EXIST;
|
||
goto SafeExit;
|
||
}
|
||
ClassSendSrbAsynchronous(Fdo, newSrb, newIrp, NULL, 0, FALSE);
|
||
break;
|
||
}
|
||
|
||
case 3: {
|
||
|
||
PSCSI_REQUEST_BLOCK srb;
|
||
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(OriginalIrp);
|
||
|
||
//
|
||
// forward this request to the device appropriately,
|
||
// don't use this completion routine anymore...
|
||
//
|
||
|
||
srb = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(SCSI_REQUEST_BLOCK),
|
||
CDROM_TAG_SRB);
|
||
if (srb == NULL) {
|
||
BAIL_OUT(OriginalIrp);
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto SafeExit;
|
||
}
|
||
|
||
RtlZeroMemory(srb, SCSI_REQUEST_BLOCK_SIZE);
|
||
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
||
srb->TimeOutValue = fdoExtension->TimeOutValue * 4;
|
||
srb->QueueTag = SP_UNTAGGED;
|
||
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
||
srb->SrbFlags = fdoExtension->SrbFlags;
|
||
srb->CdbLength = 0;
|
||
srb->OriginalRequest = OriginalIrp;
|
||
|
||
if (originalIrpStack->MajorFunction == IRP_MJ_SHUTDOWN) {
|
||
srb->Function = SRB_FUNCTION_SHUTDOWN;
|
||
} else {
|
||
srb->Function = SRB_FUNCTION_FLUSH;
|
||
}
|
||
|
||
//
|
||
// Set up IoCompletion routine address.
|
||
//
|
||
|
||
IoSetCompletionRoutine(OriginalIrp,
|
||
ClassIoComplete,
|
||
srb,
|
||
TRUE, TRUE, TRUE);
|
||
|
||
//
|
||
// Set the retry count to zero.
|
||
//
|
||
|
||
originalIrpStack->Parameters.Others.Argument4 = (PVOID) 0;
|
||
|
||
//
|
||
// Get next stack location and set major function code.
|
||
//
|
||
|
||
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
||
|
||
//
|
||
// Set up SRB for execute scsi request.
|
||
// Save SRB address in next stack for port driver.
|
||
//
|
||
|
||
nextIrpStack->Parameters.Scsi.Srb = srb;
|
||
|
||
//
|
||
// Call the port driver to process the request.
|
||
//
|
||
|
||
IoCallDriver(commonExtension->LowerDeviceObject, OriginalIrp);
|
||
|
||
break;
|
||
|
||
}
|
||
default: {
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
} // end switch
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
SafeExit:
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
OriginalIrp->IoStatus.Status = status;
|
||
CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp);
|
||
}
|
||
|
||
//
|
||
// always return STATUS_MORE_PROCESSING_REQUIRED, so noone else tries
|
||
// to access the new irp that we free'd....
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // end CdromShutdownFlush()
|
||
|
||
|
||
VOID
|
||
CdromFakePartitionInfo(
|
||
IN PCOMMON_DEVICE_EXTENSION CommonExtension,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
||
ULONG ioctl = currentIrpStack->Parameters.DeviceIoControl.IoControlCode;
|
||
PVOID systemBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
|
||
ASSERT(systemBuffer);
|
||
|
||
if ((ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT) &&
|
||
(ioctl != IOCTL_DISK_GET_DRIVE_LAYOUT_EX) &&
|
||
(ioctl != IOCTL_DISK_GET_PARTITION_INFO) &&
|
||
(ioctl != IOCTL_DISK_GET_PARTITION_INFO_EX)) {
|
||
TraceLog((CdromDebugError,
|
||
"CdromFakePartitionInfo: unhandled ioctl %x\n", ioctl));
|
||
Irp->IoStatus.Status = STATUS_INTERNAL_ERROR;
|
||
Irp->IoStatus.Information = 0;
|
||
CdRomCompleteIrpAndStartNextPacketSafely(CommonExtension->DeviceObject,
|
||
Irp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// nothing to fail from this point on, so set the size appropriately
|
||
// and set irp's status to success.
|
||
//
|
||
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromFakePartitionInfo: incoming ioctl %x\n", ioctl));
|
||
|
||
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
switch (ioctl) {
|
||
case IOCTL_DISK_GET_DRIVE_LAYOUT:
|
||
Irp->IoStatus.Information = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION,
|
||
PartitionEntry[1]);
|
||
RtlZeroMemory(systemBuffer, FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION,
|
||
PartitionEntry[1]));
|
||
break;
|
||
case IOCTL_DISK_GET_DRIVE_LAYOUT_EX:
|
||
Irp->IoStatus.Information = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX,
|
||
PartitionEntry[1]);
|
||
RtlZeroMemory(systemBuffer, FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX,
|
||
PartitionEntry[1]));
|
||
break;
|
||
case IOCTL_DISK_GET_PARTITION_INFO:
|
||
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
|
||
RtlZeroMemory(systemBuffer, sizeof(PARTITION_INFORMATION));
|
||
break;
|
||
case IOCTL_DISK_GET_PARTITION_INFO_EX:
|
||
Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
|
||
RtlZeroMemory(systemBuffer, sizeof(PARTITION_INFORMATION_EX));
|
||
break;
|
||
default:
|
||
ASSERT(!"Invalid ioctl should not have reached this point\n");
|
||
break;
|
||
}
|
||
|
||
//
|
||
// if we are getting the drive layout, then we need to start by
|
||
// adding some of the non-partition stuff that says we have
|
||
// exactly one partition available.
|
||
//
|
||
|
||
|
||
if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT) {
|
||
|
||
PDRIVE_LAYOUT_INFORMATION layout;
|
||
layout = (PDRIVE_LAYOUT_INFORMATION)systemBuffer;
|
||
layout->PartitionCount = 1;
|
||
layout->Signature = 1;
|
||
systemBuffer = (PVOID)(layout->PartitionEntry);
|
||
ioctl = IOCTL_DISK_GET_PARTITION_INFO;
|
||
|
||
} else if (ioctl == IOCTL_DISK_GET_DRIVE_LAYOUT_EX) {
|
||
|
||
PDRIVE_LAYOUT_INFORMATION_EX layoutEx;
|
||
layoutEx = (PDRIVE_LAYOUT_INFORMATION_EX)systemBuffer;
|
||
layoutEx->PartitionStyle = PARTITION_STYLE_MBR;
|
||
layoutEx->PartitionCount = 1;
|
||
layoutEx->Mbr.Signature = 1;
|
||
systemBuffer = (PVOID)(layoutEx->PartitionEntry);
|
||
ioctl = IOCTL_DISK_GET_PARTITION_INFO_EX;
|
||
|
||
}
|
||
|
||
//
|
||
// NOTE: the local var 'ioctl' is now modified to either EX or
|
||
// non-EX version. the local var 'systemBuffer' is now pointing
|
||
// to the partition information structure.
|
||
//
|
||
|
||
if (ioctl == IOCTL_DISK_GET_PARTITION_INFO) {
|
||
|
||
PPARTITION_INFORMATION partitionInfo;
|
||
partitionInfo = (PPARTITION_INFORMATION)systemBuffer;
|
||
partitionInfo->RewritePartition = FALSE;
|
||
partitionInfo->RecognizedPartition = TRUE;
|
||
partitionInfo->PartitionType = PARTITION_FAT32;
|
||
partitionInfo->BootIndicator = FALSE;
|
||
partitionInfo->HiddenSectors = 0;
|
||
partitionInfo->StartingOffset.QuadPart = 0;
|
||
partitionInfo->PartitionLength = CommonExtension->PartitionLength;
|
||
partitionInfo->PartitionNumber = 0;
|
||
|
||
} else {
|
||
|
||
PPARTITION_INFORMATION_EX partitionInfo;
|
||
partitionInfo = (PPARTITION_INFORMATION_EX)systemBuffer;
|
||
partitionInfo->PartitionStyle = PARTITION_STYLE_MBR;
|
||
partitionInfo->RewritePartition = FALSE;
|
||
partitionInfo->Mbr.RecognizedPartition = TRUE;
|
||
partitionInfo->Mbr.PartitionType = PARTITION_FAT32;
|
||
partitionInfo->Mbr.BootIndicator = FALSE;
|
||
partitionInfo->Mbr.HiddenSectors = 0;
|
||
partitionInfo->StartingOffset.QuadPart = 0;
|
||
partitionInfo->PartitionLength = CommonExtension->PartitionLength;
|
||
partitionInfo->PartitionNumber = 0;
|
||
|
||
}
|
||
TraceLog((CdromDebugWarning,
|
||
"CdromFakePartitionInfo: finishing ioctl %x\n",
|
||
currentIrpStack->Parameters.DeviceIoControl.IoControlCode));
|
||
|
||
//
|
||
// complete the irp
|
||
//
|
||
|
||
CdRomCompleteIrpAndStartNextPacketSafely(CommonExtension->DeviceObject,
|
||
Irp);
|
||
return;
|
||
|
||
}
|
||
|