diff --git a/reactos/drivers/storage/class/cdrom_new/cdrom.c b/reactos/drivers/storage/class/cdrom_new/cdrom.c
new file mode 100644
index 00000000000..aaaa8ee9c7d
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/cdrom.c
@@ -0,0 +1,7136 @@
+/*--
+
+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 "initguid.h"
+
+#include "ntddcdvd.h"
+#include "classpnp.h"
+
+#include "ntddstor.h"
+#include "cdrom.h"
+
+#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
+NTAPI
+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.
+ //
+ DbgPrint("add device\n");
+
+ 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;
+
+ //
+ // 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)
+ ) {
+
+ USHORT 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.
+ //
+
+ ExInterlockedPushEntrySList(&(cdData->Mmc.DelayedIrps),
+ (PSINGLE_LIST_ENTRY)&(Irp->Tail.Overlay.DriverContext[0]),
+ &(cdData->Mmc.DelayedLock));
+
+ queueDepth = ExQueryDepthSList(&(cdData->Mmc.DelayedIrps));
+ 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 && 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;
+ }
+
+
+ ULONG a, b;
+
+ a = max(sizeof(DVD_DESCRIPTOR_HEADER) +
+ sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+ sizeof(DVD_READ_STRUCTURE)
+ );
+ b = max(DVD_RPC_KEY_LENGTH,
+ DVD_SET_RPC_KEY_LENGTH
+ );
+ bufferLen = max(a, b);
+
+ 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;
+
+}
+
+
diff --git a/reactos/drivers/storage/class/cdrom_new/cdrom.h b/reactos/drivers/storage/class/cdrom_new/cdrom.h
new file mode 100644
index 00000000000..7b1a865dcf3
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/cdrom.h
@@ -0,0 +1,823 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ cdromp.h
+
+Abstract:
+
+ Private header file for cdrom.sys. This contains private
+ structure and function declarations as well as constant
+ values which do not need to be exported.
+
+Author:
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#ifndef __CDROMP_H__
+#define __CDROMP_H__
+
+#include "ntddmmc.h"
+#include "trace.h"
+
+extern CLASSPNP_SCAN_FOR_SPECIAL_INFO CdromHackItems[];
+
+typedef enum {
+ CdromDebugError = 0, // always printed
+ CdromDebugWarning = 1, // set bit 0x00000001 in nt!kd_cdrom_mask
+ CdromDebugTrace = 2, // set bit 0x00000002 in nt!kd_cdrom_mask
+ CdromDebugInfo = 3, // set bit 0x00000004 in nt!kd_cdrom_mask
+#if 0
+ CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask
+ CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask
+ CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask
+ CdromDebug = z, // set bit 0x00000000 in nt!kd_cdrom_mask
+#endif
+ CdromDebugFeatures = 32 // set bit 0x80000000 in nt!kd_cdrom_mask
+};
+
+#define CDROM_GET_CONFIGURATION_TIMEOUT (0x4)
+
+#define CDROM_HACK_DEC_RRD (0x00000001)
+#define CDROM_HACK_FUJITSU_FMCD_10x (0x00000002)
+#define CDROM_HACK_HITACHI_1750 (0x00000004)
+#define CDROM_HACK_HITACHI_GD_2000 (0x00000008)
+#define CDROM_HACK_TOSHIBA_SD_W1101 (0x00000010)
+#define CDROM_HACK_TOSHIBA_XM_3xx (0x00000020)
+#define CDROM_HACK_NEC_CDDA (0x00000040)
+#define CDROM_HACK_PLEXTOR_CDDA (0x00000080)
+#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT (0x00000100)
+#define CDROM_HACK_FORCE_READ_CD_DETECTION (0x00000200)
+#define CDROM_HACK_READ_CD_SUPPORTED (0x00000400)
+#define CDROM_HACK_LOCKED_PAGES (0x80000000) // not a valid flag to save
+
+#define CDROM_HACK_VALID_FLAGS (0x000007ff)
+#define CDROM_HACK_INVALID_FLAGS (~CDROM_HACK_VALID_FLAGS)
+
+
+typedef struct _XA_CONTEXT {
+
+ //
+ // Pointer to the device object.
+ //
+
+ PDEVICE_OBJECT DeviceObject;
+
+ //
+ // Pointer to the original request when
+ // a mode select must be sent.
+ //
+
+ PIRP OriginalRequest;
+
+ //
+ // Pointer to the mode select srb.
+ //
+
+ PSCSI_REQUEST_BLOCK Srb;
+} XA_CONTEXT, *PXA_CONTEXT;
+
+typedef struct _ERROR_RECOVERY_DATA {
+ MODE_PARAMETER_HEADER Header;
+ MODE_PARAMETER_BLOCK BlockDescriptor;
+ MODE_READ_RECOVERY_PAGE ReadRecoveryPage;
+} ERROR_RECOVERY_DATA, *PERROR_RECOVERY_DATA;
+
+typedef struct _ERROR_RECOVERY_DATA10 {
+ MODE_PARAMETER_HEADER10 Header10;
+ MODE_PARAMETER_BLOCK BlockDescriptor10;
+ MODE_READ_RECOVERY_PAGE ReadRecoveryPage10;
+} ERROR_RECOVERY_DATA10, *PERROR_RECOVERY_DATA10;
+
+//
+// CdRom specific addition to device extension.
+//
+
+typedef struct _CDROM_DRIVER_EXTENSION {
+ ULONG InterlockedCdRomCounter;
+ PVOID Reserved[3];
+} CDROM_DRIVER_EXTENSION, *PCDROM_DRIVER_EXTENSION;
+
+#define CdromMmcUpdateComplete 0
+#define CdromMmcUpdateRequired 1
+#define CdromMmcUpdateStarted 2
+
+typedef struct _CDROM_MMC_EXTENSION {
+
+ ULONG IsMmc; // allow quick checks
+ ULONG WriteAllowed;
+
+ LONG UpdateState;
+
+ SLIST_HEADER DelayedIrps; // irps delayed due to
+ KSPIN_LOCK DelayedLock; // lock for delayed irps
+
+ PIO_WORKITEM CapabilitiesWorkItem;
+ PIRP CapabilitiesIrp;
+ PMDL CapabilitiesMdl;
+ PGET_CONFIGURATION_HEADER CapabilitiesBuffer;
+ ULONG CapabilitiesBufferSize;
+ KEVENT CapabilitiesEvent;
+ SCSI_REQUEST_BLOCK CapabilitiesSrb;
+
+} CDROM_MMC_EXTENSION, *PCDROM_MMC_EXTENSION;
+
+
+#define CDROM_DRIVER_EXTENSION_ID CdRomAddDevice
+
+typedef struct _CDROM_DATA {
+
+ //
+ // Pointer to the cdrom driver extension
+ //
+
+ PCDROM_DRIVER_EXTENSION DriverExtension;
+
+
+ //
+ // These bits allow detection of when to requery the
+ // drive's capabilities.
+ //
+
+ CDROM_MMC_EXTENSION Mmc;
+
+ //
+ // hack flags for ScanForSpecial routines
+ //
+
+ ULONG_PTR HackFlags;
+
+ //
+ // the error handling routines need to be per-device,
+ // not per-driver....
+ //
+
+ PCLASS_ERROR ErrorHandler;
+
+ //
+ // Indicates whether an audio play operation
+ // is currently being performed.
+ // Only thing this does is prevent reads and
+ // toc requests while playing audio.
+ //
+
+ BOOLEAN PlayActive;
+
+ //
+ // Indicates whether the blocksize used for user data
+ // is 2048 or 2352.
+ //
+
+ BOOLEAN RawAccess;
+
+ //
+ // Indicates that this is a DEC RRD cdrom.
+ // This drive requires software to fix responses
+ // from the faulty firmware
+ //
+
+ BOOLEAN IsDecRrd;
+
+ //
+ // This points to an irp which needs to be delayed for a bit before a
+ // retry can be attempted. The interval counter is set by the deferring
+ // routine and will be decremented to zero in the tick handler. Once
+ // the counter goes to zero the irp will be issued again.
+ // DelayedRetryResend controls whether the irp is resent to the lower
+ // driver (TRUE) or reissued into the startio routine (FALSE)
+ //
+
+ BOOLEAN DelayedRetryResend;
+
+ PIRP DelayedRetryIrp;
+
+ ULONG DelayedRetryInterval;
+
+ KSPIN_LOCK DelayedRetrySpinLock;
+
+ //
+ // indicate we need to pick a default dvd region
+ // for the user if we can
+ //
+
+ ULONG PickDvdRegion;
+
+ //
+ // The interface strings registered for this device.
+ //
+
+ UNICODE_STRING CdromInterfaceString;
+ UNICODE_STRING VolumeInterfaceString;
+
+ //
+ // The well known name link for this device.
+ //
+
+ UNICODE_STRING WellKnownName;
+
+ //
+ // Indicates whether 6 or 10 bytes mode sense/select
+ // should be used
+ //
+
+ ULONG XAFlags;
+
+ //
+ // keep track of what type of DVD device we are
+ //
+
+ BOOLEAN DvdRpc0Device;
+ BOOLEAN DvdRpc0LicenseFailure;
+ UCHAR Rpc0SystemRegion; // bitmask, one means prevent play
+ UCHAR Rpc0SystemRegionResetCount;
+
+ ULONG Rpc0RetryRegistryCallback; // one until initial region choosen
+
+ KMUTEX Rpc0RegionMutex;
+
+ //
+ // Storage for the error recovery page. This is used
+ // as an easy method to switch block sizes.
+ //
+ // NOTE - doubly unnamed structs just aren't very clean looking code - this
+ // should get cleaned up at some point in the future.
+ //
+
+ union {
+ ERROR_RECOVERY_DATA;
+ ERROR_RECOVERY_DATA10;
+ };
+
+} CDROM_DATA, *PCDROM_DATA;
+
+#define DEVICE_EXTENSION_SIZE sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(CDROM_DATA)
+#define SCSI_CDROM_TIMEOUT 10
+#define SCSI_CHANGER_BONUS_TIMEOUT 10
+#define HITACHI_MODE_DATA_SIZE 12
+#define MODE_DATA_SIZE 64
+#define RAW_SECTOR_SIZE 2352
+#define COOKED_SECTOR_SIZE 2048
+#define CDROM_SRB_LIST_SIZE 4
+
+#define PLAY_ACTIVE(x) (((PCDROM_DATA)(x->CommonExtension.DriverData))->PlayActive)
+
+#define MSF_TO_LBA(Minutes,Seconds,Frames) \
+ (ULONG)((60 * 75 * (Minutes)) + (75 * (Seconds)) + ((Frames) - 150))
+
+#define LBA_TO_MSF(Lba,Minutes,Seconds,Frames) \
+{ \
+ (Minutes) = (UCHAR)(Lba / (60 * 75)); \
+ (Seconds) = (UCHAR)((Lba % (60 * 75)) / 75); \
+ (Frames) = (UCHAR)((Lba % (60 * 75)) % 75); \
+}
+
+#define DEC_TO_BCD(x) (((x / 10) << 4) + (x % 10))
+
+//
+// Define flags for XA, CDDA, and Mode Select/Sense
+//
+
+#define XA_USE_6_BYTE 0x01
+#define XA_USE_10_BYTE 0x02
+
+#define XA_NOT_SUPPORTED 0x10
+#define XA_USE_READ_CD 0x20
+#define XA_PLEXTOR_CDDA 0x40
+#define XA_NEC_CDDA 0x80
+
+//
+// Sector types for READ_CD
+//
+
+#define ANY_SECTOR 0
+#define CD_DA_SECTOR 1
+#define YELLOW_MODE1_SECTOR 2
+#define YELLOW_MODE2_SECTOR 3
+#define FORM2_MODE1_SECTOR 4
+#define FORM2_MODE2_SECTOR 5
+
+#define MAX_COPY_PROTECT_AGID 4
+
+#ifdef ExAllocatePool
+#undef ExAllocatePool
+#define ExAllocatePool #assert(FALSE)
+#endif
+
+#define CDROM_TAG_GET_CONFIG 'cCcS' // "ScCc" - ioctl GET_CONFIGURATION
+#define CDROM_TAG_DC_EVENT 'ECcS' // "ScCE" - device control synch event
+#define CDROM_TAG_FEATURE 'FCcS' // "ScCF" - allocated by CdRomGetConfiguration(), free'd by caller
+#define CDROM_TAG_DISK_GEOM 'GCcS' // "ScCG" - disk geometry buffer
+#define CDROM_TAG_HITACHI_ERROR 'HCcS' // "ScCH" - hitachi error buffer
+#define CDROM_TAG_SENSE_INFO 'ICcS' // "ScCI" - sense info buffers
+#define CDROM_TAG_POWER_IRP 'iCcS' // "ScCi" - irp for power request
+#define CDROM_TAG_SRB 'SCcS' // "ScCS" - srb allocation
+#define CDROM_TAG_STRINGS 'sCcS' // "ScCs" - assorted string data
+#define CDROM_TAG_MODE_DATA 'MCcS' // "ScCM" - mode data buffer
+#define CDROM_TAG_READ_CAP 'PCcS' // "ScCP" - read capacity buffer
+#define CDROM_TAG_PLAY_ACTIVE 'pCcS' // "ScCp" - play active checks
+#define CDROM_TAG_SUB_Q 'QCcS' // "ScCQ" - read sub q buffer
+#define CDROM_TAG_RAW 'RCcS' // "ScCR" - raw mode read buffer
+#define CDROM_TAG_TOC 'TCcS' // "ScCT" - read toc buffer
+#define CDROM_TAG_TOSHIBA_ERROR 'tCcS' // "ScCt" - toshiba error buffer
+#define CDROM_TAG_DEC_ERROR 'dCcS' // "ScCt" - DEC error buffer
+#define CDROM_TAG_UPDATE_CAP 'UCcS' // "ScCU" - update capacity path
+#define CDROM_TAG_VOLUME 'VCcS' // "ScCV" - volume control buffer
+#define CDROM_TAG_VOLUME_INT 'vCcS' // "ScCv" - volume control buffer
+
+#define DVD_TAG_READ_STRUCTURE 'SVcS' // "ScVS" - used for dvd structure reads
+#define DVD_TAG_READ_KEY 'kVcS' // "ScVk" - read buffer for dvd key
+#define DVD_TAG_SEND_KEY 'KVcS' // "ScVK" - write buffer for dvd key
+#define DVD_TAG_RPC2_CHECK 'sVcS' // "ScVs" - read buffer for dvd/rpc2 check
+#define DVD_TAG_DVD_REGION 'tVcS' // "ScVt" - read buffer for rpc2 check
+#define DVD_TAG_SECURITY 'XVcS' // "ScVX" - security descriptor
+
+
+#define CDROM_SUBKEY_NAME (L"CdRom") // store new settings here
+#define CDROM_READ_CD_NAME (L"ReadCD") // READ_CD support previously detected
+#define CDROM_NON_MMC_DRIVE_NAME (L"NonMmc") // MMC commands hang
+//
+// DVD Registry Value Names for RPC0 Device
+//
+#define DVD_DEFAULT_REGION (L"DefaultDvdRegion") // this is init. by the dvd class installer
+#define DVD_CURRENT_REGION (L"DvdR")
+#define DVD_REGION_RESET_COUNT (L"DvdRCnt")
+#define DVD_MAX_REGION_RESET_COUNT 2
+#define DVD_MAX_REGION 8
+
+
+
+#define BAIL_OUT(Irp) \
+ DebugPrint((2, "Cdrom: [%p] Bailing with status " \
+ " %lx at line %x file %s\n", \
+ (Irp), (Irp)->IoStatus.Status, \
+ __LINE__, __FILE__))
+
+
+/*++
+
+Routine Description:
+
+ This routine grabs an extra remove lock using a local variable
+ for a unique tag. It then completes the irp in question, and
+ the just-acquired removelock guarantees that it is still safe
+ to call IoStartNextPacket(). When that finishes, we release
+ the newly acquired RemoveLock and return.
+
+Arguments:
+
+ DeviceObject - the device object for the StartIo queue
+ Irp - the request we are completing
+
+Return Value:
+
+ None
+
+Notes:
+
+ This is implemented as an inline function to allow the compiler
+ to optimize this as either a function call or as actual inline code.
+
+ This routine will not work with IoXxxRemoveLock() calls, as the
+ behavior is different. ClassXxxRemoveLock() calls succeed until
+ the remove has completed, while IoXxxRemoveLock() calls fail as
+ soon as the call to IoReleaseRemoveLockAndWait() has been called.
+ The Class version allows this routine to work in a safe manner.
+
+ replaces the following two lines:
+ IoStartNextPacket(DeviceObject, FALSE);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ and raises irql as needed to call IoStartNextPacket()
+
+--*/
+static inline
+VOID
+CdRomCompleteIrpAndStartNextPacketSafely(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ UCHAR uniqueAddress;
+ KIRQL oldIrql = KeGetCurrentIrql();
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_CD_ROM_INCREMENT);
+
+ if (oldIrql > DISPATCH_LEVEL) {
+ ASSERT(!"Cannot call IoStartNextPacket at raised IRQL!");
+ } else if (oldIrql < DISPATCH_LEVEL) {
+ KeRaiseIrqlToDpcLevel();
+ } else { // (oldIrql == DISPATCH_LEVEL)
+ NOTHING;
+ }
+
+ IoStartNextPacket(DeviceObject, FALSE);
+
+ if (oldIrql > DISPATCH_LEVEL) {
+ ASSERT(!"Cannot call IoStartNextPacket at raised IRQL!");
+ } else if (oldIrql < DISPATCH_LEVEL) {
+ KeLowerIrql(oldIrql);
+ } else { // (oldIrql == DISPATCH_LEVEL)
+ NOTHING;
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress);
+
+
+ return;
+}
+
+VOID
+CdRomDeviceControlDvdReadStructure(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+CdRomDeviceControlDvdEndSession(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+CdRomDeviceControlDvdStartSessionReadKey(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+CdRomDeviceControlDvdSendKey(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+VOID
+CdRomUnload(
+ IN PDRIVER_OBJECT DriverObject
+ );
+
+NTSTATUS
+CdRomAddDevice(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT Pdo
+ );
+
+NTSTATUS
+CdRomOpenClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+CdRomReadWriteVerification(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+CdRomSwitchMode(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG SectorSize,
+ IN PIRP OriginalRequest
+ );
+
+NTSTATUS
+CdRomDeviceControlDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+CdRomDeviceControlCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+CdRomSetVolumeIntermediateCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+CdRomSwitchModeCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+CdRomXACompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+CdRomClassIoctlCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+VOID
+CdRomStartIo(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+CdRomTickHandler(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+CdRomUpdateCapacity(
+ IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension,
+ IN PIRP IrpToComplete,
+ IN OPTIONAL PKEVENT IoctlEvent
+ );
+
+NTSTATUS
+CdRomCreateDeviceObject(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT Pdo
+ );
+
+VOID
+ScanForSpecialHandler(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ ULONG_PTR HackFlags
+ );
+
+VOID
+ScanForSpecial(
+ PDEVICE_OBJECT DeviceObject
+ );
+
+BOOLEAN
+CdRomIsPlayActive(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+CdRomErrorHandler(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ NTSTATUS *Status,
+ BOOLEAN *Retry
+ );
+
+VOID
+HitachiProcessErrorGD2000(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ NTSTATUS *Status,
+ BOOLEAN *Retry
+ );
+
+VOID
+HitachiProcessError(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ NTSTATUS *Status,
+ BOOLEAN *Retry
+ );
+
+VOID
+ToshibaProcessError(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ NTSTATUS *Status,
+ BOOLEAN *Retry
+ );
+
+NTSTATUS
+ToshibaProcessErrorCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ );
+
+VOID
+CdRomCreateNamedEvent(
+ IN PFUNCTIONAL_DEVICE_EXTENSION DeviceExtension,
+ IN ULONG DeviceNumber
+ );
+
+NTSTATUS
+CdRomInitDevice(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+NTSTATUS
+CdRomStartDevice(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+NTSTATUS
+CdRomStopDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR Type
+ );
+
+NTSTATUS
+CdRomRemoveDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR Type
+ );
+
+NTSTATUS
+CdRomDvdEndAllSessionsCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+CdRomDvdReadDiskKeyCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+DEVICE_TYPE
+CdRomGetDeviceType(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+CdRomCreateWellKnownName(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+CdRomDeleteWellKnownName(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+CdRomGetDeviceParameter (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PWSTR ParameterName,
+ IN OUT PULONG ParameterValue
+ );
+
+NTSTATUS
+CdRomSetDeviceParameter (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PWSTR ParameterName,
+ IN ULONG ParameterValue
+ );
+
+VOID
+CdRomPickDvdRegion (
+ IN PDEVICE_OBJECT Fdo
+);
+
+NTSTATUS
+CdRomRetryRequest(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PIRP Irp,
+ IN ULONG Delay,
+ IN BOOLEAN ResendIrp
+ );
+
+NTSTATUS
+CdRomRerunRequest(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN OPTIONAL PIRP Irp,
+ IN BOOLEAN ResendIrp
+ );
+
+NTSTATUS
+CdRomGetRpc0Settings(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+NTSTATUS
+CdRomSetRpc0Settings(
+ IN PDEVICE_OBJECT Fdo,
+ IN UCHAR NewRegion
+ );
+
+NTSTATUS
+CdRomShutdownFlush(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+////////////////////////////////////////////////////////////////////////////////
+
+VOID
+CdRomIsDeviceMmcDevice(
+ IN PDEVICE_OBJECT Fdo,
+ OUT PBOOLEAN IsMmc
+ );
+
+NTSTATUS
+CdRomMmcErrorHandler(
+ IN PDEVICE_OBJECT Fdo,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ OUT PNTSTATUS Status,
+ OUT PBOOLEAN Retry
+ );
+
+PVOID
+CdRomFindFeaturePage(
+ IN PGET_CONFIGURATION_HEADER FeatureBuffer,
+ IN ULONG Length,
+ IN FEATURE_NUMBER Feature
+ );
+
+NTSTATUS
+CdRomGetConfiguration(
+ IN PDEVICE_OBJECT Fdo,
+ OUT PGET_CONFIGURATION_HEADER *Buffer,
+ OUT PULONG BytesReturned,
+ IN FEATURE_NUMBER StartingFeature,
+ IN ULONG RequestedType
+ );
+
+VOID
+CdRomUpdateMmcDriveCapabilities(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context // RESERVED == NULL
+ );
+
+VOID
+CdRomFindProfileInProfiles(
+ IN PFEATURE_DATA_PROFILE_LIST ProfileHeader,
+ IN FEATURE_PROFILE_TYPE ProfileToFind,
+ OUT PBOOLEAN Exists
+ );
+
+NTSTATUS
+CdRomAllocateMmcResources(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+VOID
+CdRomDeAllocateMmcResources(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+VOID
+CdromFakePartitionInfo(
+ IN PCOMMON_DEVICE_EXTENSION CommonExtension,
+ IN PIRP Irp
+ );
+
+VOID
+CdRomInterpretReadCapacity(
+ IN PDEVICE_OBJECT Fdo,
+ IN PREAD_CAPACITY_DATA ReadCapacityBuffer
+ );
+
+NTSTATUS
+CdRomShutdownFlushCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PIRP Context
+ );
+
+VOID
+CdRompFlushDelayedList(
+ IN PDEVICE_OBJECT Fdo,
+ IN PCDROM_MMC_EXTENSION MmcData,
+ IN NTSTATUS Status,
+ IN BOOLEAN CalledFromWorkItem
+ );
+
+#endif // __CDROMP_H__
+
+
diff --git a/reactos/drivers/storage/class/cdrom_new/cdrom.inf b/reactos/drivers/storage/class/cdrom_new/cdrom.inf
new file mode 100644
index 00000000000..0cf7858e0d3
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/cdrom.inf
@@ -0,0 +1,344 @@
+;
+; cdrom.inf -- INF file for installing CDROM drives
+;
+; Copyright (c) 1993-1997, Microsoft Corporation
+
+[Version]
+Signature="$WINDOWS NT$"
+Class=CDROM
+ClassGuid={4D36E965-E325-11CE-BFC1-08002BE10318}
+Provider=%DDK_SAMPLE%
+DriverVer=03/15/2001,5.1.2462.0
+CatalogFile=ddk_sample.cat
+
+[cdaudio_copyfiles]
+cdaudio.sys
+
+[changer_copyfiles]
+changer.sys
+
+[cdrom_copyfiles]
+cdrom.sys
+redbook.sys
+
+[storprop_copyfiles]
+storprop.dll
+
+[DestinationDirs]
+cdrom_copyfiles = 12
+cdaudio_copyfiles = 12
+changer_copyfiles = 12
+storprop_copyfiles = 11
+
+[Manufacturer]
+%ATAPI_CHGR% = atapi_chgr
+%CHINON% = chinon_cdrom
+%DENON% = denon_cdrom
+%FUJITSU% = fujitsu_cdrom
+%HITACHI% = hitachi_cdrom
+%HP% = hp_cdrom
+%MITSUMI% = mitsumi_cdrom
+%NEC% = nec_cdrom
+%OTI% = oti_cdrom
+%PIONEER% = pioneer_cdrom
+%WEARNES% = wearnes_cdrom
+%GenManufacturer% = cdrom_device
+
+[atapi_chgr]
+%NecChanger_devdesc% = changer_install,IDE\CdRomNEC_CD-ROM_DRIVE:251____________________
+%NecChanger_devdesc% = changer_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:251
+%NecChanger_devdesc% = changer_install,IDE\CdRomNEC_CD-ROM_DRIVE:253____________________
+%NecChanger_devdesc% = changer_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:253
+%NecChanger_devdesc% = changer_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:252
+%AlpsChanger_devdesc% = changer_install,IDE\CdRomALPS_DC544______________________________
+%TorisanChanger_devdesc% = changer_install,IDE\CdRomTORiSAN_CD-ROM_CDR-C3G__________________
+%TorisanChanger_devdesc% = changer_install,SCSI\CdRomTORiSAN_CD-ROM_CDR-C3G__
+%TorisanChanger_devdesc% = changer_install,IDE\CdRomTORiSAN_CD-ROM_CDR_C36__________________
+%PanasonicChanger_devdesc% = changer_install,IDE\CdRomMATSHITA_RD-DRC001-M____________________
+%PanasonicChanger_devdesc% = changer_install,IDE\CdRomMATSHITA_RD-DRC002-S____________________
+%PanasonicChanger_devdesc% = changer_install,SCSI\CdRomNAKAMICHMJ-5.16_________
+
+[fujitsu_cdrom]
+%fujitsu_devdesc% = cdaudio_install,SCSI\CdRomFUJITSU_
+
+[chinon_cdrom]
+%chinon_devdesc% = cdaudio_install,SCSI\CdRomCHINON__
+
+[denon_cdrom]
+%denon_devdesc% = cdaudio_install,SCSI\CdRomDENON___
+
+[hp_cdrom]
+%hp_devdesc% = cdaudio_install,SCSI\CdRomHP______C4324/C4325_____
+
+[hitachi_cdrom]
+%hitachi_devdesc% = cdaudio_install,SCSI\CdRomHITACHI_CDR-3650/1650S__
+%hitachi_devdesc% = cdaudio_install,SCSI\CdRomHITACHI_CDR-1750S_______
+
+[mitsumi_cdrom]
+%Mitsumi_cdrom_devdesc% = mitsumi_install,IDE\CdRomMITSUMI_CD-ROM________!A________________
+
+[nec_cdrom]
+%NecMultispin_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:38_
+%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE_4_M
+%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:80_
+%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:82_
+%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:83_
+%NecIntersect_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:84_
+%NecMultispin_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:841
+%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:400
+%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:401
+%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:500
+%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:501
+%NecOem_devdesc% = cdaudio_install,SCSI\CdRomNEC_____CD-ROM_DRIVE:900
+
+[oti_cdrom]
+%oti_devdesc% = cdaudio_install,IDE\CdRomOTI_DOLPHIN_8001_IDE____________________
+
+[pioneer_cdrom]
+%pioneer_devdesc% = cdaudio_install,SCSI\CdRomPIONEER_CD-ROM_DRM-600__
+%pioneer_devdesc% = cdaudio_install,SCSI\CdRomPIONEER_CD-ROM_DRM-600x_
+
+[wearnes_cdrom]
+%wearnes_devdesc% = cdaudio_install,IDE\CdRomWEARNES_
+
+
+[cdrom_device]
+;;
+;; if none of the above matched, then only cdrom.sys is required for this drive
+;;
+%gencdrom_devdesc% = cdrom_install,SCSI\WormPIONEER_CD-WO_DR-R504X__
+%gencdrom_devdesc% = cdrom_install,SCSI\WormSONY____CD-R___CDU920S__
+%gencdrom_devdesc% = cdrom_install,SCSI\WormSONY____CD-R___CDU948S__
+%gencdrom_devdesc% = cdrom_install,GenCdRom
+
+;;
+;; Use to add filter drivers for the device
+;;
+
+[cdaudio_addreg]
+HKR,,"UpperFilters",0x00010008,"cdaudio"
+
+[changer_addreg]
+HKR,,"UpperFilters",0x00010008,"changer"
+
+[mitsumi_addreg]
+HKR,,"FriendlyName",,%Mitsumi_Generic_FriendlyName%
+
+;;
+;; more addreg sections
+;;
+
+[dvd_property_provider_AddReg]
+HKR,,EnumPropPages32,,"storprop.dll,DvdPropPageProvider"
+
+[autorun_addreg]
+;;
+;; The AutoRunAlwaysDisable key is only for use when the hardware cannot
+;; accepts TEST_UNIT_READY commands. Disabling 'AutoRun' or including
+;; devices in this list will prevent removable media services from being
+;; able to properly handle these devices.
+;;
+HKLM,"System\CurrentControlSet\Services\cdrom","AutoRun",0x00010003,1
+HKLM,"System\CurrentControlSet\Services\cdrom","AutoRunAlwaysDisable",\
+ 0x00010000,\
+ "NEC MBR-7 ", \
+ "NEC MBR-7.4 ", \
+ "PIONEER CHANGR DRM-1804X", \
+ "PIONEER CD-ROM DRM-6324X", \
+ "PIONEER CD-ROM DRM-624X ", \
+ "TORiSAN CD-ROM CDR_C36"
+
+;;
+;; Use to disable synchronous transfers to this device. Sync transfers will
+;; always be turned off by default in this INF for any cdrom-type device
+;;
+[nosync_addreg]
+HKR,,"DefaultRequestFlags",0x00010001,8
+
+;;
+;; Installation section for cdaudio. Sets cdrom as the service and adds
+;; cdaudio as an upper filter
+;;
+
+[cdaudio_install]
+CopyFiles=cdaudio_copyfiles,cdrom_copyfiles,storprop_copyfiles
+AddReg=dvd_property_provider_AddReg
+
+[cdaudio_install.HW]
+AddReg=nosync_addreg,cdaudio_addreg
+
+[cdaudio_install.Services]
+AddService=cdrom,0x00000002,cdrom_ServiceInstallSection
+AddService=cdaudio,,cdaudio_ServiceInstallSection
+AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection
+
+;;
+;; Installation section for changer
+;;
+
+[changer_install]
+CopyFiles=changer_copyfiles,cdrom_copyfiles,storprop_copyfiles
+AddReg=dvd_property_provider_AddReg
+
+[changer_install.HW]
+AddReg=changer_addreg
+
+[changer_install.Services]
+AddService=cdrom,0x00000002,cdrom_ServiceInstallSection
+AddService=changer,,changer_ServiceInstallSection
+AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection
+
+;;
+;; Installation section for mitsumi.
+;;
+
+[mitsumi_install]
+CopyFiles=cdrom_copyfiles,storprop_copyfiles
+AddReg=dvd_property_provider_AddReg
+
+[mitsumi_install.HW]
+AddReg=nosync_addreg,mitsumi_addreg
+
+[mitsumi_install.Services]
+AddService=cdrom,0x00000002,cdrom_ServiceInstallSection
+AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection
+
+;;
+;; Installation section for generic cdrom.
+;;
+
+[cdrom_install]
+CopyFiles=cdrom_copyfiles,storprop_copyfiles
+AddReg=dvd_property_provider_AddReg
+
+[cdrom_install.HW]
+AddReg=nosync_addreg
+
+[cdrom_install.Services]
+AddService=cdrom,0x00000002,cdrom_ServiceInstallSection
+AddService=redbook,,redbook_ServiceInstallSection,redbook_InstallEventLogSection
+
+;;
+;; Service install sections for cdrom and cdaudio
+;;
+
+[cdrom_ServiceInstallSection]
+DisplayName = %cdrom_ServiceDesc%
+ServiceType = 1
+StartType = 1
+ErrorControl = 1
+ServiceBinary = %12%\cdrom.sys
+LoadOrderGroup = SCSI CDROM Class
+AddReg=autorun_addreg
+
+[cdaudio_ServiceInstallSection]
+DisplayName = %cdaudio_ServiceDesc%
+ServiceType = 1
+StartType = 1
+ErrorControl = 1
+ServiceBinary = %12%\cdaudio.sys
+LoadOrderGroup = Pnp Filter
+
+[changer_ServiceInstallSection]
+DisplayName = %changer_ServiceDesc%
+ServiceType = 1
+StartType = 1
+ErrorControl = 1
+ServiceBinary = %12%\changer.sys
+LoadOrderGroup = Pnp Filter
+
+[redbook_ServiceInstallSection]
+DisplayName = %redbook_ServiceDesc%
+ServiceType = 1
+StartType = 1
+ErrorControl = 1
+ServiceBinary = %12%\redbook.sys
+LoadOrderGroup = Pnp Filter
+
+[redbook_InstallEventLogSection]
+AddReg = redbook_EventLog_addreg
+
+[redbook_EventLog_addreg]
+HKR,,"EventMessageFile",0x00020000,"%%SystemRoot%%\System32\IoLogMsg.dll;%%SystemRoot%%\System32\drivers\redbook.sys"
+HKR,,"TypesSupported",0x00010001,7
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[Strings]
+DDK_SAMPLE="DDK Sample Provider"
+CDClassName = "DVD/CD-ROM drives"
+
+;; Manufacturer specific strings
+ATAPI_CHGR = "Atapi 2.5 Changer Devices"
+CHINON = "Chinon"
+DENON = "Denon"
+FUJITSU = "Fujitsu"
+HITACHI = "Hitachi"
+HP = "Hewlett Packard"
+MITSUMI = "Mitsumi"
+NEC = "NEC"
+OTI = "OTI"
+PIONEER = "Pioneer"
+WEARNES = "Wearnes"
+GenManufacturer = "(Standard CD-ROM drives)"
+
+;; Descriptions for enumerated brands and models
+AlpsChanger_devdesc = "Alps CD-ROM Changer"
+chinon_devdesc = "Chinon CD-ROM Drive"
+denon_devdesc = "Denon CD-ROM Drive"
+fujitsu_devdesc = "Fujitsu CD-ROM Drive"
+hp_devdesc = "Hewlett Packard CD-ROM Drive"
+hitachi_devdesc = "Hitachi CD-ROM Drive"
+Mitsumi_cdrom_devdesc = "Mitsumi CD-ROM Drive"
+NecChanger_devdesc = "NEC CD-ROM Changer"
+NecIntersect_devdesc = "NEC Intersect CD-ROM Drive"
+NecMultispin_devdesc = "NEC Multispin CD-ROM Drive"
+NecOem_devdesc = "NEC CD-ROM Drive"
+oti_devdesc = "OTI CD-ROM Drive"
+PanasonicChanger_devdesc = "Panasonic CD-ROM Changer"
+pioneer_devdesc = "Pioneer CD-ROM Drive"
+TorisanChanger_devdesc = "Torisan CD-ROM Changer"
+wearnes_devdesc = "Wearnes CD-ROM Drive"
+gencdrom_devdesc = "CD-ROM Drive"
+
+;; Mitsumi Friendly name explictly listed
+Mitsumi_Generic_FriendlyName = "Mitsumi CD-ROM Drive"
+
+;; Service descriptions
+cdrom_ServiceDesc = "CD-ROM Driver"
+cdaudio_ServiceDesc = "CD-Audio Filter Driver"
+changer_ServiceDesc = "CD-Changer Filter Driver"
+redbook_ServiceDesc = "Digital CD Audio Playback Filter Driver"
+
+
diff --git a/reactos/drivers/storage/class/cdrom_new/cdrom_new.rbuild b/reactos/drivers/storage/class/cdrom_new/cdrom_new.rbuild
new file mode 100644
index 00000000000..3969aedb585
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/cdrom_new.rbuild
@@ -0,0 +1,20 @@
+
+
+
+
+ ntoskrnl
+ hal
+ classpnp
+ ../inc
+
+ -mrtd
+ -fno-builtin
+ -w
+
+ cdrom.c
+ data.c
+ ioctl.c
+ mmc.c
+ scsicdrm.rc
+ sec.c
+
diff --git a/reactos/drivers/storage/class/cdrom_new/data.c b/reactos/drivers/storage/class/cdrom_new/data.c
new file mode 100644
index 00000000000..02d4239bf95
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/data.c
@@ -0,0 +1,99 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+
+Abstract:
+
+
+Environment:
+
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "ntddk.h"
+#include "classpnp.h"
+#include "trace.h"
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg("PAGE")
+#endif
+
+/*
+
+#define CDROM_HACK_DEC_RRD (0x00000001)
+#define CDROM_HACK_FUJITSU_FMCD_10x (0x00000002)
+#define CDROM_HACK_HITACHI_1750 (0x00000004)
+#define CDROM_HACK_HITACHI_GD_2000 (0x00000008)
+#define CDROM_HACK_TOSHIBA_SD_W1101 (0x00000010)
+#define CDROM_HACK_TOSHIBA_XM_3xx (0x00000020)
+#define CDROM_HACK_NEC_CDDA (0x00000040)
+#define CDROM_HACK_PLEXTOR_CDDA (0x00000080)
+#define CDROM_HACK_BAD_GET_CONFIG_SUPPORT (0x00000100)
+#define CDROM_HACK_FORCE_READ_CD_DETECTION (0x00000200)
+#define CDROM_HACK_READ_CD_SUPPORTED (0x00000400)
+
+*/
+
+CLASSPNP_SCAN_FOR_SPECIAL_INFO CdromHackItems[] = {
+ // digital put out drives using 512 byte block sizes,
+ // and needed us to send a mode page to set the sector
+ // size back to 2048.
+ { "DEC" , "RRD" , NULL, 0x0001 },
+ // these fujitsu drives take longer than ten seconds to
+ // timeout commands when audio discs are placed in them
+ { "FUJITSU" , "FMCD-101" , NULL, 0x0002 },
+ { "FUJITSU" , "FMCD-102" , NULL, 0x0002 },
+ // these hitachi drives don't work properly in PIO mode
+ { "HITACHI ", "CDR-1750S" , NULL, 0x0004 },
+ { "HITACHI ", "CDR-3650/1650S" , NULL, 0x0004 },
+ // this particular gem doesn't automatcially spin up
+ // on some media access commands.
+ { "" , "HITACHI GD-2000" , NULL, 0x0008 },
+ { "" , "HITACHI DVD-ROM GD-2000" , NULL, 0x0008 },
+ // this particular drive doesn't support DVD playback.
+ // just print an error message in CHK builds.
+ { "TOSHIBA ", "SD-W1101 DVD-RAM" , NULL, 0x0010 },
+ // not sure what this device's issue was. seems to
+ // require mode selects at various times.
+ { "TOSHIBA ", "CD-ROM XM-3" , NULL, 0x0020 },
+ // NEC defined a "READ_CD" type command before there was
+ // a standard, so fall back on this as an option.
+ { "NEC" , "" , NULL, 0x0040 },
+ // plextor defined a "READ_CD" type command before there was
+ // a standard, so fall back on this as an option.
+ { "PLEXTOR ", "" , NULL, 0x0080 },
+ // this drive times out and sometimes disappears from the bus
+ // when send GET_CONFIGURATION commands. don't send them.
+ { "" , "LG DVD-ROM DRD-840B" , NULL, 0x0100 },
+ { "" , "SAMSUNG DVD-ROM SD-608" , NULL, 0x0300 },
+ // these drives should have supported READ_CD, but at least
+ // some firmware revisions did not. force READ_CD detection.
+ { "" , "SAMSUNG DVD-ROM SD-" , NULL, 0x2000 },
+ // the mitsumi drive below doesn't follow the block-only spec,
+ // and we end up hanging when sending it commands it doesn't
+ // understand. this causes complications later, also.
+ { "MITSUMI ", "CR-4802TE " , NULL, 0x0100 },
+ // some drives return various funky errors (such as 3/2/0 NO_SEEK_COMPLETE)
+ // during the detection of READ_CD support, resulting in iffy detection.
+ // since they probably don't support mode switching, which is really old
+ // legacy stuff anyways, the ability to read digitally is lost when
+ // these drives return unexpected error codes. note: MMC compliant drives
+ // are presumed to support READ_CD, as are DVD drives, and anything
+ // connected to a bus type other than IDE or SCSI, and therefore don't
+ // need to be here.
+ { "YAMAHA ", "CRW8424S " , NULL, 0x0400 },
+ // and finally, a place to finish the list. :)
+ { NULL , NULL , NULL, 0x0000 }
+};
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg()
+#endif
+
diff --git a/reactos/drivers/storage/class/cdrom_new/ioctl.c b/reactos/drivers/storage/class/cdrom_new/ioctl.c
new file mode 100644
index 00000000000..446828e5294
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/ioctl.c
@@ -0,0 +1,4054 @@
+/*--
+
+Copyright (C) Microsoft Corporation, 1999 - 1999
+
+Module Name:
+
+ ioctl.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"
+
+
+#if DBG
+ PUCHAR READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor+1] = {
+ "Physical",
+ "Copyright",
+ "DiskKey",
+ "BCA",
+ "Manufacturer",
+ "Unknown"
+ };
+#endif // DBG
+
+#define DEFAULT_CDROM_SECTORS_PER_TRACK 32
+#define DEFAULT_TRACKS_PER_CYLINDER 64
+
+
+
+NTSTATUS
+CdRomDeviceControlDispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This is the NT device control handler for CDROMs.
+
+Arguments:
+
+ DeviceObject - for this CDROM
+
+ Irp - IO Request packet
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextStack;
+ PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData);
+
+ BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE);
+ SCSI_REQUEST_BLOCK srb;
+ PCDB cdb = (PCDB)srb.Cdb;
+ PVOID outputBuffer;
+ ULONG bytesTransferred = 0;
+ NTSTATUS status;
+ NTSTATUS status2;
+ KIRQL irql;
+
+ ULONG ioctlCode;
+ ULONG baseCode;
+ ULONG functionCode;
+
+RetryControl:
+
+ //
+ // Zero the SRB on stack.
+ //
+
+ RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ Irp->IoStatus.Information = 0;
+
+ //
+ // if this is a class driver ioctl then we need to change the base code
+ // to IOCTL_CDROM_BASE so that the switch statement can handle it.
+ //
+ // WARNING - currently the scsi class ioctl function codes are between
+ // 0x200 & 0x300. this routine depends on that fact
+ //
+
+ ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
+ baseCode = ioctlCode >> 16;
+ functionCode = (ioctlCode & (~0xffffc003)) >> 2;
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Ioctl Code = %lx, Base Code = %lx,"
+ " Function Code = %lx\n",
+ ioctlCode,
+ baseCode,
+ functionCode
+ ));
+
+ if((functionCode >= 0x200) && (functionCode <= 0x300)) {
+
+ ioctlCode = (ioctlCode & 0x0000ffff) | CTL_CODE(IOCTL_CDROM_BASE, 0, 0, 0);
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Class Code - new ioctl code is %lx\n",
+ ioctlCode));
+
+ irpStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
+
+ }
+
+ switch (ioctlCode) {
+
+ case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: {
+
+ PGET_MEDIA_TYPES mediaTypes = Irp->AssociatedIrp.SystemBuffer;
+ PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0];
+ ULONG sizeNeeded;
+
+ sizeNeeded = sizeof(GET_MEDIA_TYPES);
+
+ //
+ // IsMmc is static...
+ //
+
+ if (cdData->Mmc.IsMmc) {
+ sizeNeeded += sizeof(DEVICE_MEDIA_INFO) * 1; // return two media types
+ }
+
+ //
+ // Ensure that buffer is large enough.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeNeeded) {
+
+ //
+ // Buffer too small.
+ //
+
+ Irp->IoStatus.Information = sizeNeeded;
+ status = STATUS_BUFFER_TOO_SMALL;
+ break;
+ }
+
+ RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, sizeNeeded);
+
+ //
+ // ISSUE-2000/5/11-henrygab - need to update GET_MEDIA_TYPES_EX
+ //
+
+ mediaTypes->DeviceType = CdRomGetDeviceType(DeviceObject);
+
+ mediaTypes->MediaInfoCount = 1;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
+
+ if (cdData->Mmc.IsMmc) {
+
+ //
+ // also report a removable disk
+ //
+ mediaTypes->MediaInfoCount += 1;
+
+ mediaInfo++;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack;
+ mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
+ mediaInfo--;
+
+ }
+
+ //
+ // Status will either be success, if media is present, or no media.
+ // It would be optimal to base from density code and medium type, but not all devices
+ // have values for these fields.
+ //
+
+ //
+ // Send a TUR to determine if media is present.
+ //
+
+ srb.CdbLength = 6;
+ cdb = (PCDB)srb.Cdb;
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+ //
+ // Set timeout value.
+ //
+
+ srb.TimeOutValue = fdoExtension->TimeOutValue;
+
+ status = ClassSendSrbSynchronous(DeviceObject,
+ &srb,
+ NULL,
+ 0,
+ FALSE);
+
+
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: GET_MEDIA_TYPES status of TUR - %lx\n",
+ status));
+
+ if (NT_SUCCESS(status)) {
+
+ //
+ // set the disk's media as current if we can write to it.
+ //
+
+ if (cdData->Mmc.IsMmc && cdData->Mmc.WriteAllowed) {
+
+ mediaInfo++;
+ SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
+ MEDIA_CURRENTLY_MOUNTED);
+ mediaInfo--;
+
+
+ } else {
+
+ SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics,
+ MEDIA_CURRENTLY_MOUNTED);
+
+ }
+
+ }
+
+ Irp->IoStatus.Information = sizeNeeded;
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+
+ case IOCTL_CDROM_RAW_READ: {
+
+ LARGE_INTEGER startingOffset;
+ ULONGLONG transferBytes;
+ ULONGLONG endOffset;
+ ULONGLONG mdlBytes;
+ ULONG startingSector;
+ PRAW_READ_INFO rawReadInfo = (PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer;
+
+ //
+ // Ensure that XA reads are supported.
+ //
+
+ if (TEST_FLAG(cdData->XAFlags, XA_NOT_SUPPORTED)) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: XA Reads not supported. Flags (%x)\n",
+ cdData->XAFlags));
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ //
+ // Check that ending sector is on disc and buffers are there and of
+ // correct size.
+ //
+
+ if (rawReadInfo == NULL) {
+
+ //
+ // Called from user space. Save the userbuffer in the
+ // Type3InputBuffer so we can reduce the number of code paths.
+ //
+
+ irpStack->Parameters.DeviceIoControl.Type3InputBuffer =
+ Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Called from user space. Validate the buffers.
+ //
+
+ rawReadInfo = (PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer;
+
+ if (rawReadInfo == NULL) {
+
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Invalid I/O parameters for "
+ "XA Read (No extent info\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength !=
+ sizeof(RAW_READ_INFO)) {
+
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Invalid I/O parameters for "
+ "XA Read (Invalid info buffer\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+ }
+
+ //
+ // if they don't request any data, just fail the request
+ //
+
+ if (rawReadInfo->SectorCount == 0) {
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+ startingOffset.QuadPart = rawReadInfo->DiskOffset.QuadPart;
+ startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >>
+ fdoExtension->SectorShift);
+ transferBytes = (ULONGLONG)rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
+
+ endOffset = (ULONGLONG)rawReadInfo->SectorCount * COOKED_SECTOR_SIZE;
+ endOffset += startingOffset.QuadPart;
+
+ //
+ // check for overflows....
+ //
+
+ if (transferBytes < (ULONGLONG)(rawReadInfo->SectorCount)) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Invalid I/O parameters for XA "
+ "Read (TransferBytes Overflow)\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ if (endOffset < (ULONGLONG)startingOffset.QuadPart) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Invalid I/O parameters for XA "
+ "Read (EndingOffset Overflow)\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ transferBytes) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Invalid I/O parameters for XA "
+ "Read (Bad buffer size)\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ if (endOffset > (ULONGLONG)commonExtension->PartitionLength.QuadPart) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Invalid I/O parameters for XA "
+ "Read (Request Out of Bounds)\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // cannot validate the MdlAddress, since it is not included in any
+ // other location per the DDK and file system calls.
+ //
+
+ //
+ // validate the mdl describes at least the number of bytes
+ // requested from us.
+ //
+
+ mdlBytes = (ULONGLONG)MmGetMdlByteCount(Irp->MdlAddress);
+ if (mdlBytes < transferBytes) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Invalid MDL %s, Irp %p\n",
+ "size (5)", Irp));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // HACKHACK - REF #0001
+ // The retry count will be in this irp's IRP_MN function,
+ // as the new irp was freed, and we therefore cannot use
+ // this irp's next stack location for this function.
+ // This may be a good location to store this info for
+ // when we remove RAW_READ (mode switching), as we will
+ // no longer have the nextIrpStackLocation to play with
+ // when that occurs
+ //
+ // once XA_READ is removed, then this hack can also be
+ // removed.
+ //
+ irpStack->MinorFunction = MAXIMUM_RETRIES; // HACKHACK - REF #0001
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: {
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Get drive geometryEx\n"));
+ if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ FIELD_OFFSET(DISK_GEOMETRY_EX, Data)) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
+ break;
+ }
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Get drive geometry\n"));
+
+ if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof( DISK_GEOMETRY ) ) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_READ_TOC_EX: {
+
+ PCDROM_READ_TOC_EX inputBuffer;
+
+ if (CdRomIsPlayActive(DeviceObject)) {
+ status = STATUS_DEVICE_BUSY;
+ break;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(CDROM_READ_TOC_EX)) {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ break;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ MINIMUM_CDROM_READ_TOC_EX_SIZE) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = MINIMUM_CDROM_READ_TOC_EX_SIZE;
+ break;
+ }
+
+ if (irpStack->Parameters.Read.Length > ((USHORT)-1)) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ inputBuffer = Irp->AssociatedIrp.SystemBuffer;
+
+ if ((inputBuffer->Reserved1 != 0) ||
+ (inputBuffer->Reserved2 != 0) ||
+ (inputBuffer->Reserved3 != 0)) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // NOTE: when adding new formats, ensure that first two bytes
+ // specify the amount of additional data available.
+ //
+
+ if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_TOC ) ||
+ (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_FULL_TOC) ||
+ (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_CDTEXT )) {
+
+ // SessionTrack field is used
+
+ } else
+ if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) ||
+ (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_PMA) ||
+ (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_ATIP)) {
+
+ // SessionTrack field is reserved
+
+ if (inputBuffer->SessionTrack != 0) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_GET_LAST_SESSION: {
+
+ //
+ // If the cd is playing music then reject this request.
+ //
+
+ if (CdRomIsPlayActive(DeviceObject)) {
+ status = STATUS_DEVICE_BUSY;
+ break;
+ }
+
+ //
+ // Make sure the caller is requesting enough data to make this worth
+ // our while.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(CDROM_TOC_SESSION_DATA)) {
+
+ //
+ // they didn't request the entire TOC -- use _EX version
+ // for partial transfers and such.
+ //
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(CDROM_TOC_SESSION_DATA);
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_READ_TOC: {
+
+ //
+ // If the cd is playing music then reject this request.
+ //
+
+ if (CdRomIsPlayActive(DeviceObject)) {
+ status = STATUS_DEVICE_BUSY;
+ break;
+ }
+
+ //
+ // Make sure the caller is requesting enough data to make this worth
+ // our while.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(CDROM_TOC)) {
+
+ //
+ // they didn't request the entire TOC -- use _EX version
+ // for partial transfers and such.
+ //
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(CDROM_TOC);
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_PLAY_AUDIO_MSF: {
+
+ //
+ // Play Audio MSF
+ //
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Play audio MSF\n"));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(CDROM_PLAY_AUDIO_MSF)) {
+
+ //
+ // Indicate unsuccessful status.
+ //
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_SEEK_AUDIO_MSF: {
+
+
+ //
+ // Seek Audio MSF
+ //
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Seek audio MSF\n"));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(CDROM_SEEK_AUDIO_MSF)) {
+
+ //
+ // Indicate unsuccessful status.
+ //
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+
+ }
+
+ case IOCTL_CDROM_PAUSE_AUDIO: {
+
+ //
+ // Pause audio
+ //
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Pause audio\n"));
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+
+ break;
+ }
+
+ case IOCTL_CDROM_RESUME_AUDIO: {
+
+ //
+ // Resume audio
+ //
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Resume audio\n"));
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_READ_Q_CHANNEL: {
+
+ PCDROM_SUB_Q_DATA_FORMAT inputBuffer =
+ Irp->AssociatedIrp.SystemBuffer;
+
+ if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(CDROM_SUB_Q_DATA_FORMAT)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ break;
+ }
+
+ //
+ // check for all valid types of request
+ //
+
+ if (inputBuffer->Format != IOCTL_CDROM_CURRENT_POSITION &&
+ inputBuffer->Format != IOCTL_CDROM_MEDIA_CATALOG &&
+ inputBuffer->Format != IOCTL_CDROM_TRACK_ISRC ) {
+ status = STATUS_INVALID_PARAMETER;
+ Irp->IoStatus.Information = 0;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_GET_CONTROL: {
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Get audio control\n"));
+
+ //
+ // Verify user buffer is large enough for the data.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(CDROM_AUDIO_CONTROL)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(CDROM_AUDIO_CONTROL);
+ break;
+
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_GET_VOLUME: {
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Get volume control\n"));
+
+ //
+ // Verify user buffer is large enough for data.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(VOLUME_CONTROL)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(VOLUME_CONTROL);
+ break;
+
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_SET_VOLUME: {
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Set volume control\n"));
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(VOLUME_CONTROL)) {
+
+ //
+ // Indicate unsuccessful status.
+ //
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ break;
+
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_STOP_AUDIO: {
+
+ //
+ // Stop play.
+ //
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Stop audio\n"));
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_STORAGE_CHECK_VERIFY:
+ case IOCTL_DISK_CHECK_VERIFY:
+ case IOCTL_CDROM_CHECK_VERIFY: {
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: [%p] Check Verify\n", Irp));
+
+ if((irpStack->Parameters.DeviceIoControl.OutputBufferLength) &&
+ (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))) {
+
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceControl: Check Verify: media count "
+ "buffer too small\n"));
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(ULONG);
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DVD_READ_STRUCTURE: {
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl: [%p] IOCTL_DVD_READ_STRUCTURE\n", Irp));
+
+ if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: License Failure\n"));
+ status = STATUS_COPY_PROTECTION_FAILURE;
+ break;
+ }
+
+ if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) {
+ //
+ // if currently in-progress, this will just return.
+ // prevents looping by doing that interlockedExchange()
+ //
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: PickRegion() from "
+ "READ_STRUCTURE\n"));
+ CdRomPickDvdRegion(DeviceObject);
+ }
+
+
+ if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(DVD_READ_STRUCTURE)) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl - READ_STRUCTURE: input buffer "
+ "length too small (was %d should be %d)\n",
+ irpStack->Parameters.DeviceIoControl.InputBufferLength,
+ sizeof(DVD_READ_STRUCTURE)));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(READ_DVD_STRUCTURES_HEADER)) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl - READ_STRUCTURE: output buffer "
+ "cannot hold header information\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(READ_DVD_STRUCTURES_HEADER);
+ break;
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >
+ MAXUSHORT) {
+
+ //
+ // key length must fit in two bytes
+ //
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl - READ_STRUCTURE: output buffer "
+ "too large\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DVD_START_SESSION: {
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl: [%p] IOCTL_DVD_START_SESSION\n", Irp));
+
+ if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: License Failure\n"));
+ status = STATUS_COPY_PROTECTION_FAILURE;
+ break;
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(DVD_SESSION_ID)) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: DVD_START_SESSION - output "
+ "buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(DVD_SESSION_ID);
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DVD_SEND_KEY:
+ case IOCTL_DVD_SEND_KEY2: {
+
+ PDVD_COPY_PROTECT_KEY key = Irp->AssociatedIrp.SystemBuffer;
+ ULONG keyLength;
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl: [%p] IOCTL_DVD_SEND_KEY\n", Irp));
+
+ if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: License Failure\n"));
+ status = STATUS_COPY_PROTECTION_FAILURE;
+ break;
+ }
+
+ if((irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(DVD_COPY_PROTECT_KEY)) ||
+ (irpStack->Parameters.DeviceIoControl.InputBufferLength !=
+ key->KeyLength)) {
+
+ //
+ // Key is too small to have a header or the key length doesn't
+ // match the input buffer length. Key must be invalid
+ //
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: [%p] IOCTL_DVD_SEND_KEY - "
+ "key is too small or does not match KeyLength\n",
+ Irp));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // allow only certain key type (non-destructive) to go through
+ // IOCTL_DVD_SEND_KEY (which only requires READ access to the device
+ //
+ if (ioctlCode == IOCTL_DVD_SEND_KEY) {
+
+ if ((key->KeyType != DvdChallengeKey) &&
+ (key->KeyType != DvdBusKey2) &&
+ (key->KeyType != DvdInvalidateAGID)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ if (cdData->DvdRpc0Device) {
+
+ if (key->KeyType == DvdSetRpcKey) {
+
+ PDVD_SET_RPC_KEY rpcKey = (PDVD_SET_RPC_KEY) key->KeyData;
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ DVD_SET_RPC_KEY_LENGTH) {
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // we have a request to set region code
+ // on a RPC0 device which doesn't support
+ // region coding.
+ //
+ // we have to fake it.
+ //
+
+ KeWaitForMutexObject(
+ &cdData->Rpc0RegionMutex,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) {
+ //
+ // if currently in-progress, this will just return.
+ // prevents looping by doing that interlockedExchange()
+ //
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: PickRegion() from "
+ "SEND_KEY\n"));
+ CdRomPickDvdRegion(DeviceObject);
+ }
+
+ if (cdData->Rpc0SystemRegion == rpcKey->PreferredDriveRegionCode) {
+
+ //
+ // nothing to change
+ //
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl (%p) => not changing "
+ "regions -- requesting current region\n",
+ DeviceObject));
+ status = STATUS_SUCCESS;
+
+ } else if (cdData->Rpc0SystemRegionResetCount == 0) {
+
+ //
+ // not allowed to change it again
+ //
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl (%p) => no more region "
+ "changes are allowed for this device\n",
+ DeviceObject));
+ status = STATUS_CSS_RESETS_EXHAUSTED;
+
+ } else {
+
+ ULONG i;
+ UCHAR mask;
+ ULONG bufferLen;
+ PDVD_READ_STRUCTURE dvdReadStructure;
+ PDVD_COPYRIGHT_DESCRIPTOR dvdCopyRight;
+ IO_STATUS_BLOCK ioStatus;
+ UCHAR mediaRegionData;
+
+ mask = ~rpcKey->PreferredDriveRegionCode;
+
+ if (CountOfSetBitsUChar(mask) != 1) {
+
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ //
+ // this test will always be TRUE except during initial
+ // automatic selection of the first region.
+ //
+
+ if (cdData->Rpc0SystemRegion != 0xff) {
+
+ //
+ // make sure we have a media in the drive with the same
+ // region code if the drive is already has a region set
+ //
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl (%p) => Checking "
+ "media region\n",
+ DeviceObject));
+
+ bufferLen = max(sizeof(DVD_DESCRIPTOR_HEADER) +
+ sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+ sizeof(DVD_READ_STRUCTURE)
+ );
+
+ dvdReadStructure = (PDVD_READ_STRUCTURE)
+ ExAllocatePoolWithTag(PagedPool,
+ bufferLen,
+ DVD_TAG_RPC2_CHECK);
+
+ if (dvdReadStructure == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE);
+ break;
+ }
+
+ dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR)
+ ((PDVD_DESCRIPTOR_HEADER) dvdReadStructure)->Data;
+
+ //
+ // check to see if we have a DVD device
+ //
+
+ RtlZeroMemory (dvdReadStructure, bufferLen);
+ dvdReadStructure->Format = DvdCopyrightDescriptor;
+
+ //
+ // Build a request for READ_KEY
+ //
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_DVD_READ_STRUCTURE,
+ DeviceObject,
+ dvdReadStructure,
+ sizeof(DVD_READ_STRUCTURE),
+ sizeof(DVD_DESCRIPTOR_HEADER) +
+ sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+ FALSE,
+ &ioStatus);
+
+ //
+ // this is just to prevent bugs from creeping in
+ // if status is not set later in development
+ //
+
+ status = ioStatus.Status;
+
+ //
+ // handle errors
+ //
+
+ if (!NT_SUCCESS(status)) {
+ KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE);
+ ExFreePool(dvdReadStructure);
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ //
+ // save the mediaRegionData before freeing the
+ // allocated memory
+ //
+
+ mediaRegionData =
+ dvdCopyRight->RegionManagementInformation;
+ ExFreePool(dvdReadStructure);
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl (%p) => new mask is %x"
+ " MediaRegionData is %x\n", DeviceObject,
+ rpcKey->PreferredDriveRegionCode,
+ mediaRegionData));
+
+ //
+ // the media region must match the requested region
+ // for RPC0 drives for initial region selection
+ //
+
+ if (((UCHAR)~(mediaRegionData | rpcKey->PreferredDriveRegionCode)) == 0) {
+ KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE);
+ status = STATUS_CSS_REGION_MISMATCH;
+ break;
+ }
+
+ }
+
+ //
+ // now try to set the region
+ //
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl (%p) => Soft-Setting "
+ "region of RPC1 device to %x\n",
+ DeviceObject,
+ rpcKey->PreferredDriveRegionCode
+ ));
+
+ status = CdRomSetRpc0Settings(DeviceObject,
+ rpcKey->PreferredDriveRegionCode);
+
+ if (!NT_SUCCESS(status)) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl (%p) => Could not "
+ "set region code (%x)\n",
+ DeviceObject, status
+ ));
+ } else {
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl (%p) => New region set "
+ " for RPC1 drive\n", DeviceObject));
+
+ //
+ // if it worked, our extension is already updated.
+ // release the mutex
+ //
+
+ DebugPrint ((4, "DvdDeviceControl (%p) => DVD current "
+ "region bitmap 0x%x\n", DeviceObject,
+ cdData->Rpc0SystemRegion));
+ DebugPrint ((4, "DvdDeviceControl (%p) => DVD region "
+ " reset Count 0x%x\n", DeviceObject,
+ cdData->Rpc0SystemRegionResetCount));
+ }
+
+ }
+
+ KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE);
+ break;
+ } // end of key->KeyType == DvdSetRpcKey
+ } // end of Rpc0Device hacks
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+ break;
+ }
+
+ case IOCTL_DVD_READ_KEY: {
+
+ PDVD_COPY_PROTECT_KEY keyParameters = Irp->AssociatedIrp.SystemBuffer;
+ ULONG keyLength;
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl: [%p] IOCTL_DVD_READ_KEY\n", Irp));
+
+ if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: License Failure\n"));
+ status = STATUS_COPY_PROTECTION_FAILURE;
+ break;
+ }
+
+ if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: PickRegion() from READ_KEY\n"));
+ CdRomPickDvdRegion(DeviceObject);
+ }
+
+
+ if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(DVD_COPY_PROTECT_KEY)) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: EstablishDriveKey - challenge "
+ "key buffer too small\n"));
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+
+ }
+
+
+ switch(keyParameters->KeyType) {
+
+ case DvdChallengeKey:
+ keyLength = DVD_CHALLENGE_KEY_LENGTH;
+ break;
+
+ case DvdBusKey1:
+ case DvdBusKey2:
+
+ keyLength = DVD_BUS_KEY_LENGTH;
+ break;
+
+ case DvdTitleKey:
+ keyLength = DVD_TITLE_KEY_LENGTH;
+ break;
+
+ case DvdAsf:
+ keyLength = DVD_ASF_LENGTH;
+ break;
+
+ case DvdDiskKey:
+ keyLength = DVD_DISK_KEY_LENGTH;
+ break;
+
+ case DvdGetRpcKey:
+ keyLength = DVD_RPC_KEY_LENGTH;
+ break;
+
+ default:
+ keyLength = sizeof(DVD_COPY_PROTECT_KEY);
+ break;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ keyLength) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: EstablishDriveKey - output "
+ "buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = keyLength;
+ break;
+ }
+
+ if (keyParameters->KeyType == DvdGetRpcKey) {
+
+ CdRomPickDvdRegion(DeviceObject);
+ }
+
+ if ((keyParameters->KeyType == DvdGetRpcKey) &&
+ (cdData->DvdRpc0Device)) {
+
+ PDVD_RPC_KEY rpcKey;
+ rpcKey = (PDVD_RPC_KEY)keyParameters->KeyData;
+ RtlZeroMemory (rpcKey, sizeof (*rpcKey));
+
+ KeWaitForMutexObject(
+ &cdData->Rpc0RegionMutex,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ //
+ // make up the data
+ //
+ rpcKey->UserResetsAvailable = cdData->Rpc0SystemRegionResetCount;
+ rpcKey->ManufacturerResetsAvailable = 0;
+ if (cdData->Rpc0SystemRegion == 0xff) {
+ rpcKey->TypeCode = 0;
+ } else {
+ rpcKey->TypeCode = 1;
+ }
+ rpcKey->RegionMask = (UCHAR) cdData->Rpc0SystemRegion;
+ rpcKey->RpcScheme = 1;
+
+ KeReleaseMutex(
+ &cdData->Rpc0RegionMutex,
+ FALSE
+ );
+
+ Irp->IoStatus.Information = DVD_RPC_KEY_LENGTH;
+ status = STATUS_SUCCESS;
+ break;
+
+ } else if (keyParameters->KeyType == DvdDiskKey) {
+
+ PDVD_COPY_PROTECT_KEY keyHeader;
+ PDVD_READ_STRUCTURE readStructureRequest;
+
+ //
+ // Special case - build a request to get the dvd structure
+ // so we can get the disk key.
+ //
+
+ //
+ // save the key header so we can restore the interesting
+ // parts later
+ //
+
+ keyHeader = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(DVD_COPY_PROTECT_KEY),
+ DVD_TAG_READ_KEY);
+
+ if(keyHeader == NULL) {
+
+ //
+ // Can't save the context so return an error
+ //
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl - READ_KEY: unable to "
+ "allocate context\n"));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ RtlCopyMemory(keyHeader,
+ Irp->AssociatedIrp.SystemBuffer,
+ sizeof(DVD_COPY_PROTECT_KEY));
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ nextStack = IoGetNextIrpStackLocation(Irp);
+
+ nextStack->Parameters.DeviceIoControl.IoControlCode =
+ IOCTL_DVD_READ_STRUCTURE;
+
+ readStructureRequest = Irp->AssociatedIrp.SystemBuffer;
+ readStructureRequest->Format = DvdDiskKeyDescriptor;
+ readStructureRequest->BlockByteOffset.QuadPart = 0;
+ readStructureRequest->LayerNumber = 0;
+ readStructureRequest->SessionId = keyHeader->SessionId;
+
+ nextStack->Parameters.DeviceIoControl.InputBufferLength =
+ sizeof(DVD_READ_STRUCTURE);
+
+ nextStack->Parameters.DeviceIoControl.OutputBufferLength =
+ sizeof(READ_DVD_STRUCTURES_HEADER) + sizeof(DVD_DISK_KEY_DESCRIPTOR);
+
+ IoSetCompletionRoutine(Irp,
+ CdRomDvdReadDiskKeyCompletion,
+ (PVOID) keyHeader,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ {
+ UCHAR uniqueAddress;
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ IoMarkIrpPending(Irp);
+ IoCallDriver(commonExtension->DeviceObject, Irp);
+ status = STATUS_PENDING;
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress);
+ }
+
+ return STATUS_PENDING;
+
+ } else {
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ }
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DVD_END_SESSION: {
+
+ PDVD_SESSION_ID sessionId = Irp->AssociatedIrp.SystemBuffer;
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl: [%p] END_SESSION\n", Irp));
+
+ if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: License Failure\n"));
+ status = STATUS_COPY_PROTECTION_FAILURE;
+ break;
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(DVD_SESSION_ID)) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: EndSession - input buffer too "
+ "small\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+
+ if(*sessionId == DVD_END_ALL_SESSIONS) {
+
+ status = CdRomDvdEndAllSessionsCompletion(DeviceObject, Irp, NULL);
+
+ if(status == STATUS_SUCCESS) {
+
+ //
+ // Just complete the request - it was never issued to the
+ // lower device
+ //
+
+ break;
+
+ } else {
+
+ return STATUS_PENDING;
+
+ }
+ }
+
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DVD_GET_REGION: {
+
+ PDVD_COPY_PROTECT_KEY copyProtectKey;
+ ULONG keyLength;
+ IO_STATUS_BLOCK ioStatus;
+ PDVD_DESCRIPTOR_HEADER dvdHeader;
+ PDVD_COPYRIGHT_DESCRIPTOR copyRightDescriptor;
+ PDVD_REGION dvdRegion;
+ PDVD_READ_STRUCTURE readStructure;
+ PDVD_RPC_KEY rpcKey;
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControl: [%p] IOCTL_DVD_GET_REGION\n", Irp));
+
+ if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) {
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: License Failure\n"));
+ status = STATUS_COPY_PROTECTION_FAILURE;
+ break;
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(DVD_REGION)) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: output buffer DVD_REGION too small\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // figure out how much data buffer we need
+ //
+
+ keyLength = max(sizeof(DVD_DESCRIPTOR_HEADER) +
+ sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+ sizeof(DVD_READ_STRUCTURE)
+ );
+ keyLength = max(keyLength,
+ DVD_RPC_KEY_LENGTH
+ );
+
+ //
+ // round the size to nearest ULONGLONG -- why?
+ //
+
+ keyLength += sizeof(ULONGLONG) - (keyLength & (sizeof(ULONGLONG) - 1));
+
+ readStructure = ExAllocatePoolWithTag(NonPagedPool,
+ keyLength,
+ DVD_TAG_READ_KEY);
+ if (readStructure == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ RtlZeroMemory (readStructure, keyLength);
+ readStructure->Format = DvdCopyrightDescriptor;
+
+ //
+ // Build a request for READ_STRUCTURE
+ //
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_DVD_READ_STRUCTURE,
+ DeviceObject,
+ readStructure,
+ keyLength,
+ sizeof(DVD_DESCRIPTOR_HEADER) +
+ sizeof(DVD_COPYRIGHT_DESCRIPTOR),
+ FALSE,
+ &ioStatus);
+
+ status = ioStatus.Status;
+
+ if (!NT_SUCCESS(status)) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDvdGetRegion => read structure failed %x\n",
+ status));
+ ExFreePool(readStructure);
+ break;
+ }
+
+ //
+ // we got the copyright descriptor, so now get the region if possible
+ //
+
+ dvdHeader = (PDVD_DESCRIPTOR_HEADER) readStructure;
+ copyRightDescriptor = (PDVD_COPYRIGHT_DESCRIPTOR) dvdHeader->Data;
+
+ //
+ // the original irp's systembuffer has a copy of the info that
+ // should be passed down in the request
+ //
+
+ dvdRegion = Irp->AssociatedIrp.SystemBuffer;
+
+ dvdRegion->CopySystem = copyRightDescriptor->CopyrightProtectionType;
+ dvdRegion->RegionData = copyRightDescriptor->RegionManagementInformation;
+
+ //
+ // now reuse the buffer to request the copy protection info
+ //
+
+ copyProtectKey = (PDVD_COPY_PROTECT_KEY) readStructure;
+ RtlZeroMemory (copyProtectKey, DVD_RPC_KEY_LENGTH);
+ copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH;
+ copyProtectKey->KeyType = DvdGetRpcKey;
+
+ //
+ // send a request for READ_KEY
+ //
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_DVD_READ_KEY,
+ DeviceObject,
+ copyProtectKey,
+ DVD_RPC_KEY_LENGTH,
+ DVD_RPC_KEY_LENGTH,
+ FALSE,
+ &ioStatus);
+ status = ioStatus.Status;
+
+ if (!NT_SUCCESS(status)) {
+ TraceLog((CdromDebugWarning,
+ "CdRomDvdGetRegion => read key failed %x\n",
+ status));
+ ExFreePool(readStructure);
+ break;
+ }
+
+ //
+ // the request succeeded. if a supported scheme is returned,
+ // then return the information to the caller
+ //
+
+ rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData;
+
+ if (rpcKey->RpcScheme == 1) {
+
+ if (rpcKey->TypeCode) {
+
+ dvdRegion->SystemRegion = ~rpcKey->RegionMask;
+ dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
+
+ } else {
+
+ //
+ // the drive has not been set for any region
+ //
+
+ dvdRegion->SystemRegion = 0;
+ dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
+ }
+ Irp->IoStatus.Information = sizeof(DVD_REGION);
+
+ } else {
+
+ TraceLog((CdromDebugWarning,
+ "CdRomDvdGetRegion => rpcKey->RpcScheme != 1\n"));
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ ExFreePool(readStructure);
+ break;
+ }
+
+
+ case IOCTL_STORAGE_SET_READ_AHEAD: {
+
+ if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(STORAGE_SET_READ_AHEAD)) {
+
+ TraceLog((CdromDebugWarning,
+ "DvdDeviceControl: SetReadAhead buffer too small\n"));
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DISK_IS_WRITABLE: {
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ return STATUS_PENDING;
+
+ }
+
+ case IOCTL_DISK_GET_DRIVE_LAYOUT: {
+
+ ULONG size;
+
+ //
+ // we always fake zero or one partitions, and one partition
+ // structure is included in DRIVE_LAYOUT_INFORMATION
+ //
+
+ size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]);
+
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Get drive layout\n"));
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < size) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = size;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+
+
+ }
+ case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: {
+
+ ULONG size;
+
+ //
+ // we always fake zero or one partitions, and one partition
+ // structure is included in DRIVE_LAYOUT_INFORMATION_EX
+ //
+
+ size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Get drive layout ex\n"));
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < size) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = size;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+
+ }
+
+
+ case IOCTL_DISK_GET_PARTITION_INFO: {
+
+ //
+ // Check that the buffer is large enough.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(PARTITION_INFORMATION)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION);
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+
+ }
+ case IOCTL_DISK_GET_PARTITION_INFO_EX: {
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(PARTITION_INFORMATION_EX)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX);
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DISK_VERIFY: {
+
+ TraceLog((CdromDebugTrace,
+ "IOCTL_DISK_VERIFY to device %p through irp %p\n",
+ DeviceObject, Irp));
+
+ //
+ // Validate buffer length.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(VERIFY_INFORMATION)) {
+
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ break;
+ }
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_DISK_GET_LENGTH_INFO: {
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(GET_LENGTH_INFORMATION)) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
+ break;
+ }
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+ }
+
+ case IOCTL_CDROM_GET_CONFIGURATION: {
+
+ PGET_CONFIGURATION_IOCTL_INPUT inputBuffer;
+
+ TraceLog((CdromDebugTrace,
+ "IOCTL_CDROM_GET_CONFIGURATION to via irp %p\n", Irp));
+
+ //
+ // Validate buffer length.
+ //
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength !=
+ sizeof(GET_CONFIGURATION_IOCTL_INPUT)) {
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ break;
+ }
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(GET_CONFIGURATION_HEADER)) {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(GET_CONFIGURATION_HEADER);
+ break;
+ }
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength > 0xffff) {
+ // output buffer is too large
+ status = STATUS_INVALID_BUFFER_SIZE;
+ break;
+ }
+
+ //
+ // also verify the arguments are reasonable.
+ //
+
+ inputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ if (inputBuffer->Feature > 0xffff) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) &&
+ (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) &&
+ (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL)) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1]) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ return STATUS_PENDING;
+
+ }
+
+ default: {
+
+ BOOLEAN synchronize = (KeGetCurrentIrql() == PASSIVE_LEVEL);
+ PKEVENT deviceControlEvent;
+
+ //
+ // If the ioctl has come in at passive level then we will synchronize
+ // with our start-io routine when sending the ioctl. If the ioctl
+ // has come in at a higher interrupt level and it was not handled
+ // above then it's unlikely to be a request for the class DLL - however
+ // we'll still use it's common code to forward the request through.
+ //
+
+ if (synchronize) {
+
+ deviceControlEvent = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(KEVENT),
+ CDROM_TAG_DC_EVENT);
+
+ if (deviceControlEvent == NULL) {
+
+ //
+ // must complete this irp unsuccessful here
+ //
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+
+ } else {
+
+ PIO_STACK_LOCATION currentStack;
+
+ KeInitializeEvent(deviceControlEvent, NotificationEvent, FALSE);
+
+ currentStack = IoGetCurrentIrpStackLocation(Irp);
+ nextStack = IoGetNextIrpStackLocation(Irp);
+
+ //
+ // Copy the stack down a notch
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ IoSetCompletionRoutine(
+ Irp,
+ CdRomClassIoctlCompletion,
+ deviceControlEvent,
+ TRUE,
+ TRUE,
+ TRUE
+ );
+
+ IoSetNextIrpStackLocation(Irp);
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+
+ //
+ // Override volume verifies on this stack location so that we
+ // will be forced through the synchronization. Once this
+ // location goes away we get the old value back
+ //
+
+ SET_FLAG(nextStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
+
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+
+ //
+ // Wait for CdRomClassIoctlCompletion to set the event. This
+ // ensures serialization remains intact for these unhandled device
+ // controls.
+ //
+
+ KeWaitForSingleObject(
+ deviceControlEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ ExFreePool(deviceControlEvent);
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: irp %p synchronized\n", Irp));
+
+ status = Irp->IoStatus.Status;
+ }
+
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ //
+ // If an error occured then propagate that back up - we are no longer
+ // guaranteed synchronization and the upper layers will have to
+ // retry.
+ //
+ // If no error occured, call down to the class driver directly
+ // then start up the next request.
+ //
+
+ if (NT_SUCCESS(status)) {
+
+ UCHAR uniqueAddress;
+
+ //
+ // The class device control routine will release the remove
+ // lock for this Irp. We need to make sure we have one
+ // available so that it's safe to call IoStartNextPacket
+ //
+
+ if(synchronize) {
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress);
+
+ }
+
+ status = ClassDeviceControl(DeviceObject, Irp);
+
+ if(synchronize) {
+ KeRaiseIrql(DISPATCH_LEVEL, &irql);
+ IoStartNextPacket(DeviceObject, FALSE);
+ KeLowerIrql(irql);
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress);
+ }
+ return status;
+
+ }
+
+ //
+ // an error occurred (either STATUS_INSUFFICIENT_RESOURCES from
+ // attempting to synchronize or StartIo() error'd this one
+ // out), so we need to finish the irp, which is
+ // done at the end of this routine.
+ //
+ break;
+
+ } // end default case
+
+ } // end switch()
+
+ if (status == STATUS_VERIFY_REQUIRED) {
+
+ //
+ // If the status is verified required and this request
+ // should bypass verify required then retry the request.
+ //
+
+ if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {
+
+ status = STATUS_IO_DEVICE_ERROR;
+ goto RetryControl;
+
+ }
+ }
+
+ if (IoIsErrorUserInduced(status)) {
+
+ if (Irp->Tail.Overlay.Thread) {
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ }
+
+ }
+
+ //
+ // Update IRP with completion status.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // Complete the request.
+ //
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControl: Status is %lx\n", status));
+ return status;
+
+} // end CdRomDeviceControl()
+
+
+NTSTATUS
+CdRomClassIoctlCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine signals the event used by CdRomDeviceControl to synchronize
+ class driver (and lower level driver) ioctls with cdrom's startio routine.
+ The irp completion is short-circuited so that CdRomDeviceControlDispatch
+ can reissue it once it wakes up.
+
+Arguments:
+
+ DeviceObject - the device object
+ Irp - the request we are synchronizing
+ Context - a PKEVENT that we need to signal
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PKEVENT syncEvent = (PKEVENT) Context;
+
+ TraceLog((CdromDebugTrace,
+ "CdRomClassIoctlCompletion: setting event for irp %p\n", Irp));
+
+ //
+ // We released the lock when we completed this request. Reacquire it.
+ //
+
+ ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ KeSetEvent(syncEvent, IO_DISK_INCREMENT, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+NTSTATUS
+CdRomDeviceControlCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData);
+ BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE);
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ 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 that we've really got the correct irp
+ //
+
+ ASSERT(realIrpNextStack->Parameters.Others.Argument3 == Irp);
+
+ //
+ // Check SRB status for success of completing request.
+ //
+
+ if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ ULONG retryInterval;
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControlCompletion: Irp %p, Srb %p Real Irp %p Status %lx\n",
+ Irp,
+ srb,
+ realIrp,
+ srb->SrbStatus));
+
+ //
+ // Release the queue if it is frozen.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControlCompletion: Releasing Queue\n"));
+ ClassReleaseQueue(DeviceObject);
+ }
+
+
+ retry = ClassInterpretSenseInfo(DeviceObject,
+ srb,
+ irpStack->MajorFunction,
+ irpStack->Parameters.DeviceIoControl.IoControlCode,
+ MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1),
+ &status,
+ &retryInterval);
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControlCompletion: IRP will %sbe retried\n",
+ (retry ? "" : "not ")));
+
+ //
+ // Some of the Device Controls need special cases on non-Success status's.
+ //
+
+ if (realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+ if ((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) ||
+ (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) ||
+ (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC_EX) ||
+ (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_CONTROL) ||
+ (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)) {
+
+ if (status == STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ retry = FALSE;
+ }
+ }
+
+ if (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL) {
+ PLAY_ACTIVE(fdoExtension) = FALSE;
+ }
+ }
+
+ //
+ // 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) {
+
+ // note: status gets overwritten here
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+
+ if (((realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) ||
+ (realIrpStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)
+ ) &&
+ ((realIrpStack->Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_CDROM_CHECK_VERIFY) ||
+ (realIrpStack->Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_STORAGE_CHECK_VERIFY) ||
+ (realIrpStack->Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_STORAGE_CHECK_VERIFY2) ||
+ (realIrpStack->Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_DISK_CHECK_VERIFY)
+ )
+ ) {
+
+ //
+ // Update the geometry information, as the media could have
+ // changed. The completion routine for this will complete
+ // the real irp and start the next packet.
+ //
+
+ if (srb) {
+ if (srb->SenseInfoBuffer) {
+ ExFreePool(srb->SenseInfoBuffer);
+ }
+ if (srb->DataBuffer) {
+ ExFreePool(srb->DataBuffer);
+ }
+ ExFreePool(srb);
+ srb = NULL;
+ }
+
+ if (Irp->MdlAddress) {
+ IoFreeMdl(Irp->MdlAddress);
+ Irp->MdlAddress = NULL;
+ }
+
+ IoFreeIrp(Irp);
+ Irp = NULL;
+
+ status = CdRomUpdateCapacity(fdoExtension, realIrp, NULL);
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControlCompletion: [%p] "
+ "CdRomUpdateCapacity completed with status %lx\n",
+ realIrp, status));
+
+ //
+ // needed to update the capacity.
+ // the irp's already handed off to CdRomUpdateCapacity().
+ // we've already free'd the current irp.
+ // nothing left to do in this code path.
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ } // end of ioctls to update capacity
+
+ }
+
+ if (retry && 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);
+ if (srb->DataBuffer) {
+ ExFreePool(srb->DataBuffer);
+ }
+ ExFreePool(srb);
+ if (Irp->MdlAddress) {
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+ realIrpNextStack->Parameters.Others.Argument3 = (PVOID)-1;
+ IoFreeIrp(Irp);
+
+ 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)) {
+
+ BOOLEAN b = FALSE;
+
+
+ switch (realIrpStack->Parameters.DeviceIoControl.IoControlCode) {
+
+ case IOCTL_CDROM_GET_CONFIGURATION: {
+ RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
+ srb->DataBuffer,
+ srb->DataTransferLength);
+ realIrp->IoStatus.Information = srb->DataTransferLength;
+ break;
+ }
+
+ case IOCTL_DISK_GET_LENGTH_INFO: {
+
+ PGET_LENGTH_INFORMATION lengthInfo;
+
+ CdRomInterpretReadCapacity(DeviceObject,
+ (PREAD_CAPACITY_DATA)srb->DataBuffer);
+
+ lengthInfo = (PGET_LENGTH_INFORMATION)realIrp->AssociatedIrp.SystemBuffer;
+ lengthInfo->Length = commonExtension->PartitionLength;
+ realIrp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: {
+
+ PDISK_GEOMETRY_EX geometryEx;
+
+ CdRomInterpretReadCapacity(DeviceObject,
+ (PREAD_CAPACITY_DATA)srb->DataBuffer);
+
+ geometryEx = (PDISK_GEOMETRY_EX)(realIrp->AssociatedIrp.SystemBuffer);
+ geometryEx->DiskSize = commonExtension->PartitionLength;
+ geometryEx->Geometry = fdoExtension->DiskGeometry;
+ realIrp->IoStatus.Information =
+ FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
+ break;
+ }
+
+ case IOCTL_DISK_GET_DRIVE_GEOMETRY:
+ case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
+
+ PDISK_GEOMETRY geometry;
+
+ CdRomInterpretReadCapacity(DeviceObject,
+ (PREAD_CAPACITY_DATA)srb->DataBuffer);
+
+ geometry = (PDISK_GEOMETRY)(realIrp->AssociatedIrp.SystemBuffer);
+ *geometry = fdoExtension->DiskGeometry;
+ realIrp->IoStatus.Information = sizeof(DISK_GEOMETRY);
+ break;
+ }
+
+ case IOCTL_DISK_VERIFY: {
+ //
+ // nothing to do but return the status...
+ //
+ break;
+ }
+
+ case IOCTL_DISK_CHECK_VERIFY:
+ case IOCTL_STORAGE_CHECK_VERIFY:
+ case IOCTL_CDROM_CHECK_VERIFY: {
+
+ if((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY) &&
+ (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength)) {
+
+ *((PULONG)realIrp->AssociatedIrp.SystemBuffer) =
+ commonExtension->PartitionZeroExtension->MediaChangeCount;
+
+ realIrp->IoStatus.Information = sizeof(ULONG);
+ } else {
+ realIrp->IoStatus.Information = 0;
+ }
+
+ TraceLog((CdromDebugTrace,
+ "CdRomDeviceControlCompletion: [%p] completing "
+ "CHECK_VERIFY buddy irp %p\n", realIrp, Irp));
+ break;
+ }
+
+ case IOCTL_CDROM_READ_TOC_EX: {
+
+ if (srb->DataTransferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE) {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ //
+ // Copy the returned info into the user buffer.
+ //
+
+ RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
+ srb->DataBuffer,
+ srb->DataTransferLength);
+
+ //
+ // update information field.
+ //
+
+ realIrp->IoStatus.Information = srb->DataTransferLength;
+ break;
+ }
+
+
+ case IOCTL_CDROM_GET_LAST_SESSION:
+ case IOCTL_CDROM_READ_TOC: {
+
+ //
+ // Copy the returned info into the user buffer.
+ //
+
+ RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
+ srb->DataBuffer,
+ srb->DataTransferLength);
+
+ //
+ // update information field.
+ //
+
+ realIrp->IoStatus.Information = srb->DataTransferLength;
+ break;
+ }
+
+ case IOCTL_DVD_READ_STRUCTURE: {
+
+ DVD_STRUCTURE_FORMAT format = ((PDVD_READ_STRUCTURE) realIrp->AssociatedIrp.SystemBuffer)->Format;
+
+ PDVD_DESCRIPTOR_HEADER header = realIrp->AssociatedIrp.SystemBuffer;
+
+ FOUR_BYTE fourByte;
+ PTWO_BYTE twoByte;
+ UCHAR tmp;
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControlCompletion - IOCTL_DVD_READ_STRUCTURE: completing irp %p (buddy %p)\n",
+ Irp,
+ realIrp));
+
+ TraceLog((CdromDebugTrace,
+ "DvdDCCompletion - READ_STRUCTURE: descriptor format of %d\n", format));
+
+ RtlMoveMemory(header,
+ srb->DataBuffer,
+ srb->DataTransferLength);
+
+ //
+ // Cook the data. There are a number of fields that really
+ // should be byte-swapped for the caller.
+ //
+
+ TraceLog((CdromDebugInfo,
+ "DvdDCCompletion - READ_STRUCTURE:\n"
+ "\tHeader at %p\n"
+ "\tDvdDCCompletion - READ_STRUCTURE: data at %p\n"
+ "\tDataBuffer was at %p\n"
+ "\tDataTransferLength was %lx\n",
+ header,
+ header->Data,
+ srb->DataBuffer,
+ srb->DataTransferLength));
+
+ //
+ // First the fields in the header
+ //
+
+ TraceLog((CdromDebugInfo, "READ_STRUCTURE: header->Length %lx -> ",
+ header->Length));
+ REVERSE_SHORT(&header->Length);
+ TraceLog((CdromDebugInfo, "%lx\n", header->Length));
+
+ //
+ // Now the fields in the descriptor
+ //
+
+ if(format == DvdPhysicalDescriptor) {
+
+ PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR) &(header->Data[0]);
+
+ TraceLog((CdromDebugInfo, "READ_STRUCTURE: StartingDataSector %lx -> ",
+ layer->StartingDataSector));
+ REVERSE_LONG(&(layer->StartingDataSector));
+ TraceLog((CdromDebugInfo, "%lx\n", layer->StartingDataSector));
+
+ TraceLog((CdromDebugInfo, "READ_STRUCTURE: EndDataSector %lx -> ",
+ layer->EndDataSector));
+ REVERSE_LONG(&(layer->EndDataSector));
+ TraceLog((CdromDebugInfo, "%lx\n", layer->EndDataSector));
+
+ TraceLog((CdromDebugInfo, "READ_STRUCTURE: EndLayerZeroSector %lx -> ",
+ layer->EndLayerZeroSector));
+ REVERSE_LONG(&(layer->EndLayerZeroSector));
+ TraceLog((CdromDebugInfo, "%lx\n", layer->EndLayerZeroSector));
+ }
+
+ TraceLog((CdromDebugTrace, "Status is %lx\n", Irp->IoStatus.Status));
+ TraceLog((CdromDebugTrace, "DvdDeviceControlCompletion - "
+ "IOCTL_DVD_READ_STRUCTURE: data transfer length of %d\n",
+ srb->DataTransferLength));
+
+ realIrp->IoStatus.Information = srb->DataTransferLength;
+ break;
+ }
+
+ case IOCTL_DVD_READ_KEY: {
+
+ PDVD_COPY_PROTECT_KEY copyProtectKey = realIrp->AssociatedIrp.SystemBuffer;
+
+ PCDVD_KEY_HEADER keyHeader = srb->DataBuffer;
+ ULONG dataLength;
+
+ ULONG transferLength =
+ srb->DataTransferLength -
+ FIELD_OFFSET(CDVD_KEY_HEADER, Data);
+
+ //
+ // Adjust the data length to ignore the two reserved bytes in the
+ // header.
+ //
+
+ dataLength = (keyHeader->DataLength[0] << 8) +
+ keyHeader->DataLength[1];
+ dataLength -= 2;
+
+ //
+ // take the minimum of the transferred length and the
+ // length as specified in the header.
+ //
+
+ if(dataLength < transferLength) {
+ transferLength = dataLength;
+ }
+
+ TraceLog((CdromDebugTrace,
+ "DvdDeviceControlCompletion: [%p] - READ_KEY with "
+ "transfer length of (%d or %d) bytes\n",
+ Irp,
+ dataLength,
+ srb->DataTransferLength - 2));
+
+ //
+ // Copy the key data into the return buffer
+ //
+ if(copyProtectKey->KeyType == DvdTitleKey) {
+
+ RtlMoveMemory(copyProtectKey->KeyData,
+ keyHeader->Data + 1,
+ transferLength - 1);
+ copyProtectKey->KeyData[transferLength - 1] = 0;
+
+ //
+ // If this is a title key then we need to copy the CGMS flags
+ // as well.
+ //
+ copyProtectKey->KeyFlags = *(keyHeader->Data);
+
+ } else {
+
+ RtlMoveMemory(copyProtectKey->KeyData,
+ keyHeader->Data,
+ transferLength);
+ }
+
+ copyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY);
+ copyProtectKey->KeyLength += transferLength;
+
+ realIrp->IoStatus.Information = copyProtectKey->KeyLength;
+ break;
+ }
+
+ case IOCTL_DVD_START_SESSION: {
+
+ PDVD_SESSION_ID sessionId = realIrp->AssociatedIrp.SystemBuffer;
+
+ PCDVD_KEY_HEADER keyHeader = srb->DataBuffer;
+ PCDVD_REPORT_AGID_DATA keyData = (PCDVD_REPORT_AGID_DATA) keyHeader->Data;
+
+ *sessionId = keyData->AGID;
+
+ realIrp->IoStatus.Information = sizeof(DVD_SESSION_ID);
+
+ break;
+ }
+
+ case IOCTL_DVD_END_SESSION:
+ case IOCTL_DVD_SEND_KEY:
+ case IOCTL_DVD_SEND_KEY2:
+
+ //
+ // nothing to return
+ //
+ realIrp->IoStatus.Information = 0;
+ break;
+
+ case IOCTL_CDROM_PLAY_AUDIO_MSF:
+
+ PLAY_ACTIVE(fdoExtension) = TRUE;
+
+ break;
+
+ case IOCTL_CDROM_READ_Q_CHANNEL: {
+
+ PSUB_Q_CHANNEL_DATA userChannelData = realIrp->AssociatedIrp.SystemBuffer;
+ PCDROM_SUB_Q_DATA_FORMAT inputBuffer = realIrp->AssociatedIrp.SystemBuffer;
+ PSUB_Q_CHANNEL_DATA subQPtr = srb->DataBuffer;
+
+#if DBG
+ switch( inputBuffer->Format ) {
+
+ case IOCTL_CDROM_CURRENT_POSITION:
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Control = 0x%x\n", subQPtr->CurrentPosition.Control ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Track = %u\n", subQPtr->CurrentPosition.TrackNumber ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Index = %u\n", subQPtr->CurrentPosition.IndexNumber ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) ));
+ break;
+
+ case IOCTL_CDROM_MEDIA_CATALOG:
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Mcval is %u\n", subQPtr->MediaCatalog.Mcval ));
+ break;
+
+ case IOCTL_CDROM_TRACK_ISRC:
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus ));
+ TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Tcval is %u\n", subQPtr->TrackIsrc.Tcval ));
+ break;
+
+ }
+#endif
+
+ //
+ // Update the play active status.
+ //
+
+ if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
+
+ PLAY_ACTIVE(fdoExtension) = TRUE;
+
+ } else {
+
+ PLAY_ACTIVE(fdoExtension) = FALSE;
+
+ }
+
+ //
+ // Check if output buffer is large enough to contain
+ // the data.
+ //
+
+ if (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ srb->DataTransferLength) {
+
+ srb->DataTransferLength =
+ realIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
+ }
+
+ //
+ // Copy our buffer into users.
+ //
+
+ RtlMoveMemory(userChannelData,
+ subQPtr,
+ srb->DataTransferLength);
+
+ realIrp->IoStatus.Information = srb->DataTransferLength;
+ break;
+ }
+
+ case IOCTL_CDROM_PAUSE_AUDIO:
+
+ PLAY_ACTIVE(fdoExtension) = FALSE;
+ realIrp->IoStatus.Information = 0;
+ break;
+
+ case IOCTL_CDROM_RESUME_AUDIO:
+
+ realIrp->IoStatus.Information = 0;
+ break;
+
+ case IOCTL_CDROM_SEEK_AUDIO_MSF:
+
+ realIrp->IoStatus.Information = 0;
+ break;
+
+ case IOCTL_CDROM_STOP_AUDIO:
+
+ PLAY_ACTIVE(fdoExtension) = FALSE;
+ realIrp->IoStatus.Information = 0;
+ break;
+
+ case IOCTL_CDROM_GET_CONTROL: {
+
+ PCDROM_AUDIO_CONTROL audioControl = srb->DataBuffer;
+ PAUDIO_OUTPUT audioOutput;
+ ULONG bytesTransferred;
+
+ audioOutput = ClassFindModePage((PCHAR)audioControl,
+ srb->DataTransferLength,
+ CDROM_AUDIO_CONTROL_PAGE,
+ use6Byte);
+ //
+ // Verify the page is as big as expected.
+ //
+
+ bytesTransferred = (ULONG)((PCHAR) audioOutput - (PCHAR) audioControl) +
+ sizeof(AUDIO_OUTPUT);
+
+ if (audioOutput != NULL &&
+ srb->DataTransferLength >= bytesTransferred) {
+
+ audioControl->LbaFormat = audioOutput->LbaFormat;
+
+ audioControl->LogicalBlocksPerSecond =
+ (audioOutput->LogicalBlocksPerSecond[0] << (UCHAR)8) |
+ audioOutput->LogicalBlocksPerSecond[1];
+
+ realIrp->IoStatus.Information = sizeof(CDROM_AUDIO_CONTROL);
+
+ } else {
+ realIrp->IoStatus.Information = 0;
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+ break;
+ }
+
+ case IOCTL_CDROM_GET_VOLUME: {
+
+ PAUDIO_OUTPUT audioOutput;
+ PVOLUME_CONTROL volumeControl = srb->DataBuffer;
+ ULONG i;
+ ULONG bytesTransferred;
+
+ audioOutput = ClassFindModePage((PCHAR)volumeControl,
+ srb->DataTransferLength,
+ CDROM_AUDIO_CONTROL_PAGE,
+ use6Byte);
+
+ //
+ // Verify the page is as big as expected.
+ //
+
+ bytesTransferred = (ULONG)((PCHAR) audioOutput - (PCHAR) volumeControl) +
+ sizeof(AUDIO_OUTPUT);
+
+ if (audioOutput != NULL &&
+ srb->DataTransferLength >= bytesTransferred) {
+
+ for (i=0; i<4; i++) {
+ volumeControl->PortVolume[i] =
+ audioOutput->PortOutput[i].Volume;
+ }
+
+ //
+ // Set bytes transferred in IRP.
+ //
+
+ realIrp->IoStatus.Information = sizeof(VOLUME_CONTROL);
+
+ } else {
+ realIrp->IoStatus.Information = 0;
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ break;
+ }
+
+ case IOCTL_CDROM_SET_VOLUME:
+
+ realIrp->IoStatus.Information = 0;
+ break;
+
+ default:
+
+ ASSERT(FALSE);
+ realIrp->IoStatus.Information = 0;
+ status = STATUS_INVALID_DEVICE_REQUEST;
+
+ } // end switch()
+ }
+
+ //
+ // Deallocate srb and sense buffer.
+ //
+
+ if (srb) {
+ if (srb->DataBuffer) {
+ ExFreePool(srb->DataBuffer);
+ }
+ if (srb->SenseInfoBuffer) {
+ ExFreePool(srb->SenseInfoBuffer);
+ }
+ ExFreePool(srb);
+ }
+
+ 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.
+ //
+
+ TraceLog((CdromDebugWarning,
+ "CdRomDeviceCompletion - Setting Hard Error on realIrp %p\n",
+ realIrp));
+ if (realIrp->Tail.Overlay.Thread) {
+ IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
+ }
+
+ realIrp->IoStatus.Information = 0;
+ }
+
+ //
+ // note: must complete the realIrp, as the completed irp (above)
+ // was self-allocated.
+ //
+
+ CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+NTSTATUS
+CdRomSetVolumeIntermediateCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = 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 (status == STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ retry = FALSE;
+ }
+
+ //
+ // 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 && 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);
+
+ CdRomRetryRequest(deviceExtension,
+ 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)) {
+
+ PAUDIO_OUTPUT audioInput = NULL;
+ PAUDIO_OUTPUT audioOutput;
+ PVOLUME_CONTROL volumeControl = realIrp->AssociatedIrp.SystemBuffer;
+ ULONG i,bytesTransferred,headerLength;
+ PVOID dataBuffer;
+ PCDB cdb;
+
+ audioInput = ClassFindModePage((PCHAR)srb->DataBuffer,
+ srb->DataTransferLength,
+ CDROM_AUDIO_CONTROL_PAGE,
+ use6Byte);
+
+ //
+ // Check to make sure the mode sense data is valid before we go on
+ //
+
+ if(audioInput == NULL) {
+
+ TraceLog((CdromDebugWarning,
+ "Mode Sense Page %d not found\n",
+ CDROM_AUDIO_CONTROL_PAGE));
+
+ realIrp->IoStatus.Information = 0;
+ realIrp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
+ goto SafeExit;
+ }
+
+ if (use6Byte) {
+ headerLength = sizeof(MODE_PARAMETER_HEADER);
+ } else {
+ headerLength = sizeof(MODE_PARAMETER_HEADER10);
+ }
+
+ bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength;
+
+ //
+ // Allocate a new buffer for the mode select.
+ //
+
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ bytesTransferred,
+ CDROM_TAG_VOLUME_INT);
+
+ if (!dataBuffer) {
+ realIrp->IoStatus.Information = 0;
+ realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SafeExit;
+ }
+
+ RtlZeroMemory(dataBuffer, bytesTransferred);
+
+ //
+ // Rebuild the data buffer to include the user requested values.
+ //
+
+ audioOutput = (PAUDIO_OUTPUT) ((PCHAR) dataBuffer + headerLength);
+
+ for (i=0; i<4; i++) {
+ audioOutput->PortOutput[i].Volume =
+ volumeControl->PortVolume[i];
+ audioOutput->PortOutput[i].ChannelSelection =
+ audioInput->PortOutput[i].ChannelSelection;
+ }
+
+ audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE;
+ audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2;
+ audioOutput->Immediate = MODE_SELECT_IMMEDIATE;
+
+ //
+ // Free the old data buffer, mdl.
+ //
+
+ IoFreeMdl(Irp->MdlAddress);
+ Irp->MdlAddress = NULL;
+ ExFreePool(srb->DataBuffer);
+
+ //
+ // set the data buffer to new allocation, so it can be
+ // freed in the exit path
+ //
+
+ srb->DataBuffer = dataBuffer;
+
+ //
+ // rebuild the srb.
+ //
+
+ cdb = (PCDB)srb->Cdb;
+ RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
+
+ srb->SrbStatus = srb->ScsiStatus = 0;
+ srb->SrbFlags = deviceExtension->SrbFlags;
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT);
+ srb->DataTransferLength = bytesTransferred;
+
+ if (use6Byte) {
+
+ cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
+ cdb->MODE_SELECT.ParameterListLength = (UCHAR) bytesTransferred;
+ cdb->MODE_SELECT.PFBit = 1;
+ srb->CdbLength = 6;
+ } else {
+
+ cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10;
+ cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8);
+ cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF);
+ cdb->MODE_SELECT10.PFBit = 1;
+ srb->CdbLength = 10;
+ }
+
+ //
+ // Prepare the MDL
+ //
+
+ Irp->MdlAddress = IoAllocateMdl(dataBuffer,
+ bytesTransferred,
+ FALSE,
+ FALSE,
+ (PIRP) NULL);
+
+ if (!Irp->MdlAddress) {
+ realIrp->IoStatus.Information = 0;
+ realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SafeExit;
+ }
+
+ MmBuildMdlForNonPagedPool(Irp->MdlAddress);
+
+ irpStack = IoGetNextIrpStackLocation(Irp);
+ irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN;
+ irpStack->Parameters.Scsi.Srb = srb;
+
+ //
+ // reset the irp completion.
+ //
+
+ IoSetCompletionRoutine(Irp,
+ CdRomDeviceControlCompletion,
+ srb,
+ TRUE,
+ TRUE,
+ TRUE);
+ //
+ // Call the port driver.
+ //
+
+ IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+SafeExit:
+
+ //
+ // 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;
+}
+
+NTSTATUS
+CdRomDvdEndAllSessionsCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine will setup the next stack location to issue an end session
+ to the device. It will increment the session id in the system buffer
+ and issue an END_SESSION for that AGID if the AGID is valid.
+
+ When the new AGID is > 3 this routine will complete the request.
+
+Arguments:
+
+ DeviceObject - the device object for this drive
+
+ Irp - the request
+
+ Context - done
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED if there is another AGID to clear
+ status otherwise.
+
+--*/
+
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+
+ PDVD_SESSION_ID sessionId = Irp->AssociatedIrp.SystemBuffer;
+
+ NTSTATUS status;
+
+ if(++(*sessionId) > MAX_COPY_PROTECT_AGID) {
+
+ //
+ // We're done here - just return success and let the io system
+ // continue to complete it.
+ //
+
+ return STATUS_SUCCESS;
+
+ }
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ IoSetCompletionRoutine(Irp,
+ CdRomDvdEndAllSessionsCompletion,
+ NULL,
+ TRUE,
+ FALSE,
+ FALSE);
+
+ IoMarkIrpPending(Irp);
+
+ IoCallDriver(fdoExtension->CommonExtension.DeviceObject, Irp);
+
+ //
+ // At this point we have to assume the irp may have already been
+ // completed. Ignore the returned status and return.
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS
+CdRomDvdReadDiskKeyCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine handles the completion of a request to obtain the disk
+ key from the dvd media. It will transform the raw 2K of key data into
+ a DVD_COPY_PROTECT_KEY structure and copy back the saved key parameters
+ from the context pointer before returning.
+
+Arguments:
+
+ DeviceObject -
+
+ Irp -
+
+ Context - a DVD_COPY_PROTECT_KEY pointer which contains the key
+ parameters handed down by the caller.
+
+Return Value:
+
+ STATUS_SUCCESS;
+
+--*/
+
+{
+ PDVD_COPY_PROTECT_KEY savedKey = Context;
+
+ PREAD_DVD_STRUCTURES_HEADER rawKey = Irp->AssociatedIrp.SystemBuffer;
+ PDVD_COPY_PROTECT_KEY outputKey = Irp->AssociatedIrp.SystemBuffer;
+
+ if (NT_SUCCESS(Irp->IoStatus.Status)) {
+
+ //
+ // Shift the data down to its new position.
+ //
+
+ RtlMoveMemory(outputKey->KeyData,
+ rawKey->Data,
+ sizeof(DVD_DISK_KEY_DESCRIPTOR));
+
+ RtlCopyMemory(outputKey,
+ savedKey,
+ sizeof(DVD_COPY_PROTECT_KEY));
+
+ outputKey->KeyLength = DVD_DISK_KEY_LENGTH;
+
+ Irp->IoStatus.Information = DVD_DISK_KEY_LENGTH;
+
+ } else {
+
+ TraceLog((CdromDebugWarning,
+ "DiskKey Failed with status %x, %p (%x) bytes\n",
+ Irp->IoStatus.Status,
+ (PVOID)Irp->IoStatus.Information,
+ ((rawKey->Length[0] << 16) | rawKey->Length[1])
+ ));
+
+ }
+
+ //
+ // release the context block
+ //
+
+ ExFreePool(Context);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+CdRomXACompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This routine executes when the port driver has completed a request.
+ It looks at the SRB status in the completing SRB and if not success
+ it checks for valid request sense buffer information. If valid, the
+ info is used to update status with more precise message of type of
+ error. This routine deallocates the SRB.
+
+Arguments:
+
+ DeviceObject - Supplies the device object which represents the logical
+ unit.
+
+ Irp - Supplies the Irp which has completed.
+
+ Context - Supplies a pointer to the SRB.
+
+Return Value:
+
+ NT status
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = Context;
+ PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
+ NTSTATUS status;
+ BOOLEAN retry;
+
+ //
+ // Check SRB status for success of completing request.
+ //
+
+ if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ ULONG retryInterval;
+
+ TraceLog((CdromDebugTrace, "CdromXAComplete: IRP %p SRB %p Status %x\n",
+ Irp, srb, srb->SrbStatus));
+
+ //
+ // Release the queue if it is frozen.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+ ClassReleaseQueue(DeviceObject);
+ }
+
+ retry = ClassInterpretSenseInfo(
+ DeviceObject,
+ srb,
+ irpStack->MajorFunction,
+ irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0,
+ MAXIMUM_RETRIES - irpStack->MinorFunction, // HACKHACK - REF #0001
+ &status,
+ &retryInterval);
+
+ //
+ // If the status is verified required and the this request
+ // should bypass verify required then retry the request.
+ //
+
+ if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
+ status == STATUS_VERIFY_REQUIRED) {
+
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ if (retry) {
+
+ if (irpStack->MinorFunction != 0) { // HACKHACK - REF #0001
+
+ irpStack->MinorFunction--; // HACKHACK - REF #0001
+
+ //
+ // Retry request.
+ //
+
+ TraceLog((CdromDebugWarning,
+ "CdRomXACompletion: Retry request %p (%x) - "
+ "Calling StartIo\n", Irp, irpStack->MinorFunction));
+
+
+ ExFreePool(srb->SenseInfoBuffer);
+ ExFreePool(srb);
+
+ //
+ // Call StartIo directly since IoStartNextPacket hasn't been called,
+ // the serialisation is still intact.
+ //
+
+ CdRomRetryRequest(deviceExtension,
+ Irp,
+ retryInterval,
+ FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ }
+
+ //
+ // Exhausted retries, fall through and complete the request
+ // with the appropriate status
+ //
+
+ TraceLog((CdromDebugWarning,
+ "CdRomXACompletion: Retries exhausted for irp %p\n",
+ Irp));
+
+ }
+
+ } else {
+
+ //
+ // Set status for successful request.
+ //
+
+ status = STATUS_SUCCESS;
+
+ } // end if (SRB_STATUS(srb->SrbStatus) ...
+
+ //
+ // Return SRB to nonpaged pool.
+ //
+
+ ExFreePool(srb->SenseInfoBuffer);
+ ExFreePool(srb);
+
+ //
+ // Set status in completing IRP.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // Set the hard error if necessary.
+ //
+
+ if (!NT_SUCCESS(status) &&
+ IoIsErrorUserInduced(status) &&
+ Irp->Tail.Overlay.Thread != NULL ) {
+
+ //
+ // Store DeviceObject for filesystem, and clear
+ // in IoStatus.Information field.
+ //
+
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ Irp->IoStatus.Information = 0;
+ }
+
+ //
+ // If pending has be returned for this irp then mark the current stack as
+ // pending.
+ //
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ {
+ KIRQL oldIrql = KeRaiseIrqlToDpcLevel();
+ IoStartNextPacket(DeviceObject, FALSE);
+ KeLowerIrql(oldIrql);
+ }
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ return status;
+}
+
+
+VOID
+CdRomDeviceControlDvdReadStructure(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCDB cdb = (PCDB)Srb->Cdb;
+ PVOID dataBuffer;
+
+ PDVD_READ_STRUCTURE request;
+ USHORT dataLength;
+ ULONG blockNumber;
+ PFOUR_BYTE fourByte;
+
+ dataLength =
+ (USHORT)currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
+
+ request = OriginalIrp->AssociatedIrp.SystemBuffer;
+ blockNumber =
+ (ULONG)(request->BlockByteOffset.QuadPart >> fdoExtension->SectorShift);
+ fourByte = (PFOUR_BYTE) &blockNumber;
+
+ Srb->CdbLength = 12;
+ Srb->TimeOutValue = fdoExtension->TimeOutValue;
+ Srb->SrbFlags = fdoExtension->SrbFlags;
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
+
+ cdb->READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
+ cdb->READ_DVD_STRUCTURE.RMDBlockNumber[0] = fourByte->Byte3;
+ cdb->READ_DVD_STRUCTURE.RMDBlockNumber[1] = fourByte->Byte2;
+ cdb->READ_DVD_STRUCTURE.RMDBlockNumber[2] = fourByte->Byte1;
+ cdb->READ_DVD_STRUCTURE.RMDBlockNumber[3] = fourByte->Byte0;
+ cdb->READ_DVD_STRUCTURE.LayerNumber = request->LayerNumber;
+ cdb->READ_DVD_STRUCTURE.Format = (UCHAR)request->Format;
+
+#if DBG
+ {
+ if ((UCHAR)request->Format > DvdMaxDescriptor) {
+ TraceLog((CdromDebugWarning,
+ "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
+ (UCHAR)request->Format,
+ READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor],
+ dataLength
+ ));
+ } else {
+ TraceLog((CdromDebugWarning,
+ "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n",
+ (UCHAR)request->Format,
+ READ_DVD_STRUCTURE_FORMAT_STRINGS[(UCHAR)request->Format],
+ dataLength
+ ));
+ }
+ }
+#endif // DBG
+
+ if (request->Format == DvdDiskKeyDescriptor) {
+
+ cdb->READ_DVD_STRUCTURE.AGID = (UCHAR) request->SessionId;
+
+ }
+
+ cdb->READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataLength >> 8);
+ cdb->READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataLength & 0xff);
+ Srb->DataTransferLength = dataLength;
+
+
+
+ dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ dataLength,
+ DVD_TAG_READ_STRUCTURE);
+
+ if (!dataBuffer) {
+ ExFreePool(Srb->SenseInfoBuffer);
+ ExFreePool(Srb);
+ IoFreeIrp(NewIrp);
+ OriginalIrp->IoStatus.Information = 0;
+ OriginalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ BAIL_OUT(OriginalIrp);
+ CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp);
+ return;
+ }
+ RtlZeroMemory(dataBuffer, dataLength);
+
+ NewIrp->MdlAddress = IoAllocateMdl(dataBuffer,
+ currentIrpStack->Parameters.Read.Length,
+ FALSE,
+ FALSE,
+ (PIRP) NULL);
+
+ if (NewIrp->MdlAddress == NULL) {
+ ExFreePool(dataBuffer);
+ ExFreePool(Srb->SenseInfoBuffer);
+ ExFreePool(Srb);
+ IoFreeIrp(NewIrp);
+ OriginalIrp->IoStatus.Information = 0;
+ OriginalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ BAIL_OUT(OriginalIrp);
+ CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp);
+ return;
+ }
+
+ //
+ // Prepare the MDL
+ //
+
+ MmBuildMdlForNonPagedPool(NewIrp->MdlAddress);
+
+ Srb->DataBuffer = dataBuffer;
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp);
+
+ return;
+}
+
+
+VOID
+CdRomDeviceControlDvdEndSession(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCDB cdb = (PCDB)Srb->Cdb;
+
+ PDVD_SESSION_ID sessionId = OriginalIrp->AssociatedIrp.SystemBuffer;
+
+ Srb->CdbLength = 12;
+ Srb->TimeOutValue = fdoExtension->TimeOutValue;
+ Srb->SrbFlags = fdoExtension->SrbFlags;
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
+
+ cdb->SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
+ cdb->SEND_KEY.AGID = (UCHAR) (*sessionId);
+ cdb->SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp);
+ return;
+
+}
+
+
+VOID
+CdRomDeviceControlDvdStartSessionReadKey(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCDB cdb = (PCDB)Srb->Cdb;
+ NTSTATUS status;
+
+ PDVD_COPY_PROTECT_KEY keyParameters;
+ PCDVD_KEY_HEADER keyBuffer = NULL;
+
+ ULONG keyLength;
+
+ ULONG allocationLength;
+ PFOUR_BYTE fourByte;
+
+ //
+ // Both of these use REPORT_KEY commands.
+ // Determine the size of the input buffer
+ //
+
+ if(currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_DVD_READ_KEY) {
+
+ keyParameters = OriginalIrp->AssociatedIrp.SystemBuffer;
+
+ keyLength = sizeof(CDVD_KEY_HEADER) +
+ (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength -
+ sizeof(DVD_COPY_PROTECT_KEY));
+ } else {
+
+ keyParameters = NULL;
+ keyLength = sizeof(CDVD_KEY_HEADER) +
+ sizeof(CDVD_REPORT_AGID_DATA);
+ }
+
+ TRY {
+
+ keyBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ keyLength,
+ DVD_TAG_READ_KEY);
+
+ if(keyBuffer == NULL) {
+
+ TraceLog((CdromDebugWarning,
+ "IOCTL_DVD_READ_KEY - couldn't allocate "
+ "%d byte buffer for key\n",
+ keyLength));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ LEAVE;
+ }
+
+
+ NewIrp->MdlAddress = IoAllocateMdl(keyBuffer,
+ keyLength,
+ FALSE,
+ FALSE,
+ (PIRP) NULL);
+
+ if(NewIrp->MdlAddress == NULL) {
+
+ TraceLog((CdromDebugWarning,
+ "IOCTL_DVD_READ_KEY - couldn't create mdl\n"));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ LEAVE;
+ }
+
+ MmBuildMdlForNonPagedPool(NewIrp->MdlAddress);
+
+ Srb->DataBuffer = keyBuffer;
+ Srb->CdbLength = 12;
+
+ cdb->REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
+
+ allocationLength = keyLength;
+ fourByte = (PFOUR_BYTE) &allocationLength;
+ cdb->REPORT_KEY.AllocationLength[0] = fourByte->Byte1;
+ cdb->REPORT_KEY.AllocationLength[1] = fourByte->Byte0;
+
+ Srb->DataTransferLength = keyLength;
+
+ //
+ // set the specific parameters....
+ //
+
+ if(currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
+ IOCTL_DVD_READ_KEY) {
+
+ if(keyParameters->KeyType == DvdTitleKey) {
+
+ ULONG logicalBlockAddress;
+
+ logicalBlockAddress = (ULONG)
+ (keyParameters->Parameters.TitleOffset.QuadPart >>
+ fdoExtension->SectorShift);
+
+ fourByte = (PFOUR_BYTE) &(logicalBlockAddress);
+
+ cdb->REPORT_KEY.LogicalBlockAddress[0] = fourByte->Byte3;
+ cdb->REPORT_KEY.LogicalBlockAddress[1] = fourByte->Byte2;
+ cdb->REPORT_KEY.LogicalBlockAddress[2] = fourByte->Byte1;
+ cdb->REPORT_KEY.LogicalBlockAddress[3] = fourByte->Byte0;
+ }
+
+ cdb->REPORT_KEY.KeyFormat = (UCHAR)keyParameters->KeyType;
+ cdb->REPORT_KEY.AGID = (UCHAR) keyParameters->SessionId;
+ TraceLog((CdromDebugWarning,
+ "CdRomDvdReadKey => sending irp %p for irp %p (%s)\n",
+ NewIrp, OriginalIrp, "READ_KEY"));
+
+ } else {
+
+ cdb->REPORT_KEY.KeyFormat = DVD_REPORT_AGID;
+ cdb->REPORT_KEY.AGID = 0;
+ TraceLog((CdromDebugWarning,
+ "CdRomDvdReadKey => sending irp %p for irp %p (%s)\n",
+ NewIrp, OriginalIrp, "START_SESSION"));
+ }
+
+ Srb->TimeOutValue = fdoExtension->TimeOutValue;
+ Srb->SrbFlags = fdoExtension->SrbFlags;
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp);
+
+ status = STATUS_SUCCESS;
+
+ } FINALLY {
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // An error occured during setup - free resources and
+ // complete this request.
+ //
+ if (NewIrp->MdlAddress != NULL) {
+ IoFreeMdl(NewIrp->MdlAddress);
+ }
+
+ if (keyBuffer != NULL) {
+ ExFreePool(keyBuffer);
+ }
+ ExFreePool(Srb->SenseInfoBuffer);
+ ExFreePool(Srb);
+ IoFreeIrp(NewIrp);
+
+ OriginalIrp->IoStatus.Information = 0;
+ OriginalIrp->IoStatus.Status = status;
+
+ BAIL_OUT(OriginalIrp);
+ CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp);
+
+ } // end !NT_SUCCESS
+ }
+ return;
+}
+
+
+VOID
+CdRomDeviceControlDvdSendKey(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP OriginalIrp,
+ IN PIRP NewIrp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCDB cdb = (PCDB)Srb->Cdb;
+
+ PDVD_COPY_PROTECT_KEY key;
+ PCDVD_KEY_HEADER keyBuffer = NULL;
+
+ NTSTATUS status;
+ ULONG keyLength;
+ PFOUR_BYTE fourByte;
+
+ key = OriginalIrp->AssociatedIrp.SystemBuffer;
+ keyLength = (key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)) +
+ sizeof(CDVD_KEY_HEADER);
+
+ TRY {
+
+ keyBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ keyLength,
+ DVD_TAG_SEND_KEY);
+
+ if(keyBuffer == NULL) {
+
+ TraceLog((CdromDebugWarning,
+ "IOCTL_DVD_SEND_KEY - couldn't allocate "
+ "%d byte buffer for key\n",
+ keyLength));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ LEAVE;
+ }
+
+ RtlZeroMemory(keyBuffer, keyLength);
+
+ //
+ // keylength is decremented here by two because the
+ // datalength does not include the header, which is two
+ // bytes. keylength is immediately incremented later
+ // by the same amount.
+ //
+
+ keyLength -= 2;
+ fourByte = (PFOUR_BYTE) &keyLength;
+ keyBuffer->DataLength[0] = fourByte->Byte1;
+ keyBuffer->DataLength[1] = fourByte->Byte0;
+ keyLength += 2;
+
+ //
+ // copy the user's buffer to our own allocated buffer
+ //
+
+ RtlMoveMemory(keyBuffer->Data,
+ key->KeyData,
+ key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY));
+
+
+ NewIrp->MdlAddress = IoAllocateMdl(keyBuffer,
+ keyLength,
+ FALSE,
+ FALSE,
+ (PIRP) NULL);
+
+ if(NewIrp->MdlAddress == NULL) {
+ TraceLog((CdromDebugWarning,
+ "IOCTL_DVD_SEND_KEY - couldn't create mdl\n"));
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ LEAVE;
+ }
+
+
+ MmBuildMdlForNonPagedPool(NewIrp->MdlAddress);
+
+ Srb->CdbLength = 12;
+ Srb->DataBuffer = keyBuffer;
+ Srb->DataTransferLength = keyLength;
+
+ Srb->TimeOutValue = fdoExtension->TimeOutValue;
+ Srb->SrbFlags = fdoExtension->SrbFlags;
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
+
+ cdb->REPORT_KEY.OperationCode = SCSIOP_SEND_KEY;
+
+ fourByte = (PFOUR_BYTE) &keyLength;
+
+ cdb->SEND_KEY.ParameterListLength[0] = fourByte->Byte1;
+ cdb->SEND_KEY.ParameterListLength[1] = fourByte->Byte0;
+ cdb->SEND_KEY.KeyFormat = (UCHAR)key->KeyType;
+ cdb->SEND_KEY.AGID = (UCHAR) key->SessionId;
+
+ if (key->KeyType == DvdSetRpcKey) {
+ TraceLog((CdromDebugWarning,
+ "IOCTL_DVD_SEND_KEY - Setting RPC2 drive region\n"));
+ } else {
+ TraceLog((CdromDebugWarning,
+ "IOCTL_DVD_SEND_KEY - key type %x\n", key->KeyType));
+ }
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp);
+
+ status = STATUS_SUCCESS;
+
+ } FINALLY {
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // An error occured during setup - free resources and
+ // complete this request.
+ //
+
+ if (NewIrp->MdlAddress != NULL) {
+ IoFreeMdl(NewIrp->MdlAddress);
+ }
+
+ if (keyBuffer != NULL) {
+ ExFreePool(keyBuffer);
+ }
+
+ ExFreePool(Srb->SenseInfoBuffer);
+ ExFreePool(Srb);
+ IoFreeIrp(NewIrp);
+
+ OriginalIrp->IoStatus.Information = 0;
+ OriginalIrp->IoStatus.Status = status;
+
+ BAIL_OUT(OriginalIrp);
+ CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp);
+
+ }
+ }
+
+ return;
+}
+
+
+VOID
+CdRomInterpretReadCapacity(
+ IN PDEVICE_OBJECT Fdo,
+ IN PREAD_CAPACITY_DATA ReadCapacityBuffer
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ ULONG lastSector;
+ ULONG bps;
+ ULONG lastBit;
+ ULONG tmp;
+
+ ASSERT(ReadCapacityBuffer);
+ ASSERT(commonExtension->IsFdo);
+
+ TraceLog((CdromDebugError,
+ "CdRomInterpretReadCapacity: Entering\n"));
+
+ //
+ // Swizzle bytes from Read Capacity and translate into
+ // the necessary geometry information in the device extension.
+ //
+
+ tmp = ReadCapacityBuffer->BytesPerBlock;
+ ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
+ ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
+ ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
+ ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
+
+ //
+ // Insure that bps is a power of 2.
+ // This corrects a problem with the HP 4020i CDR where it
+ // returns an incorrect number for bytes per sector.
+ //
+
+ if (!bps) {
+ bps = 2048;
+ } else {
+ lastBit = (ULONG) -1;
+ while (bps) {
+ lastBit++;
+ bps = bps >> 1;
+ }
+ bps = 1 << lastBit;
+ }
+
+ fdoExtension->DiskGeometry.BytesPerSector = bps;
+
+ TraceLog((CdromDebugTrace, "CdRomInterpretReadCapacity: Calculated bps %#x\n",
+ fdoExtension->DiskGeometry.BytesPerSector));
+
+ //
+ // Copy last sector in reverse byte order.
+ //
+
+ tmp = ReadCapacityBuffer->LogicalBlockAddress;
+ ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
+ ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
+ ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
+ ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
+
+ //
+ // Calculate sector to byte shift.
+ //
+
+ WHICH_BIT(bps, fdoExtension->SectorShift);
+
+ TraceLog((CdromDebugTrace,"CdRomInterpretReadCapacity: Sector size is %d\n",
+ fdoExtension->DiskGeometry.BytesPerSector));
+
+ TraceLog((CdromDebugTrace,"CdRomInterpretReadCapacity: Number of Sectors is %d\n",
+ lastSector + 1));
+
+ //
+ // Calculate media capacity in bytes.
+ //
+
+ commonExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
+
+ //
+ // we've defaulted to 32/64 forever. don't want to change this now...
+ //
+
+ fdoExtension->DiskGeometry.TracksPerCylinder = 0x40;
+ fdoExtension->DiskGeometry.SectorsPerTrack = 0x20;
+
+ //
+ // Calculate number of cylinders.
+ //
+
+ fdoExtension->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1) / (32 * 64));
+
+ commonExtension->PartitionLength.QuadPart =
+ (commonExtension->PartitionLength.QuadPart << fdoExtension->SectorShift);
+
+
+ ASSERT(TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA));
+
+ //
+ // This device supports removable media.
+ //
+
+ fdoExtension->DiskGeometry.MediaType = RemovableMedia;
+
+ return;
+}
+
+
diff --git a/reactos/drivers/storage/class/cdrom_new/mmc.c b/reactos/drivers/storage/class/cdrom_new/mmc.c
new file mode 100644
index 00000000000..15319bb6c68
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/mmc.c
@@ -0,0 +1,1422 @@
+/*--
+
+Copyright (C) Microsoft Corporation, 2000
+
+Module Name:
+
+ mmc.c
+
+Abstract:
+
+ This file is used to extend cdrom.sys to detect and use mmc-compatible
+ drives' capabilities more wisely.
+
+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 "ntddk.h"
+#include "classpnp.h"
+#include "cdrom.h"
+
+
+
+NTSTATUS
+CdRomGetConfiguration(
+ IN PDEVICE_OBJECT Fdo,
+ OUT PGET_CONFIGURATION_HEADER *Buffer,
+ OUT PULONG BytesReturned,
+ IN FEATURE_NUMBER StartingFeature,
+ IN ULONG RequestedType
+ );
+VOID
+CdRompPrintAllFeaturePages(
+ IN PGET_CONFIGURATION_HEADER Buffer,
+ IN ULONG Usable
+ );
+NTSTATUS
+CdRomUpdateMmcDriveCapabilitiesCompletion(
+ IN PDEVICE_OBJECT Unused,
+ IN PIRP Irp,
+ IN PDEVICE_OBJECT Fdo
+ );
+VOID
+CdRomPrepareUpdateCapabilitiesIrp(
+ PDEVICE_OBJECT Fdo
+ );
+
+/*++
+
+ NOT DOCUMENTED YET - may be called at up to DISPATCH_LEVEL
+ if memory is non-paged
+ PRESUMES ALL DATA IS ACCESSIBLE based on FeatureBuffer
+
+--*/
+VOID
+CdRomFindProfileInProfiles(
+ IN PFEATURE_DATA_PROFILE_LIST ProfileHeader,
+ IN FEATURE_PROFILE_TYPE ProfileToFind,
+ OUT PBOOLEAN Found
+ )
+{
+ PFEATURE_DATA_PROFILE_LIST_EX profile;
+ ULONG numberOfProfiles;
+ ULONG i;
+
+ ASSERT((ProfileHeader->Header.AdditionalLength % 4) == 0);
+
+ *Found = FALSE;
+
+ numberOfProfiles = ProfileHeader->Header.AdditionalLength / 4;
+ profile = ProfileHeader->Profiles; // zero-sized array
+
+ for (i = 0; i < numberOfProfiles; i++) {
+
+ FEATURE_PROFILE_TYPE currentProfile;
+
+ currentProfile =
+ (profile->ProfileNumber[0] << 8) |
+ (profile->ProfileNumber[1] & 0xff);
+
+ if (currentProfile == ProfileToFind) {
+
+ *Found = TRUE;
+
+ }
+
+ profile++;
+ }
+ return;
+
+}
+
+
+/*++
+
+ NOT DOCUMENTED YET - may be called at up to DISPATCH_LEVEL
+ if memory is non-paged
+
+--*/
+PVOID
+CdRomFindFeaturePage(
+ IN PGET_CONFIGURATION_HEADER FeatureBuffer,
+ IN ULONG Length,
+ IN FEATURE_NUMBER Feature
+ )
+{
+ PUCHAR buffer;
+ PUCHAR limit;
+
+ if (Length < sizeof(GET_CONFIGURATION_HEADER) + sizeof(FEATURE_HEADER)) {
+ return NULL;
+ }
+
+ //
+ // set limit to point to first illegal address
+ //
+
+ limit = (PUCHAR)FeatureBuffer;
+ limit += Length;
+
+ //
+ // set buffer to point to first page
+ //
+
+ buffer = FeatureBuffer->Data;
+
+ //
+ // loop through each page until we find the requested one, or
+ // until it's not safe to access the entire feature header
+ // (if equal, have exactly enough for the feature header)
+ //
+ while (buffer + sizeof(FEATURE_HEADER) <= limit) {
+
+ PFEATURE_HEADER header = (PFEATURE_HEADER)buffer;
+ FEATURE_NUMBER thisFeature;
+
+ thisFeature =
+ (header->FeatureCode[0] << 8) |
+ (header->FeatureCode[1]);
+
+ if (thisFeature == Feature) {
+
+ PUCHAR temp;
+
+ //
+ // if don't have enough memory to safely access all the feature
+ // information, return NULL
+ //
+ temp = buffer;
+ temp += sizeof(FEATURE_HEADER);
+ temp += header->AdditionalLength;
+
+ if (temp > limit) {
+
+ //
+ // this means the transfer was cut-off, an insufficiently
+ // small buffer was given, or other arbitrary error. since
+ // it's not safe to view the amount of data (even though
+ // the header is safe) in this feature, pretend it wasn't
+ // transferred at all...
+ //
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "Feature %x exists, but not safe to access all its "
+ "data. returning NULL\n", Feature));
+ return NULL;
+ } else {
+ return buffer;
+ }
+ }
+
+ if (header->AdditionalLength % 4) {
+ ASSERT(!"Feature page AdditionalLength field must be integral multiple of 4!\n");
+ return NULL;
+ }
+
+ buffer += sizeof(FEATURE_HEADER);
+ buffer += header->AdditionalLength;
+
+ }
+ return NULL;
+}
+
+/*++
+
+Private so we can later expose to someone wanting to use a preallocated buffer
+
+--*/
+NTSTATUS
+CdRompGetConfiguration(
+ IN PDEVICE_OBJECT Fdo,
+ IN PGET_CONFIGURATION_HEADER Buffer,
+ IN ULONG BufferSize,
+ OUT PULONG ValidBytes,
+ IN FEATURE_NUMBER StartingFeature,
+ IN ULONG RequestedType
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCDROM_DATA cdData;
+ SCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ ULONG_PTR returned;
+ NTSTATUS status;
+
+ PAGED_CODE();
+ ASSERT(Buffer);
+ ASSERT(ValidBytes);
+
+ *ValidBytes = 0;
+ returned = 0;
+
+ RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+ RtlZeroMemory(Buffer, BufferSize);
+
+ fdoExtension = Fdo->DeviceExtension;
+ cdData = (PCDROM_DATA)(fdoExtension->CommonExtension.DriverData);
+
+ if (TEST_FLAG(cdData->HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT)) {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ srb.TimeOutValue = CDROM_GET_CONFIGURATION_TIMEOUT;
+ srb.CdbLength = 10;
+
+ cdb = (PCDB)srb.Cdb;
+ cdb->GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION;
+ cdb->GET_CONFIGURATION.RequestType = (UCHAR)RequestedType;
+ cdb->GET_CONFIGURATION.StartingFeature[0] = (UCHAR)(StartingFeature >> 8);
+ cdb->GET_CONFIGURATION.StartingFeature[1] = (UCHAR)(StartingFeature & 0xff);
+ cdb->GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(BufferSize >> 8);
+ cdb->GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(BufferSize & 0xff);
+
+ status = ClassSendSrbSynchronous(Fdo, &srb, Buffer,
+ BufferSize, FALSE);
+ returned = srb.DataTransferLength;
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: Status was %x\n", status));
+
+ if (NT_SUCCESS(status) || status == STATUS_BUFFER_OVERFLOW) {
+
+ //
+ // if returned more than can be stored in a ULONG, return false
+ //
+
+ if (returned > (ULONG)(-1)) {
+ return STATUS_UNSUCCESSFUL;
+ }
+ ASSERT(returned <= BufferSize);
+ *ValidBytes = (ULONG)returned;
+ return STATUS_SUCCESS;
+
+ } else {
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: failed %x\n", status));
+ return status;
+
+ }
+ ASSERT(FALSE);
+ return STATUS_UNSUCCESSFUL;
+}
+
+/*++
+
+ Allocates buffer with configuration info, returns STATUS_SUCCESS
+ or an error if one occurred
+
+ NOTE: does not handle case where more than 65000 bytes are returned,
+ which requires multiple calls with different starting feature
+ numbers.
+
+--*/
+NTSTATUS
+CdRomGetConfiguration(
+ IN PDEVICE_OBJECT Fdo,
+ OUT PGET_CONFIGURATION_HEADER *Buffer,
+ OUT PULONG BytesReturned,
+ IN FEATURE_NUMBER StartingFeature,
+ IN ULONG RequestedType
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ GET_CONFIGURATION_HEADER header; // eight bytes, not a lot
+ PGET_CONFIGURATION_HEADER buffer;
+ ULONG returned;
+ ULONG size;
+ ULONG i;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+
+ fdoExtension = Fdo->DeviceExtension;
+ *Buffer = NULL;
+ *BytesReturned = 0;
+
+ buffer = NULL;
+ returned = 0;
+
+ //
+ // send the first request down to just get the header
+ //
+
+ status = CdRompGetConfiguration(Fdo, &header, sizeof(header),
+ &returned, StartingFeature, RequestedType);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ //
+ // now try again, using information returned to allocate
+ // just enough memory
+ //
+
+ size = header.DataLength[0] << 24 |
+ header.DataLength[1] << 16 |
+ header.DataLength[2] << 8 |
+ header.DataLength[3] << 0 ;
+
+
+ for (i = 0; i < 4; i++) {
+
+ //
+ // the datalength field is the size *following*
+ // itself, so adjust accordingly
+ //
+
+ size += 4*sizeof(UCHAR);
+
+ //
+ // make sure the size is reasonable
+ //
+
+ if (size <= sizeof(FEATURE_HEADER)) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: drive reports only %x bytes?\n",
+ size));
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // allocate the memory
+ //
+
+ buffer = (PGET_CONFIGURATION_HEADER)
+ ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ size,
+ CDROM_TAG_FEATURE);
+
+ if (buffer == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // send the first request down to just get the header
+ //
+
+ status = CdRompGetConfiguration(Fdo, buffer, size, &returned,
+ StartingFeature, RequestedType);
+
+ if (!NT_SUCCESS(status)) {
+ ExFreePool(buffer);
+ return status;
+ }
+
+ if (returned > size) {
+ ExFreePool(buffer);
+ return STATUS_INTERNAL_ERROR;
+ }
+
+ returned = buffer->DataLength[0] << 24 |
+ buffer->DataLength[1] << 16 |
+ buffer->DataLength[2] << 8 |
+ buffer->DataLength[3] << 0 ;
+ returned += 4*sizeof(UCHAR);
+
+ if (returned <= size) {
+ *Buffer = buffer;
+ *BytesReturned = size; // amount of 'safe' memory
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // else retry using the new size....
+ //
+
+ size = returned;
+ ExFreePool(buffer);
+ buffer = NULL;
+
+ }
+
+ //
+ // it failed after a number of attempts, so just fail.
+ //
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomGetConfiguration: Failed %d attempts to get all feature "
+ "information\n", i));
+ return STATUS_IO_DEVICE_ERROR;
+}
+
+VOID
+CdRomIsDeviceMmcDevice(
+ IN PDEVICE_OBJECT Fdo,
+ OUT PBOOLEAN IsMmc
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCDROM_DATA cdData = commonExtension->DriverData;
+ GET_CONFIGURATION_HEADER localHeader;
+ NTSTATUS status;
+ ULONG usable;
+ ULONG size;
+ ULONG previouslyFailed;
+
+ PAGED_CODE();
+ ASSERT( commonExtension->IsFdo );
+
+ *IsMmc = FALSE;
+
+ //
+ // read the registry in case the drive failed previously,
+ // and a timeout is occurring.
+ //
+
+ previouslyFailed = FALSE;
+ ClassGetDeviceParameter(fdoExtension,
+ CDROM_SUBKEY_NAME,
+ CDROM_NON_MMC_DRIVE_NAME,
+ &previouslyFailed
+ );
+
+ if (previouslyFailed) {
+ SET_FLAG(cdData->HackFlags, CDROM_HACK_BAD_GET_CONFIG_SUPPORT);
+ }
+
+ //
+ // check for the following profiles:
+ //
+ // ProfileList
+ //
+
+ status = CdRompGetConfiguration(Fdo,
+ &localHeader,
+ sizeof(localHeader),
+ &usable,
+ FeatureProfileList,
+ SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+
+ if (status == STATUS_INVALID_DEVICE_REQUEST ||
+ status == STATUS_NO_MEDIA_IN_DEVICE ||
+ status == STATUS_IO_DEVICE_ERROR ||
+ status == STATUS_IO_TIMEOUT) {
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "GetConfiguration Failed (%x), device %p not mmc-compliant\n",
+ status, Fdo
+ ));
+ previouslyFailed = TRUE;
+ ClassSetDeviceParameter(fdoExtension,
+ CDROM_SUBKEY_NAME,
+ CDROM_NON_MMC_DRIVE_NAME,
+ previouslyFailed
+ );
+ return;
+
+ } else if (!NT_SUCCESS(status)) {
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError,
+ "GetConfiguration Failed, status %x -- defaulting to -ROM\n",
+ status));
+ return;
+
+ } else if (usable < sizeof(GET_CONFIGURATION_HEADER)) {
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "GetConfiguration Failed, returned only %x bytes!\n", usable));
+ previouslyFailed = TRUE;
+ ClassSetDeviceParameter(fdoExtension,
+ CDROM_SUBKEY_NAME,
+ CDROM_NON_MMC_DRIVE_NAME,
+ previouslyFailed
+ );
+ return;
+
+ }
+
+ size = (localHeader.DataLength[0] << 24) |
+ (localHeader.DataLength[1] << 16) |
+ (localHeader.DataLength[2] << 8) |
+ (localHeader.DataLength[3] << 0);
+
+ if(size <= 4) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "GetConfiguration Failed, claims MMC support but doesn't "
+ "correctly return config length!\n"));
+ return;
+ }
+
+ size += 4; // sizeof the datalength fields
+
+#if DBG
+ {
+ PGET_CONFIGURATION_HEADER dbgBuffer;
+ NTSTATUS dbgStatus;
+
+ dbgBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ (SIZE_T)size,
+ CDROM_TAG_FEATURE);
+ if (dbgBuffer != NULL) {
+ RtlZeroMemory(dbgBuffer, size);
+
+ dbgStatus = CdRompGetConfiguration(Fdo, dbgBuffer, size,
+ &size, FeatureProfileList,
+ SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+
+ if (NT_SUCCESS(dbgStatus)) {
+ CdRompPrintAllFeaturePages(dbgBuffer, usable);
+ }
+ ExFreePool(dbgBuffer);
+ }
+ }
+#endif // DBG
+
+ *IsMmc = TRUE;
+ return;
+}
+
+VOID
+CdRompPrintAllFeaturePages(
+ IN PGET_CONFIGURATION_HEADER Buffer,
+ IN ULONG Usable
+ )
+{
+ PFEATURE_HEADER header;
+
+////////////////////////////////////////////////////////////////////////////////
+// items expected to ALWAYS be current
+////////////////////////////////////////////////////////////////////////////////
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureProfileList);
+ if (header != NULL) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: CurrentProfile %x "
+ "with %x bytes of data at %p\n",
+ Buffer->CurrentProfile[0] << 8 |
+ Buffer->CurrentProfile[1],
+ Usable, Buffer));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureCore);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "CORE Features"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureMorphing);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Morphing"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureMultiRead);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Multi-Read"
+ ));
+ }
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureRemovableMedium);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Removable Medium"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureTimeout);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Timeouts"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeaturePowerManagement);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Power Management"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureEmbeddedChanger);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Embedded Changer"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureLogicalUnitSerialNumber);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "LUN Serial Number"
+ ));
+ }
+
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureMicrocodeUpgrade);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Microcode Update"
+ ));
+ }
+
+////////////////////////////////////////////////////////////////////////////////
+// items expected not to always be current
+////////////////////////////////////////////////////////////////////////////////
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureCDAudioAnalogPlay);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Analogue CD Audio Operations"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureCdRead);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "reading from CD-ROM/R/RW"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureCdMastering);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "CD Recording (Mastering)"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureCdTrackAtOnce);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "CD Recording (Track At Once)"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureDvdCSS);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "DVD CSS"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureDvdRead);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "DVD Structure Reads"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureDvdRecordableWrite);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "DVD Recording (Mastering)"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureDiscControlBlocks);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "DVD Disc Control Blocks"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureFormattable);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Formatting"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureRandomReadable);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Random Reads"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureRandomWritable);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Random Writes"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureRestrictedOverwrite);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Restricted Overwrites."
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureWriteOnce);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Write Once Media"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureSectorErasable);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Sector Erasable Media"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureIncrementalStreamingWritable);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Incremental Streaming Writing"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureRealTimeStreaming);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "Real-time Streaming Reads"
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureSMART);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "S.M.A.R.T."
+ ));
+ }
+
+ header = CdRomFindFeaturePage(Buffer, Usable, FeatureDefectManagement);
+ if (header) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdromGetConfiguration: %s %s\n",
+ (header->Current ?
+ "Currently supports" : "Is able to support"),
+ "defect management"
+ ));
+ }
+ return;
+}
+
+NTSTATUS
+CdRomUpdateMmcDriveCapabilitiesCompletion(
+ IN PDEVICE_OBJECT Unused,
+ IN PIRP Irp,
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCDROM_DATA cdData = fdoExtension->CommonExtension.DriverData;
+ PCDROM_MMC_EXTENSION mmcData = &(cdData->Mmc);
+ PSCSI_REQUEST_BLOCK srb = &(mmcData->CapabilitiesSrb);
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PIRP delayedIrp;
+
+ // completion routine should retry as neccessary.
+ // when success, clear the flag to allow startio to proceed.
+ // else fail original request when retries are exhausted.
+
+ ASSERT(mmcData->CapabilitiesIrp == Irp);
+
+ // for now, if succeeded, just print the new pages.
+
+ if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ //
+ // ISSUE-2000/4/20-henrygab - should we try to reallocate if size
+ // available became larger than what we
+ // originally allocated? otherwise, it
+ // is possible (not probable) that we
+ // would miss the feature. can check
+ // that by finding out what the last
+ // feature is in the current group.
+ //
+
+ BOOLEAN retry;
+ ULONG retryInterval;
+
+ //
+ // Release the queue if it is frozen.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+ ClassReleaseQueue(Fdo);
+ }
+
+ retry = ClassInterpretSenseInfo(
+ Fdo,
+ srb,
+ irpStack->MajorFunction,
+ 0,
+ MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
+ &status,
+ &retryInterval);
+
+ //
+ // DATA_OVERRUN is not an error in this case....
+ //
+
+ if (status == STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ }
+
+ //
+ // override verify_volume based on original irp's settings
+ //
+
+ if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ status == STATUS_VERIFY_REQUIRED) {
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ if (retry && irpStack->Parameters.Others.Argument4--) {
+
+ LARGE_INTEGER delay;
+ delay.QuadPart = retryInterval;
+ delay.QuadPart *= (LONGLONG)1000 * 1000 * 10;
+
+ //
+ // retry the request
+ //
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugError,
+ "Not using ClassRetryRequest Yet\n"));
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "Retry update capabilities %p\n", Irp));
+ CdRomPrepareUpdateCapabilitiesIrp(Fdo);
+
+ CdRomRetryRequest(fdoExtension, Irp, retryInterval, TRUE);
+
+ //
+ // ClassRetryRequest(Fdo, Irp, delay);
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ }
+
+ } else {
+
+ status = STATUS_SUCCESS;
+
+ }
+
+ Irp->IoStatus.Status = status;
+
+ KeSetEvent(&mmcData->CapabilitiesEvent, IO_CD_ROM_INCREMENT, FALSE);
+
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+VOID
+CdRomPrepareUpdateCapabilitiesIrp(
+ PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCDROM_DATA cdData = fdoExtension->CommonExtension.DriverData;
+ PCDROM_MMC_EXTENSION mmcData = &(cdData->Mmc);
+ PIO_STACK_LOCATION nextStack;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ ULONG bufferSize;
+ PIRP irp;
+
+ ASSERT(mmcData->UpdateState);
+ ASSERT(ExQueryDepthSList(&(mmcData->DelayedIrps)) != 0);
+ ASSERT(mmcData->CapabilitiesIrp != NULL);
+ ASSERT(mmcData->CapabilitiesMdl != NULL);
+ ASSERT(mmcData->CapabilitiesBuffer);
+ ASSERT(mmcData->CapabilitiesBufferSize != 0);
+ ASSERT(fdoExtension->SenseData);
+
+ //
+ // do *NOT* call IoReuseIrp(), since it would zero out our
+ // current irp stack location, which we really don't want
+ // to happen. it would also set the current irp stack location
+ // to one greater than currently exists (to give max irp usage),
+ // but we don't want that either, since we use the top irp stack.
+ //
+ // IoReuseIrp(mmcData->CapabilitiesIrp, STATUS_UNSUCCESSFUL);
+ //
+
+ irp = mmcData->CapabilitiesIrp;
+ srb = &(mmcData->CapabilitiesSrb);
+ cdb = (PCDB)(srb->Cdb);
+ bufferSize = mmcData->CapabilitiesBufferSize;
+
+ //
+ // zero stuff out
+ //
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+ RtlZeroMemory(fdoExtension->SenseData, sizeof(SENSE_DATA));
+ RtlZeroMemory(mmcData->CapabilitiesBuffer, bufferSize);
+
+ //
+ // setup the srb
+ //
+
+ srb->TimeOutValue = CDROM_GET_CONFIGURATION_TIMEOUT;
+ srb->Length = SCSI_REQUEST_BLOCK_SIZE;
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+ srb->SenseInfoBuffer = fdoExtension->SenseData;
+ srb->DataBuffer = mmcData->CapabilitiesBuffer;
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->DataTransferLength = mmcData->CapabilitiesBufferSize;
+ srb->ScsiStatus = 0;
+ srb->SrbStatus = 0;
+ srb->NextSrb = NULL;
+ srb->OriginalRequest = irp;
+ srb->SrbFlags = fdoExtension->SrbFlags;
+ srb->CdbLength = 10;
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ //
+ // setup the cdb
+ //
+
+ cdb->GET_CONFIGURATION.OperationCode = SCSIOP_GET_CONFIGURATION;
+ cdb->GET_CONFIGURATION.RequestType = SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT;
+ cdb->GET_CONFIGURATION.StartingFeature[0] = 0;
+ cdb->GET_CONFIGURATION.StartingFeature[1] = 0;
+ cdb->GET_CONFIGURATION.AllocationLength[0] = (UCHAR)(bufferSize >> 8);
+ cdb->GET_CONFIGURATION.AllocationLength[1] = (UCHAR)(bufferSize & 0xff);
+
+ //
+ // setup the irp
+ //
+
+ nextStack = IoGetNextIrpStackLocation(irp);
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+ nextStack->Parameters.Scsi.Srb = srb;
+ irp->MdlAddress = mmcData->CapabilitiesMdl;
+ irp->AssociatedIrp.SystemBuffer = mmcData->CapabilitiesBuffer;
+ IoSetCompletionRoutine(irp, CdRomUpdateMmcDriveCapabilitiesCompletion, Fdo,
+ TRUE, TRUE, TRUE);
+
+ return;
+
+}
+
+VOID
+CdRomUpdateMmcDriveCapabilities(
+ IN PDEVICE_OBJECT Fdo,
+ IN PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCDROM_DATA cdData = fdoExtension->CommonExtension.DriverData;
+ PCDROM_MMC_EXTENSION mmcData = &(cdData->Mmc);
+ PIO_STACK_LOCATION thisStack = IoGetCurrentIrpStackLocation(mmcData->CapabilitiesIrp);
+ PSCSI_REQUEST_BLOCK srb = &(mmcData->CapabilitiesSrb);
+ NTSTATUS status;
+
+
+ ASSERT(Context == NULL);
+
+ //
+ // NOTE: a remove lock is unneccessary, since the delayed irp
+ // will have said lock held for itself, preventing a remove.
+ //
+ CdRomPrepareUpdateCapabilitiesIrp(Fdo);
+
+ ASSERT(thisStack->Parameters.Others.Argument1 == Fdo);
+ ASSERT(thisStack->Parameters.Others.Argument2 == mmcData->CapabilitiesBuffer);
+ ASSERT(thisStack->Parameters.Others.Argument3 == &(mmcData->CapabilitiesSrb));
+
+ mmcData->WriteAllowed = FALSE; // default to read-only
+
+ //
+ // set max retries, and also allow volume verify override based on
+ // original (delayed) irp
+ //
+
+ thisStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+ //
+ // send to self... note that SL_OVERRIDE_VERIFY_VOLUME is not required,
+ // as this is IRP_MJ_INTERNAL_DEVICE_CONTROL
+ //
+
+ IoCallDriver(commonExtension->LowerDeviceObject, mmcData->CapabilitiesIrp);
+
+ KeWaitForSingleObject(&mmcData->CapabilitiesEvent,
+ Executive, KernelMode, FALSE, NULL);
+
+ status = mmcData->CapabilitiesIrp->IoStatus.Status;
+
+ if (!NT_SUCCESS(status)) {
+
+ goto FinishDriveUpdate;
+
+ }
+
+ //
+ // we've updated the feature set, so update whether or not reads and writes
+ // are allowed or not.
+ //
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => Succeeded "
+ "--------------------"
+ "--------------------\n"));
+
+ /*++
+
+ NOTE: It is important to only use srb->DataTransferLength worth
+ of data at this point, since the bufferSize is what is
+ *available* to use, not what was *actually* used.
+
+ --*/
+
+#if DBG
+ CdRompPrintAllFeaturePages(mmcData->CapabilitiesBuffer,
+ srb->DataTransferLength);
+#endif // DBG
+
+ //
+ // update whether or not writes are allowed. this is currently defined
+ // as requiring TargetDefectManagement and RandomWritable features
+ //
+ {
+ PFEATURE_HEADER defectHeader;
+ PFEATURE_HEADER writableHeader;
+
+ defectHeader = CdRomFindFeaturePage(mmcData->CapabilitiesBuffer,
+ srb->DataTransferLength,
+ FeatureDefectManagement);
+ writableHeader = CdRomFindFeaturePage(mmcData->CapabilitiesBuffer,
+ srb->DataTransferLength,
+ FeatureRandomWritable);
+
+ if ((defectHeader != NULL) && (writableHeader != NULL) &&
+ (defectHeader->Current) && (writableHeader->Current)) {
+
+ //
+ // this should be the *ONLY* place writes are set to allowed
+ //
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => Writes *allowed*\n"));
+ mmcData->WriteAllowed = TRUE;
+
+ } else {
+
+ if (defectHeader == NULL) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => No writes - %s = %s\n",
+ "defect management", "DNE"));
+ } else {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => No writes - %s = %s\n",
+ "defect management", "Not Current"));
+ }
+ if (writableHeader == NULL) {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => No writes - %s = %s\n",
+ "sector writable", "DNE"));
+ } else {
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => No writes - %s = %s\n",
+ "sector writable", "Not Current"));
+ }
+ } // end of feature checking
+ } // end of check for writability
+
+ //
+ // update the cached partition table information
+ //
+ // NOTE: THIS WILL CURRENTLY CAUSE A DEADLOCK!
+ //
+ // ISSUE-2000/06/20-henrygab - partition support not implemented
+ // IoReadPartitionTable must be done
+ // at PASSIVE level, requiring a thread
+ // or worker item or other such method.
+ //
+#if 0
+ status = IoReadPartitionTable(Fdo, 1 << fdoExtension->SectorShift,
+ TRUE, &mmcData->PartitionList);
+ if (!NT_SUCCESS(status)) {
+
+ goto FinishDriveUpdate;
+
+ }
+#endif
+
+ status = STATUS_SUCCESS;
+
+FinishDriveUpdate:
+
+ CdRompFlushDelayedList(Fdo, mmcData, status, TRUE);
+
+ return;
+}
+
+
+VOID
+CdRompFlushDelayedList(
+ IN PDEVICE_OBJECT Fdo,
+ IN PCDROM_MMC_EXTENSION MmcData,
+ IN NTSTATUS Status,
+ IN BOOLEAN CalledFromWorkItem
+ )
+{
+ PSINGLE_LIST_ENTRY list;
+ PIRP irp;
+
+ // NOTE - REF #0002
+ //
+ // need to set the new state first to prevent deadlocks.
+ // this is only done from the workitem, to prevent any
+ // edge cases where we'd "lose" the UpdateRequired
+ //
+ // then, must ignore the state, since it's not guaranteed to
+ // be the same any longer. the only thing left is to handle
+ // all the delayed irps by flushing the queue and sending them
+ // back onto the StartIo queue for the device.
+ //
+
+ if (CalledFromWorkItem) {
+
+ LONG oldState;
+ LONG newState;
+
+ if (NT_SUCCESS(Status)) {
+ newState = CdromMmcUpdateComplete;
+ } else {
+ newState = CdromMmcUpdateRequired;
+ }
+
+ oldState = InterlockedCompareExchange(&MmcData->UpdateState,
+ newState,
+ CdromMmcUpdateStarted);
+ ASSERT(oldState == CdromMmcUpdateStarted);
+
+ } else {
+
+ //
+ // just flushing the queue if not called from the workitem,
+ // and we don't want to ever fail the queue in those cases.
+ //
+
+ ASSERT(NT_SUCCESS(Status));
+
+ }
+
+ list = ExInterlockedFlushSList(&MmcData->DelayedIrps);
+
+ // if this assert fires, it means that we have started
+ // a workitem when the previous workitem took the delayed
+ // irp. if this happens, then the logic in HACKHACK #0002
+ // is either flawed or the rules set within are not being
+ // followed. this would require investigation.
+
+ ASSERT(list != NULL);
+
+ //
+ // now either succeed or fail all the delayed irps, according
+ // to the update status.
+ //
+
+ while (list != NULL) {
+
+ irp = (PIRP)( ((PUCHAR)list) -
+ FIELD_OFFSET(IRP, Tail.Overlay.DriverContext[0])
+ );
+ list = list->Next;
+ irp->Tail.Overlay.DriverContext[0] = 0;
+ irp->Tail.Overlay.DriverContext[1] = 0;
+ irp->Tail.Overlay.DriverContext[2] = 0;
+ irp->Tail.Overlay.DriverContext[3] = 0;
+
+ if (NT_SUCCESS(Status)) {
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => Re-sending delayed irp %p\n",
+ irp));
+ IoStartPacket(Fdo, irp, NULL, NULL);
+
+ } else {
+
+ KdPrintEx((DPFLTR_CDROM_ID, CdromDebugFeatures,
+ "CdRomUpdateMmc => Failing delayed irp %p with "
+ " status %x\n", irp, Status));
+ irp->IoStatus.Information = 0;
+ irp->IoStatus.Status = Status;
+ ClassReleaseRemoveLock(Fdo, irp);
+ IoCompleteRequest(irp, IO_CD_ROM_INCREMENT);
+
+ }
+
+ } // while (list)
+
+ return;
+
+}
+
+VOID
+CdRomDeAllocateMmcResources(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCDROM_DATA cddata = commonExtension->DriverData;
+ PCDROM_MMC_EXTENSION mmcData = &cddata->Mmc;
+ NTSTATUS status;
+
+ if (mmcData->CapabilitiesWorkItem) {
+ IoFreeWorkItem(mmcData->CapabilitiesWorkItem);
+ mmcData->CapabilitiesWorkItem = NULL;
+ }
+ if (mmcData->CapabilitiesIrp) {
+ IoFreeIrp(mmcData->CapabilitiesIrp);
+ mmcData->CapabilitiesIrp = NULL;
+ }
+ if (mmcData->CapabilitiesMdl) {
+ IoFreeMdl(mmcData->CapabilitiesMdl);
+ mmcData->CapabilitiesMdl = NULL;
+ }
+ if (mmcData->CapabilitiesBuffer) {
+ ExFreePool(mmcData->CapabilitiesBuffer);
+ mmcData->CapabilitiesBuffer = NULL;
+ }
+ mmcData->CapabilitiesBuffer = 0;
+ mmcData->IsMmc = FALSE;
+ mmcData->WriteAllowed = FALSE;
+
+ return;
+}
+
+NTSTATUS
+CdRomAllocateMmcResources(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCDROM_DATA cddata = commonExtension->DriverData;
+ PCDROM_MMC_EXTENSION mmcData = &cddata->Mmc;
+ PIO_STACK_LOCATION irpStack;
+ NTSTATUS status;
+
+ ASSERT(mmcData->CapabilitiesWorkItem == NULL);
+ ASSERT(mmcData->CapabilitiesIrp == NULL);
+ ASSERT(mmcData->CapabilitiesMdl == NULL);
+ ASSERT(mmcData->CapabilitiesBuffer == NULL);
+ ASSERT(mmcData->CapabilitiesBufferSize == 0);
+
+ status = CdRomGetConfiguration(Fdo,
+ &mmcData->CapabilitiesBuffer,
+ &mmcData->CapabilitiesBufferSize,
+ FeatureProfileList,
+ SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL);
+ if (!NT_SUCCESS(status)) {
+ ASSERT(mmcData->CapabilitiesBuffer == NULL);
+ ASSERT(mmcData->CapabilitiesBufferSize == 0);
+ return status;
+ }
+ ASSERT(mmcData->CapabilitiesBuffer != NULL);
+ ASSERT(mmcData->CapabilitiesBufferSize != 0);
+
+ mmcData->CapabilitiesMdl = IoAllocateMdl(mmcData->CapabilitiesBuffer,
+ mmcData->CapabilitiesBufferSize,
+ FALSE, FALSE, NULL);
+ if (mmcData->CapabilitiesMdl == NULL) {
+ ExFreePool(mmcData->CapabilitiesBuffer);
+ mmcData->CapabilitiesBufferSize = 0;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+
+ mmcData->CapabilitiesIrp = IoAllocateIrp(Fdo->StackSize + 2, FALSE);
+ if (mmcData->CapabilitiesIrp == NULL) {
+ IoFreeMdl(mmcData->CapabilitiesMdl);
+ ExFreePool(mmcData->CapabilitiesBuffer);
+ mmcData->CapabilitiesBufferSize = 0;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ mmcData->CapabilitiesWorkItem = IoAllocateWorkItem(Fdo);
+ if (mmcData->CapabilitiesWorkItem == NULL) {
+ IoFreeIrp(mmcData->CapabilitiesIrp);
+ IoFreeMdl(mmcData->CapabilitiesMdl);
+ ExFreePool(mmcData->CapabilitiesBuffer);
+ mmcData->CapabilitiesBufferSize = 0;
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // everything has been allocated, so now prepare it all....
+ //
+
+ MmBuildMdlForNonPagedPool(mmcData->CapabilitiesMdl);
+ ExInitializeSListHead(&mmcData->DelayedIrps);
+ KeInitializeSpinLock(&mmcData->DelayedLock);
+
+ //
+ // use the extra stack for internal bookkeeping
+ //
+ IoSetNextIrpStackLocation(mmcData->CapabilitiesIrp);
+ irpStack = IoGetCurrentIrpStackLocation(mmcData->CapabilitiesIrp);
+ irpStack->Parameters.Others.Argument1 = Fdo;
+ irpStack->Parameters.Others.Argument2 = mmcData->CapabilitiesBuffer;
+ irpStack->Parameters.Others.Argument3 = &(mmcData->CapabilitiesSrb);
+ // arg 4 is the retry count
+
+ //
+ // set the completion event to FALSE for now
+ //
+
+ KeInitializeEvent(&mmcData->CapabilitiesEvent,
+ SynchronizationEvent, FALSE);
+ return STATUS_SUCCESS;
+
+}
+
diff --git a/reactos/drivers/storage/class/cdrom_new/scsicdrm.rc b/reactos/drivers/storage/class/cdrom_new/scsicdrm.rc
new file mode 100644
index 00000000000..cac5f53da93
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/scsicdrm.rc
@@ -0,0 +1,23 @@
+//+-------------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (C) Microsoft Corporation, 1996 - 1999
+//
+// File: scsicdrm.rc
+//
+//--------------------------------------------------------------------------
+
+#include
+
+#include
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "SCSI CD-ROM Driver"
+#define VER_INTERNALNAME_STR "cdrom.sys"
+#define VER_ORIGINALFILENAME_STR "cdrom.sys"
+#define VER_LANGNEUTRAL
+
+#include "common.ver"
+
diff --git a/reactos/drivers/storage/class/cdrom_new/sec.c b/reactos/drivers/storage/class/cdrom_new/sec.c
new file mode 100644
index 00000000000..60b70768954
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/sec.c
@@ -0,0 +1,38 @@
+/*--
+
+Copyright (C) Microsoft Corporation, 1999
+
+--*/
+
+
+#include "sec.h"
+
+
+NTSTATUS
+CdRomGetRpc0Settings(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCDROM_DATA cddata = (PCDROM_DATA)(commonExtension->DriverData);
+
+ cddata->Rpc0SystemRegion = (UCHAR)(~1); // region one
+ cddata->Rpc0SystemRegionResetCount = 0; // no resets
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CdRomSetRpc0Settings(
+ IN PDEVICE_OBJECT Fdo,
+ IN UCHAR NewRegion
+ )
+{
+ return STATUS_SUCCESS;
+}
+
+
+
+
+
diff --git a/reactos/drivers/storage/class/cdrom_new/sec.h b/reactos/drivers/storage/class/cdrom_new/sec.h
new file mode 100644
index 00000000000..ce8b6272dba
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/sec.h
@@ -0,0 +1,11 @@
+/*--
+
+Copyright (C) Microsoft Corporation, 1999
+
+--*/
+
+
+#include "ntddk.h"
+#include "classpnp.h"
+#include "cdrom.h"
+
diff --git a/reactos/drivers/storage/class/cdrom_new/sources b/reactos/drivers/storage/class/cdrom_new/sources
new file mode 100644
index 00000000000..6fbd1284b9e
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/sources
@@ -0,0 +1,32 @@
+!IF 0
+
+Copyright (C) Microsoft Corporation, 1996 - 1999
+
+Module Name:
+
+ sources.
+
+!ENDIF
+
+TARGETNAME=cdrom
+TARGETPATH=obj
+TARGETTYPE=DRIVER
+
+TARGETLIBS=\
+ $(DDK_LIB_PATH)\classpnp.lib \
+ $(DDK_LIB_PATH)\ntoskrnl.lib
+
+INCLUDES=..\inc;..\..\inc
+
+SOURCES=\
+ data.c \
+ cdrom.c \
+ ioctl.c \
+ mmc.c \
+ scsicdrm.rc \
+ sec.c
+
+RUN_WPP=$(SOURCES)\
+ -km\
+ -func:TraceLog((LEVEL,MSG,...))
+
diff --git a/reactos/drivers/storage/class/cdrom_new/trace.h b/reactos/drivers/storage/class/cdrom_new/trace.h
new file mode 100644
index 00000000000..2db702cf3f1
--- /dev/null
+++ b/reactos/drivers/storage/class/cdrom_new/trace.h
@@ -0,0 +1,53 @@
+/*
+
+ WPP_DEFINE_CONTROL_GUID specifies the GUID used for this filter.
+ *** REPLACE THE GUID WITH YOUR OWN UNIQUE ID ***
+ WPP_DEFINE_BIT allows setting debug bit masks to selectively print.
+
+ everything else can revert to the default?
+
+*/
+
+
+#define TraceLogger(x, ...) DbgPrint(__VA_ARGS__)
+#define TraceLog(x) TraceLogger x
+#define WPP_INIT_TRACING(x, y)
+#define WPP_CLEANUP(x)
+
+#define WPP_CONTROL_GUIDS \
+ WPP_DEFINE_CONTROL_GUID(Cdrom,(58db8e03,0537,45cb,b29b,597f6cbebbfd), \
+ WPP_DEFINE_BIT(CdromDebugError) /* bit 0 = 0x00000001 */ \
+ WPP_DEFINE_BIT(CdromDebugWarning) /* bit 1 = 0x00000002 */ \
+ WPP_DEFINE_BIT(CdromDebugTrace) /* bit 2 = 0x00000004 */ \
+ WPP_DEFINE_BIT(CdromDebugInfo) /* bit 3 = 0x00000008 */ \
+ WPP_DEFINE_BIT(FilterDebugD04) /* bit 4 = 0x00000010 */ \
+ WPP_DEFINE_BIT(FilterDebugD05) /* bit 5 = 0x00000020 */ \
+ WPP_DEFINE_BIT(FilterDebugD06) /* bit 6 = 0x00000040 */ \
+ WPP_DEFINE_BIT(FilterDebugD07) /* bit 7 = 0x00000080 */ \
+ WPP_DEFINE_BIT(FilterDebugD08) /* bit 8 = 0x00000100 */ \
+ WPP_DEFINE_BIT(FilterDebugD09) /* bit 9 = 0x00000200 */ \
+ WPP_DEFINE_BIT(FilterDebugD10) /* bit 10 = 0x00000400 */ \
+ WPP_DEFINE_BIT(FilterDebugD11) /* bit 11 = 0x00000800 */ \
+ WPP_DEFINE_BIT(FilterDebugD12) /* bit 12 = 0x00001000 */ \
+ WPP_DEFINE_BIT(FilterDebugD13) /* bit 13 = 0x00002000 */ \
+ WPP_DEFINE_BIT(FilterDebugD14) /* bit 14 = 0x00004000 */ \
+ WPP_DEFINE_BIT(FilterDebugD15) /* bit 15 = 0x00008000 */ \
+ WPP_DEFINE_BIT(FilterDebugD16) /* bit 16 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD17) /* bit 17 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD18) /* bit 18 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD19) /* bit 19 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD20) /* bit 20 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD21) /* bit 21 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD22) /* bit 22 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD23) /* bit 23 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD24) /* bit 24 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD25) /* bit 25 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD26) /* bit 26 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD27) /* bit 27 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD28) /* bit 28 = 0x00000000 */ \
+ WPP_DEFINE_BIT(FilterDebugD29) /* bit 29 = 0x00000000 */ \
+ WPP_DEFINE_BIT(CdromSecError) /* bit 30 = 0x00000000 */ \
+ WPP_DEFINE_BIT(CdromSecInfo) /* bit 31 = 0x00000000 */ \
+ )
+
+
diff --git a/reactos/drivers/storage/class/directory.rbuild b/reactos/drivers/storage/class/directory.rbuild
index 0cfffe09e30..30dbef9435e 100644
--- a/reactos/drivers/storage/class/directory.rbuild
+++ b/reactos/drivers/storage/class/directory.rbuild
@@ -4,6 +4,9 @@
+
+
+
diff --git a/reactos/drivers/storage/classpnp/autorun.c b/reactos/drivers/storage/classpnp/autorun.c
new file mode 100644
index 00000000000..0d2f36d4a52
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/autorun.c
@@ -0,0 +1,3611 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ autorun.c
+
+Abstract:
+
+ Code for support of media change detection in the class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#define GESN_TIMEOUT_VALUE (0x4)
+#define GESN_BUFFER_SIZE (0x8)
+#define MAXIMUM_IMMEDIATE_MCN_RETRIES (0x20)
+#define MCN_REG_SUBKEY_NAME (L"MediaChangeNotification")
+#define MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME (L"AlwaysDisableMCN")
+#define MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME (L"AlwaysEnableMCN")
+
+GUID StoragePredictFailureEventGuid = WMI_STORAGE_PREDICT_FAILURE_EVENT_GUID;
+
+//
+// Only send polling irp when device is fully powered up and a
+// power down irp is not in progress.
+//
+// NOTE: This helps close a window in time where a polling irp could cause
+// a drive to spin up right after it has powered down. The problem is
+// that SCSIPORT, ATAPI and SBP2 will be in the process of powering
+// down (which may take a few seconds), but won't know that. It would
+// then get a polling irp which will be put into its queue since it
+// the disk isn't powered down yet. Once the disk is powered down it
+// will find the polling irp in the queue and then power up the
+// device to do the poll. They do not want to check if the polling
+// irp has the SRB_NO_KEEP_AWAKE flag here since it is in a critical
+// path and would slow down all I/Os. A better way to fix this
+// would be to serialize the polling and power down irps so that
+// only one of them is sent to the device at a time.
+//
+#define ClasspCanSendPollingIrp(fdoExtension) \
+ ((fdoExtension->DevicePowerState == PowerDeviceD0) && \
+ (! fdoExtension->PowerDownInProgress) )
+
+BOOLEAN
+ClasspIsMediaChangeDisabledDueToHardwareLimitation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+NTSTATUS
+ClasspMediaChangeDeviceInstanceOverride(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PBOOLEAN Enabled
+ );
+
+BOOLEAN
+ClasspIsMediaChangeDisabledForClass(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+VOID
+ClasspSetMediaChangeStateEx(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN MEDIA_CHANGE_DETECTION_STATE NewState,
+ IN BOOLEAN Wait,
+ IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
+ );
+
+NTSTATUS
+ClasspMediaChangeRegistryCallBack(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+VOID
+ClasspSendMediaStateIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info,
+ IN ULONG CountDown
+ );
+
+VOID
+ClasspFailurePredict(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PFAILURE_PREDICTION_INFO Info
+ );
+
+NTSTATUS
+ClasspInitializePolling(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN BOOLEAN AllowDriveToSleep
+ );
+
+
+#if ALLOC_PRAGMA
+
+#pragma alloc_text(PAGE, ClassInitializeMediaChangeDetection)
+#pragma alloc_text(PAGE, ClassEnableMediaChangeDetection)
+#pragma alloc_text(PAGE, ClassDisableMediaChangeDetection)
+#pragma alloc_text(PAGE, ClassCleanupMediaChangeDetection)
+#pragma alloc_text(PAGE, ClasspMediaChangeRegistryCallBack)
+#pragma alloc_text(PAGE, ClasspInitializePolling)
+
+#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledDueToHardwareLimitation)
+#pragma alloc_text(PAGE, ClasspMediaChangeDeviceInstanceOverride)
+#pragma alloc_text(PAGE, ClasspIsMediaChangeDisabledForClass)
+
+#pragma alloc_text(PAGE, ClassSetFailurePredictionPoll)
+#pragma alloc_text(PAGE, ClasspDisableTimer)
+#pragma alloc_text(PAGE, ClasspEnableTimer)
+
+#endif
+
+// ISSUE -- make this public?
+VOID
+ClassSendEjectionNotification(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ //
+ // For post-NT5.1 work, need to move EjectSynchronizationEvent
+ // to be a MUTEX so we can attempt to grab it here and benefit
+ // from deadlock detection. This will allow checking if the media
+ // has been locked by programs before broadcasting these events.
+ // (what's the point of broadcasting if the media is not locked?)
+ //
+ // This would currently only be a slight optimization. For post-NT5.1,
+ // it would allow us to send a single PERSISTENT_PREVENT to MMC devices,
+ // thereby cleaning up a lot of the ejection code. Then, when the
+ // ejection request occured, we could see if any locks for the media
+ // existed. if locked, broadcast. if not, we send the eject irp.
+ //
+
+ //
+ // for now, just always broadcast. make this a public routine,
+ // so class drivers can add special hacks to broadcast this for their
+ // non-MMC-compliant devices also from sense codes.
+ //
+
+ DBGTRACE(ClassDebugTrace, ("ClassSendEjectionNotification: media EJECT_REQUEST"));
+ ClasspSendNotification(FdoExtension,
+ &GUID_IO_MEDIA_EJECT_REQUEST,
+ 0,
+ NULL);
+ return;
+}
+
+
+VOID
+ClasspSendNotification(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN const GUID * Guid,
+ IN ULONG ExtraDataSize,
+ IN PVOID ExtraData
+ )
+{
+ PTARGET_DEVICE_CUSTOM_NOTIFICATION notification;
+ ULONG requiredSize;
+
+ requiredSize =
+ (sizeof(TARGET_DEVICE_CUSTOM_NOTIFICATION) - sizeof(UCHAR)) +
+ ExtraDataSize;
+
+ if (requiredSize > 0x0000ffff) {
+ // MAX_USHORT, max total size for these events!
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
+ "Error sending event: size too large! (%x)\n",
+ requiredSize));
+ return;
+ }
+
+ notification = ExAllocatePoolWithTag(NonPagedPool,
+ requiredSize,
+ 'oNcS');
+
+ //
+ // if none allocated, exit
+ //
+
+ if (notification == NULL) {
+ return;
+ }
+
+ //
+ // Prepare and send the request!
+ //
+
+ RtlZeroMemory(notification, requiredSize);
+ notification->Version = 1;
+ notification->Size = (USHORT)(requiredSize);
+ notification->FileObject = NULL;
+ notification->NameBufferOffset = -1;
+ notification->Event = *Guid;
+ RtlCopyMemory(notification->CustomDataBuffer, ExtraData, ExtraDataSize);
+
+ IoReportTargetDeviceChangeAsynchronous(FdoExtension->LowerPdo,
+ notification,
+ NULL, NULL);
+
+ ExFreePool(notification);
+ notification = NULL;
+ return;
+}
+
+
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspInterpretGesnData()
+
+Routine Description:
+
+ This routine will interpret the data returned for a GESN command, and
+ (if appropriate) set the media change event, and broadcast the
+ appropriate events to user mode for applications who care.
+
+Arguments:
+
+ FdoExtension - the device
+
+ DataBuffer - the resulting data from a GESN event.
+ requires at least EIGHT valid bytes (header == 4, data == 4)
+
+ ResendImmediately - whether or not to immediately resend the request.
+ this should be FALSE if there was no event, FALSE if the reported
+ event was of the DEVICE BUSY class, else true.
+
+Return Value:
+
+ None
+
+Notes:
+
+ DataBuffer must be at least four bytes of valid data (header == 4 bytes),
+ and have at least eight bytes of allocated memory (all events == 4 bytes).
+
+ The call to StartNextPacket may occur before this routine is completed.
+ the operational change notifications are informational in nature, and
+ while useful, are not neccessary to ensure proper operation. For example,
+ if the device morphs to no longer supporting WRITE commands, all further
+ write commands will fail. There exists a small timing window wherein
+ IOCTL_IS_DISK_WRITABLE may be called and get an incorrect response. If
+ a device supports software write protect, it is expected that the
+ application can handle such a case.
+
+ NOTE: perhaps setting the updaterequired byte to one should be done here.
+ if so, it relies upon the setting of a 32-byte value to be an atomic
+ operation. unfortunately, there is no simple way to notify a class driver
+ which wants to know that the device behavior requires updating.
+
+ Not ready events may be sent every second. For example, if we were
+ to minimize the number of asynchronous notifications, an application may
+ register just after a large busy time was reported. This would then
+ prevent the application from knowing the device was busy until some
+ arbitrarily chosen timeout has occurred. Also, the GESN request would
+ have to still occur, since it checks for non-busy events (such as user
+ keybutton presses and media change events) as well. The specification
+ states that the lower-numered events get reported first, so busy events,
+ while repeating, will only be reported when all other events have been
+ cleared from the device.
+
+--*/
+VOID
+ClasspInterpretGesnData(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PNOTIFICATION_EVENT_STATUS_HEADER Header,
+ IN PBOOLEAN ResendImmediately
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info;
+ LONG dataLength;
+ LONG requiredLength;
+
+ info = FdoExtension->MediaChangeDetectionInfo;
+
+ //
+ // note: don't allocate anything in this routine so that we can
+ // always just 'return'.
+ //
+
+ *ResendImmediately = FALSE;
+
+ if (Header->NEA) {
+ return;
+ }
+ if (Header->NotificationClass == NOTIFICATION_NO_CLASS_EVENTS) {
+ return;
+ }
+
+ //
+ // HACKHACK - REF #0001
+ // This loop is only taken initially, due to the inability to reliably
+ // auto-detect drives that report events correctly at boot. When we
+ // detect this behavior during the normal course of running, we will
+ // disable the hack, allowing more efficient use of the system. This
+ // should occur "nearly" instantly, as the drive should have multiple
+ // events queue'd (ie. power, morphing, media).
+ //
+
+ if (info->Gesn.HackEventMask) {
+
+ //
+ // all events use the low four bytes of zero to indicate
+ // that there was no change in status.
+ //
+
+ UCHAR thisEvent = Header->ClassEventData[0] & 0xf;
+ UCHAR lowestSetBit;
+ UCHAR thisEventBit = (1 << Header->NotificationClass);
+
+ ASSERT(TEST_FLAG(info->Gesn.EventMask, thisEventBit));
+
+
+ //
+ // some bit magic here... this results in the lowest set bit only
+ //
+
+ lowestSetBit = info->Gesn.EventMask;
+ lowestSetBit &= (info->Gesn.EventMask - 1);
+ lowestSetBit ^= (info->Gesn.EventMask);
+
+ if (thisEventBit != lowestSetBit) {
+
+ //
+ // HACKHACK - REF #0001
+ // the first time we ever see an event set that is not the lowest
+ // set bit in the request (iow, highest priority), we know that the
+ // hack is no longer required, as the device is ignoring "no change"
+ // events when a real event is waiting in the other requested queues.
+ //
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN::NONE: Compliant drive found, "
+ "removing GESN hack (%x, %x)\n",
+ thisEventBit, info->Gesn.EventMask));
+
+ info->Gesn.HackEventMask = FALSE;
+
+ } else if (thisEvent == 0) {
+
+ //
+ // HACKHACK - REF #0001
+ // note: this hack prevents poorly implemented firmware from constantly
+ // returning "No Event". we do this by cycling through the
+ // supported list of events here.
+ //
+
+ SET_FLAG(info->Gesn.NoChangeEventMask, thisEventBit);
+ CLEAR_FLAG(info->Gesn.EventMask, thisEventBit);
+
+ //
+ // if we have cycled through all supported event types, then
+ // we need to reset the events we are asking about. else we
+ // want to resend this request immediately in case there was
+ // another event pending.
+ //
+
+ if (info->Gesn.EventMask == 0) {
+ info->Gesn.EventMask = info->Gesn.NoChangeEventMask;
+ info->Gesn.NoChangeEventMask = 0;
+ } else {
+ *ResendImmediately = TRUE;
+ }
+ return;
+ }
+
+ } // end if (info->Gesn.HackEventMask)
+
+ dataLength =
+ (Header->EventDataLength[0] << 8) |
+ (Header->EventDataLength[1] & 0xff);
+ dataLength -= 2;
+ requiredLength = 4; // all events are four bytes
+
+ if (dataLength < requiredLength) {
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
+ "Classpnp => GESN returned only %x bytes data for fdo %p\n",
+ dataLength, FdoExtension->DeviceObject));
+ return;
+ }
+ if (dataLength != requiredLength) {
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
+ "Classpnp => GESN returned too many (%x) bytes data for fdo %p\n",
+ dataLength, FdoExtension->DeviceObject));
+ dataLength = 4;
+ }
+
+/*
+ ClasspSendNotification(FdoExtension,
+ &GUID_IO_GENERIC_GESN_EVENT,
+ sizeof(NOTIFICATION_EVENT_STATUS_HEADER) + dataLength,
+ Header)
+*/
+
+ switch (Header->NotificationClass) {
+
+ case NOTIFICATION_EXTERNAL_REQUEST_CLASS_EVENTS: { // 0x3
+
+ PNOTIFICATION_EXTERNAL_STATUS externalInfo =
+ (PNOTIFICATION_EXTERNAL_STATUS)(Header->ClassEventData);
+ DEVICE_EVENT_EXTERNAL_REQUEST externalData;
+
+ //
+ // unfortunately, due to time constraints, we will only notify
+ // about keys being pressed, and not released. this makes keys
+ // single-function, but simplifies the code significantly.
+ //
+
+ if (externalInfo->ExternalEvent !=
+ NOTIFICATION_EXTERNAL_EVENT_BUTTON_DOWN) {
+ break;
+ }
+
+ *ResendImmediately = TRUE;
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN::EXTERNAL: Event: %x Status %x Req %x\n",
+ externalInfo->ExternalEvent, externalInfo->ExternalStatus,
+ (externalInfo->Request[0] >> 8) | externalInfo->Request[1]
+ ));
+
+ RtlZeroMemory(&externalData, sizeof(DEVICE_EVENT_EXTERNAL_REQUEST));
+ externalData.Version = 1;
+ externalData.DeviceClass = 0;
+ externalData.ButtonStatus = externalInfo->ExternalEvent;
+ externalData.Request =
+ (externalInfo->Request[0] << 8) |
+ (externalInfo->Request[1] & 0xff);
+ KeQuerySystemTime(&(externalData.SystemTime));
+ externalData.SystemTime.QuadPart *= (LONGLONG)KeQueryTimeIncrement();
+
+ DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media DEVICE_EXTERNAL_REQUEST"));
+ ClasspSendNotification(FdoExtension,
+ &GUID_IO_DEVICE_EXTERNAL_REQUEST,
+ sizeof(DEVICE_EVENT_EXTERNAL_REQUEST),
+ &externalData);
+ return;
+ }
+
+ case NOTIFICATION_MEDIA_STATUS_CLASS_EVENTS: { // 0x4
+
+ PNOTIFICATION_MEDIA_STATUS mediaInfo =
+ (PNOTIFICATION_MEDIA_STATUS)(Header->ClassEventData);
+
+ if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NO_CHANGE) {
+ break;
+ }
+
+ *ResendImmediately = TRUE;
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN::MEDIA: Event: %x Status %x\n",
+ mediaInfo->MediaEvent, mediaInfo->MediaStatus));
+
+ if ((mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_NEW_MEDIA) ||
+ (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_CHANGE)) {
+
+
+ if (TEST_FLAG(FdoExtension->DeviceObject->Characteristics,
+ FILE_REMOVABLE_MEDIA) &&
+ (ClassGetVpb(FdoExtension->DeviceObject) != NULL) &&
+ (ClassGetVpb(FdoExtension->DeviceObject)->Flags & VPB_MOUNTED)
+ ) {
+
+ SET_FLAG(FdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+
+ }
+ InterlockedIncrement(&FdoExtension->MediaChangeCount);
+ ClasspSetMediaChangeStateEx(FdoExtension,
+ MediaPresent,
+ FALSE,
+ TRUE);
+
+ } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_MEDIA_REMOVAL) {
+
+ ClasspSetMediaChangeStateEx(FdoExtension,
+ MediaNotPresent,
+ FALSE,
+ TRUE);
+
+ } else if (mediaInfo->MediaEvent == NOTIFICATION_MEDIA_EVENT_EJECT_REQUEST) {
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugError,
+ "Classpnp => GESN Ejection request received!\n"));
+ ClassSendEjectionNotification(FdoExtension);
+
+ }
+ break;
+
+ }
+
+ case NOTIFICATION_DEVICE_BUSY_CLASS_EVENTS: { // lowest priority events...
+
+ PNOTIFICATION_BUSY_STATUS busyInfo =
+ (PNOTIFICATION_BUSY_STATUS)(Header->ClassEventData);
+ DEVICE_EVENT_BECOMING_READY busyData;
+
+ //
+ // NOTE: we never actually need to immediately retry for these
+ // events: if one exists, the device is busy, and if not,
+ // we still don't want to retry.
+ //
+
+ if (busyInfo->DeviceBusyStatus == NOTIFICATION_BUSY_STATUS_NO_EVENT) {
+ break;
+ }
+
+ //
+ // else we want to report the approximated time till it's ready.
+ //
+
+ RtlZeroMemory(&busyData, sizeof(DEVICE_EVENT_BECOMING_READY));
+ busyData.Version = 1;
+ busyData.Reason = busyInfo->DeviceBusyStatus;
+ busyData.Estimated100msToReady = (busyInfo->Time[0] << 8) |
+ (busyInfo->Time[1] & 0xff);
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN::BUSY: Event: %x Status %x Time %x\n",
+ busyInfo->DeviceBusyEvent, busyInfo->DeviceBusyStatus,
+ busyData.Estimated100msToReady
+ ));
+
+ DBGTRACE(ClassDebugTrace, ("ClasspInterpretGesnData: media BECOMING_READY"));
+ ClasspSendNotification(FdoExtension,
+ &GUID_IO_DEVICE_BECOMING_READY,
+ sizeof(DEVICE_EVENT_BECOMING_READY),
+ &busyData);
+ break;
+ }
+
+ default: {
+
+ break;
+
+ }
+
+ } // end switch on notification class
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspInternalSetMediaChangeState()
+
+Routine Description:
+
+ This routine will (if appropriate) set the media change event for the
+ device. The event will be set if the media state is changed and
+ media change events are enabled. Otherwise the media state will be
+ tracked but the event will not be set.
+
+ This routine will lock out the other media change routines if possible
+ but if not a media change notification may be lost after the enable has
+ been completed.
+
+Arguments:
+
+ FdoExtension - the device
+
+ MediaPresent - indicates whether the device has media inserted into it
+ (TRUE) or not (FALSE).
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClasspInternalSetMediaChangeState(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN MEDIA_CHANGE_DETECTION_STATE NewState,
+ IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
+ )
+{
+#if DBG
+ PUCHAR states[] = {"Unknown", "Present", "Not Present"};
+#endif
+ MEDIA_CHANGE_DETECTION_STATE oldMediaState;
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ ULONG data;
+ NTSTATUS status;
+
+ ASSERT((NewState >= MediaUnknown) && (NewState <= MediaNotPresent));
+
+ if(info == NULL) {
+ return;
+ }
+
+ oldMediaState = InterlockedExchange(
+ (PLONG)(&info->MediaChangeDetectionState),
+ (LONG)NewState);
+
+ if((oldMediaState == MediaUnknown) && (!KnownStateChange)) {
+
+ //
+ // The media was in an indeterminate state before - don't notify for
+ // this change.
+ //
+
+ DebugPrint((ClassDebugMCN,
+ "ClassSetMediaChangeState: State was unknown - this may "
+ "not be a change\n"));
+ return;
+
+ } else if(oldMediaState == NewState) {
+
+ //
+ // Media is in the same state it was before.
+ //
+
+ return;
+ }
+
+ if(info->MediaChangeDetectionDisableCount != 0) {
+
+ DBGTRACE(ClassDebugMCN,
+ ("ClassSetMediaChangeState: MCN not enabled, state "
+ "changed from %s to %s\n",
+ states[oldMediaState], states[NewState]));
+ return;
+
+ }
+
+ DBGTRACE(ClassDebugMCN,
+ ("ClassSetMediaChangeState: State change from %s to %s\n",
+ states[oldMediaState], states[NewState]));
+
+ //
+ // make the data useful -- it used to always be zero.
+ //
+ data = FdoExtension->MediaChangeCount;
+
+ if (NewState == MediaPresent) {
+
+ DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media ARRIVAL"));
+ ClasspSendNotification(FdoExtension,
+ &GUID_IO_MEDIA_ARRIVAL,
+ sizeof(ULONG),
+ &data);
+
+ }
+ else if (NewState == MediaNotPresent) {
+
+ DBGTRACE(ClassDebugTrace, ("ClasspInternalSetMediaChangeState: media REMOVAL"));
+ ClasspSendNotification(FdoExtension,
+ &GUID_IO_MEDIA_REMOVAL,
+ sizeof(ULONG),
+ &data);
+
+ } else {
+
+ //
+ // Don't notify of changed going to unknown.
+ //
+
+ return;
+ }
+
+ return;
+} // end ClasspInternalSetMediaChangeState()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSetMediaChangeState()
+
+Routine Description:
+
+ This routine will (if appropriate) set the media change event for the
+ device. The event will be set if the media state is changed and
+ media change events are enabled. Otherwise the media state will be
+ tracked but the event will not be set.
+
+ This routine will lock out the other media change routines if possible
+ but if not a media change notification may be lost after the enable has
+ been completed.
+
+Arguments:
+
+ FdoExtension - the device
+
+ MediaPresent - indicates whether the device has media inserted into it
+ (TRUE) or not (FALSE).
+
+ Wait - indicates whether the function should wait until it can acquire
+ the synchronization lock or not.
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClasspSetMediaChangeStateEx(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN MEDIA_CHANGE_DETECTION_STATE NewState,
+ IN BOOLEAN Wait,
+ IN BOOLEAN KnownStateChange // can ignore oldstate == unknown
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ LARGE_INTEGER zero;
+ NTSTATUS status;
+
+ DBGTRACE(ClassDebugMCN, ("> ClasspSetMediaChangeStateEx"));
+
+ //
+ // Reset SMART status on media removal as the old status may not be
+ // valid when there is no media in the device or when new media is
+ // inserted.
+ //
+
+ if (NewState == MediaNotPresent) {
+
+ FdoExtension->FailurePredicted = FALSE;
+ FdoExtension->FailureReason = 0;
+
+ }
+
+
+ zero.QuadPart = 0;
+
+ if(info == NULL) {
+ return;
+ }
+
+ status = KeWaitForMutexObject(&info->MediaChangeMutex,
+ Executive,
+ KernelMode,
+ FALSE,
+ ((Wait == TRUE) ? NULL : &zero));
+
+ if(status == STATUS_TIMEOUT) {
+
+ //
+ // Someone else is in the process of setting the media state
+ //
+
+ DBGWARN(("ClasspSetMediaChangeStateEx - timed out waiting for mutex"));
+ return;
+ }
+
+ //
+ // Change the media present state and signal an event, if applicable
+ //
+
+ ClasspInternalSetMediaChangeState(FdoExtension, NewState, KnownStateChange);
+
+ KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+ DBGTRACE(ClassDebugMCN, ("< ClasspSetMediaChangeStateEx"));
+
+ return;
+} // end ClassSetMediaChangeStateEx()
+VOID
+ClassSetMediaChangeState(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN MEDIA_CHANGE_DETECTION_STATE NewState,
+ IN BOOLEAN Wait
+ )
+{
+ ClasspSetMediaChangeStateEx(FdoExtension, NewState, Wait, FALSE);
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMediaChangeDetectionCompletion()
+
+Routine Description:
+
+ This routine handles 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
+
+--*/
+NTSTATUS
+ClasspMediaChangeDetectionCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PMEDIA_CHANGE_DETECTION_INFO info;
+ PIO_STACK_LOCATION nextIrpStack;
+ NTSTATUS status;
+ BOOLEAN retryImmediately = FALSE;
+
+ //
+ // Since the class driver created this request, it's completion routine
+ // will not get a valid device object handed in. Use the one in the
+ // irp stack instead
+ //
+
+ DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
+ fdoExtension = DeviceObject->DeviceExtension;
+ fdoData = fdoExtension->PrivateFdoData;
+ info = fdoExtension->MediaChangeDetectionInfo;
+
+ ASSERT(info->MediaChangeIrp != NULL);
+ ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
+ DBGTRACE(ClassDebugMCN, ("> ClasspMediaChangeDetectionCompletion: Device %p completed MCN irp %p.", DeviceObject, Irp));
+
+ /*
+ * HACK for IoMega 2GB Jaz drive:
+ * This drive spins down on its own to preserve the media.
+ * When spun down, TUR fails with 2/4/0 (SCSI_SENSE_NOT_READY/SCSI_ADSENSE_LUN_NOT_READY/?).
+ * ClassInterpretSenseInfo would then call ClassSendStartUnit to spin the media up, which defeats the
+ * purpose of the spindown.
+ * So in this case, make this into a successful TUR.
+ * This allows the drive to stay spun down until it is actually accessed again.
+ * (If the media were actually removed, TUR would fail with 2/3a/0 ).
+ * This hack only applies to drives with the CAUSE_NOT_REPORTABLE_HACK bit set; this
+ * is set by disk.sys when HackCauseNotReportableHack is set for the drive in its BadControllers list.
+ */
+ if ((SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) &&
+ TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK) &&
+ (Srb->SenseInfoBufferLength >= RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode))){
+
+ PSENSE_DATA senseData = Srb->SenseInfoBuffer;
+
+ if ((senseData->SenseKey == SCSI_SENSE_NOT_READY) &&
+ (senseData->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)){
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ }
+ }
+
+
+ //
+ // use ClassInterpretSenseInfo() to check for media state, and also
+ // to call ClassError() with correct parameters.
+ //
+ status = STATUS_SUCCESS;
+ if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - failed - srb status=%s, sense=%s/%s/%s.", DBGGETSRBSTATUSSTR(Srb), DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
+
+ ClassInterpretSenseInfo(DeviceObject,
+ Srb,
+ IRP_MJ_SCSI,
+ 0,
+ 0,
+ &status,
+ NULL);
+
+ }
+ else {
+
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+
+ if (!info->Gesn.Supported) {
+
+ DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded and GESN NOT supported, setting MediaPresent."));
+
+ //
+ // success != media for GESN case
+ //
+
+ ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
+
+ }
+ else {
+ DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - succeeded (GESN supported)."));
+ }
+ }
+
+ if (info->Gesn.Supported) {
+
+ if (status == STATUS_DATA_OVERRUN) {
+ DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - Overrun"));
+ status = STATUS_SUCCESS;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion: GESN failed with status %x", status));
+ } else {
+
+ //
+ // for GESN, need to interpret the results of the data.
+ // this may also require an immediate retry
+ //
+
+ if (Irp->IoStatus.Information == 8 ) {
+ ClasspInterpretGesnData(fdoExtension,
+ (PVOID)info->Gesn.Buffer,
+ &retryImmediately);
+ }
+
+ } // end of NT_SUCCESS(status)
+
+ } // end of Info->Gesn.Supported
+
+ //
+ // free port-allocated sense buffer, if any.
+ //
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
+ }
+
+ //
+ // Remember the IRP and SRB for use the next time.
+ //
+
+ ASSERT(IoGetNextIrpStackLocation(Irp));
+ IoGetNextIrpStackLocation(Irp)->Parameters.Scsi.Srb = Srb;
+
+ //
+ // Reset the MCN timer.
+ //
+
+ ClassResetMediaChangeTimer(fdoExtension);
+
+ //
+ // run a sanity check to make sure we're not recursing continuously
+ //
+
+ if (retryImmediately) {
+
+ info->MediaChangeRetryCount++;
+ if (info->MediaChangeRetryCount > MAXIMUM_IMMEDIATE_MCN_RETRIES) {
+ ASSERT(!"Recursing too often in MCN?");
+ info->MediaChangeRetryCount = 0;
+ retryImmediately = FALSE;
+ }
+
+ } else {
+
+ info->MediaChangeRetryCount = 0;
+
+ }
+
+
+ //
+ // release the remove lock....
+ //
+
+ {
+ UCHAR uniqueValue;
+ ClassAcquireRemoveLock(DeviceObject, (PIRP)(&uniqueValue));
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+
+ //
+ // set the irp as not in use
+ //
+ {
+ volatile LONG irpWasInUse;
+ irpWasInUse = InterlockedCompareExchange(&info->MediaChangeIrpInUse, 0, 1);
+ #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
+ ASSERT(irpWasInUse);
+ #endif
+ }
+
+ //
+ // now send it again before we release our last remove lock
+ //
+
+ if (retryImmediately) {
+ ClasspSendMediaStateIrp(fdoExtension, info, 0);
+ }
+ else {
+ DBGTRACE(ClassDebugMCN, ("ClasspMediaChangeDetectionCompletion - not retrying immediately"));
+ }
+
+ //
+ // release the temporary remove lock
+ //
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)(&uniqueValue));
+ }
+
+ DBGTRACE(ClassDebugMCN, ("< ClasspMediaChangeDetectionCompletion"));
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspSendTestUnitIrp() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+
+--*/
+PIRP
+ClasspPrepareMcnIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info,
+ IN BOOLEAN UseGesn
+)
+{
+ PSCSI_REQUEST_BLOCK srb;
+ PIO_STACK_LOCATION irpStack;
+ PIO_STACK_LOCATION nextIrpStack;
+ NTSTATUS status;
+ PCDB cdb;
+ PIRP irp;
+ PVOID buffer;
+
+ //
+ // Setup the IRP to perform a test unit ready.
+ //
+
+ irp = Info->MediaChangeIrp;
+
+ ASSERT(irp);
+
+ if (irp == NULL) {
+ return NULL;
+ }
+
+ //
+ // don't keep sending this if the device is being removed.
+ //
+
+ status = ClassAcquireRemoveLock(FdoExtension->DeviceObject, irp);
+ if (status == REMOVE_COMPLETE) {
+ ASSERT(status != REMOVE_COMPLETE);
+ return NULL;
+ }
+ else if (status == REMOVE_PENDING) {
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+ return NULL;
+ }
+ else {
+ ASSERT(status == NO_REMOVE);
+ }
+
+ irp->IoStatus.Status = STATUS_SUCCESS;
+ irp->IoStatus.Information = 0;
+ irp->Flags = 0;
+ irp->UserBuffer = NULL;
+
+ //
+ // If the irp is sent down when the volume needs to be
+ // verified, CdRomUpdateGeometryCompletion won't complete
+ // it since it's not associated with a thread. Marking
+ // it to override the verify causes it always be sent
+ // to the port driver
+ //
+
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+ irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
+
+ nextIrpStack = IoGetNextIrpStackLocation(irp);
+ nextIrpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ nextIrpStack->Parameters.Scsi.Srb = &(Info->MediaChangeSrb);
+
+ //
+ // Prepare the SRB for execution.
+ //
+
+ srb = nextIrpStack->Parameters.Scsi.Srb;
+ buffer = srb->SenseInfoBuffer;
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+ RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
+
+
+ srb->QueueTag = SP_UNTAGGED;
+ srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ srb->SenseInfoBuffer = buffer;
+ srb->SrbStatus = 0;
+ srb->ScsiStatus = 0;
+ srb->OriginalRequest = irp;
+ srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+ srb->SrbFlags = FdoExtension->SrbFlags;
+ SET_FLAG(srb->SrbFlags, Info->SrbFlags);
+
+ srb->TimeOutValue = FdoExtension->TimeOutValue * 2;
+
+ if (srb->TimeOutValue == 0) {
+
+ if (FdoExtension->TimeOutValue == 0) {
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
+ "ClassSendTestUnitIrp: FdoExtension->TimeOutValue "
+ "is set to zero?! -- resetting to 10\n"));
+ srb->TimeOutValue = 10 * 2; // reasonable default
+
+ } else {
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
+ "ClassSendTestUnitIrp: Someone set "
+ "srb->TimeOutValue to zero?! -- resetting to %x\n",
+ FdoExtension->TimeOutValue * 2));
+ srb->TimeOutValue = FdoExtension->TimeOutValue * 2;
+
+ }
+
+ }
+
+ if (!UseGesn) {
+
+ srb->CdbLength = 6;
+ srb->DataTransferLength = 0;
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
+ nextIrpStack->Parameters.DeviceIoControl.IoControlCode =
+ IOCTL_SCSI_EXECUTE_NONE;
+ srb->DataBuffer = NULL;
+ srb->DataTransferLength = 0;
+ irp->MdlAddress = NULL;
+
+ cdb = (PCDB) &srb->Cdb[0];
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+ } else {
+
+ ASSERT(Info->Gesn.Buffer);
+
+ srb->TimeOutValue = GESN_TIMEOUT_VALUE; // much shorter timeout for GESN
+
+ srb->CdbLength = 10;
+ SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_IN);
+ nextIrpStack->Parameters.DeviceIoControl.IoControlCode =
+ IOCTL_SCSI_EXECUTE_IN;
+ srb->DataBuffer = Info->Gesn.Buffer;
+ srb->DataTransferLength = Info->Gesn.BufferSize;
+ irp->MdlAddress = Info->Gesn.Mdl;
+
+ cdb = (PCDB) &srb->Cdb[0];
+ cdb->GET_EVENT_STATUS_NOTIFICATION.OperationCode =
+ SCSIOP_GET_EVENT_STATUS;
+ cdb->GET_EVENT_STATUS_NOTIFICATION.Immediate = 1;
+ cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[0] =
+ (UCHAR)((Info->Gesn.BufferSize) >> 8);
+ cdb->GET_EVENT_STATUS_NOTIFICATION.EventListLength[1] =
+ (UCHAR)((Info->Gesn.BufferSize) & 0xff);
+ cdb->GET_EVENT_STATUS_NOTIFICATION.NotificationClassRequest =
+ Info->Gesn.EventMask;
+
+ }
+
+ IoSetCompletionRoutine(irp,
+ ClasspMediaChangeDetectionCompletion,
+ srb,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ return irp;
+
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspSendMediaStateIrp() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+VOID
+ClasspSendMediaStateIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info,
+ IN ULONG CountDown
+ )
+{
+ BOOLEAN requestPending = FALSE;
+ LONG irpInUse;
+ LARGE_INTEGER zero;
+ NTSTATUS status;
+
+ DBGTRACE(ClassDebugMCN, ("> ClasspSendMediaStateIrp"));
+
+ if (((FdoExtension->CommonExtension.CurrentState != IRP_MN_START_DEVICE) ||
+ (FdoExtension->DevicePowerState != PowerDeviceD0)
+ ) &&
+ (!Info->MediaChangeIrpLost)) {
+
+ //
+ // the device may be stopped, powered down, or otherwise queueing io,
+ // so should not timeout the autorun irp (yet) -- set to zero ticks.
+ // scattered code relies upon this to not prematurely "lose" an
+ // autoplay irp that was queued.
+ //
+
+ Info->MediaChangeIrpTimeInUse = 0;
+ }
+
+ //
+ // if the irp is not in use, mark it as such.
+ //
+
+ irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 1, 0);
+
+ if (irpInUse) {
+
+ LONG timeInUse;
+
+ timeInUse = InterlockedIncrement(&Info->MediaChangeIrpTimeInUse);
+
+ DebugPrint((ClassDebugMCN, "ClasspSendMediaStateIrp: irp in use for "
+ "%x seconds when synchronizing for MCD\n", timeInUse));
+
+ if (Info->MediaChangeIrpLost == FALSE) {
+
+ if (timeInUse > MEDIA_CHANGE_TIMEOUT_TIME) {
+
+ //
+ // currently set to five minutes. hard to imagine a drive
+ // taking that long to spin up.
+ //
+
+ DebugPrint((ClassDebugError,
+ "CdRom%d: Media Change Notification has lost "
+ "it's irp and doesn't know where to find it. "
+ "Leave it alone and it'll come home dragging "
+ "it's stack behind it.\n",
+ FdoExtension->DeviceNumber));
+ Info->MediaChangeIrpLost = TRUE;
+ }
+ }
+
+ DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp - irpInUse"));
+ return;
+
+ }
+
+ TRY {
+
+ if (Info->MediaChangeDetectionDisableCount != 0) {
+ DebugPrint((ClassDebugTrace, "ClassCheckMediaState: device %p has "
+ " detection disabled \n", FdoExtension->DeviceObject));
+ LEAVE;
+ }
+
+ if (FdoExtension->DevicePowerState != PowerDeviceD0) {
+
+ if (TEST_FLAG(Info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE)) {
+ DebugPrint((ClassDebugMCN,
+ "ClassCheckMediaState: device %p is powered "
+ "down and flags are set to let it sleep\n",
+ FdoExtension->DeviceObject));
+ ClassResetMediaChangeTimer(FdoExtension);
+ LEAVE;
+ }
+
+ //
+ // NOTE: we don't increment the time in use until our power state
+ // changes above. this way, we won't "lose" the autoplay irp.
+ // it's up to the lower driver to determine if powering up is a
+ // good idea.
+ //
+
+ DebugPrint((ClassDebugMCN,
+ "ClassCheckMediaState: device %p needs to powerup "
+ "to handle this io (may take a few extra seconds).\n",
+ FdoExtension->DeviceObject));
+
+ }
+
+ Info->MediaChangeIrpTimeInUse = 0;
+ Info->MediaChangeIrpLost = FALSE;
+
+ if (CountDown == 0) {
+
+ PIRP irp;
+
+ DebugPrint((ClassDebugTrace,
+ "ClassCheckMediaState: timer expired\n"));
+
+ if (Info->MediaChangeDetectionDisableCount != 0) {
+ DebugPrint((ClassDebugTrace,
+ "ClassCheckMediaState: detection disabled\n"));
+ LEAVE;
+ }
+
+ //
+ // Prepare the IRP for the test unit ready
+ //
+
+ irp = ClasspPrepareMcnIrp(FdoExtension,
+ Info,
+ Info->Gesn.Supported);
+
+ //
+ // Issue the request.
+ //
+
+ DebugPrint((ClassDebugTrace,
+ "ClasspSendMediaStateIrp: Device %p getting TUR "
+ " irp %p\n", FdoExtension->DeviceObject, irp));
+
+ if (irp == NULL) {
+ LEAVE;
+ }
+
+
+ //
+ // note: if we send it to the class dispatch routines, there is
+ // a timing window here (since they grab the remove lock)
+ // where we'd be removed. ELIMINATE the window by grabbing
+ // the lock ourselves above and sending it to the lower
+ // device object directly or to the device's StartIo
+ // routine (which doesn't acquire the lock).
+ //
+
+ requestPending = TRUE;
+
+ DBGTRACE(ClassDebugMCN, (" ClasspSendMediaStateIrp - calling IoCallDriver."));
+ IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
+ }
+
+ } FINALLY {
+
+ if(requestPending == FALSE) {
+ irpInUse = InterlockedCompareExchange(&Info->MediaChangeIrpInUse, 0, 1);
+ #if _MSC_FULL_VER != 13009111 // This compiler always takes the wrong path here.
+ ASSERT(irpInUse);
+ #endif
+ }
+
+ }
+
+ DBGTRACE(ClassDebugMCN, ("< ClasspSendMediaStateIrp"));
+
+ return;
+} // end ClasspSendMediaStateIrp()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCheckMediaState()
+
+Routine Description:
+
+ This routine is called by the class driver to test for a media change
+ condition and/or poll for disk failure prediction. It should be called
+ from the class driver's IO timer routine once per second.
+
+Arguments:
+
+ FdoExtension - the device extension
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClassCheckMediaState(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ LONG countDown;
+
+ if(info == NULL) {
+ DebugPrint((ClassDebugTrace,
+ "ClassCheckMediaState: detection not enabled\n"));
+ return;
+ }
+
+ //
+ // Media change support is active and the IRP is waiting. Decrement the
+ // timer. There is no MP protection on the timer counter. This code
+ // is the only code that will manipulate the timer counter and only one
+ // instance of it should be running at any given time.
+ //
+
+ countDown = InterlockedDecrement(&(info->MediaChangeCountDown));
+
+ //
+ // Try to acquire the media change event. If we can't do it immediately
+ // then bail out and assume the caller will try again later.
+ //
+ ClasspSendMediaStateIrp(FdoExtension,
+ info,
+ countDown);
+
+ return;
+} // end ClassCheckMediaState()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassResetMediaChangeTimer()
+
+Routine Description:
+
+ Resets the media change count down timer to the default number of seconds.
+
+Arguments:
+
+ FdoExtension - the device to reset the timer for
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClassResetMediaChangeTimer(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+
+ if(info != NULL) {
+ InterlockedExchange(&(info->MediaChangeCountDown),
+ MEDIA_CHANGE_DEFAULT_TIME);
+ }
+ return;
+} // end ClassResetMediaChangeTimer()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspInitializePolling() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspInitializePolling(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN BOOLEAN AllowDriveToSleep
+ )
+{
+ PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+
+ ULONG size;
+ PMEDIA_CHANGE_DETECTION_INFO info;
+ PIRP irp;
+
+ PAGED_CODE();
+
+ if (FdoExtension->MediaChangeDetectionInfo != NULL) {
+ return STATUS_SUCCESS;
+ }
+
+ info = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(MEDIA_CHANGE_DETECTION_INFO),
+ CLASS_TAG_MEDIA_CHANGE_DETECTION);
+
+ if(info != NULL) {
+ RtlZeroMemory(info, sizeof(MEDIA_CHANGE_DETECTION_INFO));
+
+ FdoExtension->KernelModeMcnContext.FileObject = (PVOID)-1;
+ FdoExtension->KernelModeMcnContext.DeviceObject = (PVOID)-1;
+ FdoExtension->KernelModeMcnContext.LockCount = 0;
+ FdoExtension->KernelModeMcnContext.McnDisableCount = 0;
+
+ /*
+ * Allocate an IRP to carry the Test-Unit-Ready.
+ * Allocate an extra IRP stack location
+ * so we can cache our device object in the top location.
+ */
+ irp = IoAllocateIrp((CCHAR)(fdo->StackSize+1), FALSE);
+
+ if (irp != NULL) {
+
+ PVOID buffer;
+
+ buffer = ExAllocatePoolWithTag(
+ NonPagedPoolCacheAligned,
+ SENSE_BUFFER_SIZE,
+ CLASS_TAG_MEDIA_CHANGE_DETECTION);
+
+ if (buffer != NULL) {
+ PIO_STACK_LOCATION irpStack;
+ PSCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+
+ srb = &(info->MediaChangeSrb);
+ info->MediaChangeIrp = irp;
+ info->SenseBuffer = buffer;
+
+ /*
+ * For the driver that creates an IRP, there is no 'current' stack location.
+ * Step down one IRP stack location so that the extra top one
+ * becomes our 'current' one.
+ */
+ IoSetNextIrpStackLocation(irp);
+
+ /*
+ * Cache our device object in the extra top IRP stack location
+ * so we have it in our completion routine.
+ */
+ irpStack = IoGetCurrentIrpStackLocation(irp);
+ irpStack->DeviceObject = fdo;
+
+ /*
+ * Now start setting up the next IRP stack location for the call like any driver would.
+ */
+ irpStack = IoGetNextIrpStackLocation(irp);
+ irpStack->Parameters.Scsi.Srb = srb;
+ info->MediaChangeIrp = irp;
+
+ //
+ // Initialize the SRB
+ //
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ //
+ // Initialize and set up the sense information buffer
+ //
+
+ RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
+ srb->SenseInfoBuffer = buffer;
+ srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+ //
+ // Set default values for the media change notification
+ // configuration.
+ //
+
+ info->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
+ info->MediaChangeDetectionDisableCount = 0;
+
+ //
+ // Assume that there is initially no media in the device
+ // only notify upper layers if there is something there
+ //
+
+ info->MediaChangeDetectionState = MediaUnknown;
+
+ info->MediaChangeIrpTimeInUse = 0;
+ info->MediaChangeIrpLost = FALSE;
+
+ //
+ // setup all extra flags we'll be setting for this irp
+ //
+ info->SrbFlags = 0;
+ if (AllowDriveToSleep) {
+ SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_KEEP_AWAKE);
+ }
+ SET_FLAG(info->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
+ SET_FLAG(info->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ SET_FLAG(info->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ KeInitializeMutex(&info->MediaChangeMutex, 0x100);
+
+ //
+ // It is ok to support media change events on this
+ // device.
+ //
+
+ FdoExtension->MediaChangeDetectionInfo = info;
+
+ //
+ // NOTE: the DeviceType is FILE_DEVICE_CD_ROM even
+ // when the device supports DVD (no need to
+ // check for FILE_DEVICE_DVD, as it's not a
+ // valid check).
+ //
+
+ if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM){
+
+ NTSTATUS status;
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "ClasspInitializePolling: Testing for GESN\n"));
+ status = ClasspInitializeGesn(FdoExtension, info);
+ if (NT_SUCCESS(status)) {
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "ClasspInitializePolling: GESN available "
+ "for %p\n", FdoExtension->DeviceObject));
+ ASSERT(info->Gesn.Supported );
+ ASSERT(info->Gesn.Buffer != NULL);
+ ASSERT(info->Gesn.BufferSize != 0);
+ ASSERT(info->Gesn.EventMask != 0);
+ // must return here, for ASSERTs to be valid.
+ return STATUS_SUCCESS;
+ }
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "ClasspInitializePolling: GESN *NOT* available "
+ "for %p\n", FdoExtension->DeviceObject));
+ }
+
+ ASSERT(info->Gesn.Supported == 0);
+ ASSERT(info->Gesn.Buffer == NULL);
+ ASSERT(info->Gesn.BufferSize == 0);
+ ASSERT(info->Gesn.EventMask == 0);
+ info->Gesn.Supported = 0; // just in case....
+ return STATUS_SUCCESS;
+ }
+
+ IoFreeIrp(irp);
+ }
+
+ ExFreePool(info);
+ }
+
+ //
+ // nothing to free here
+ //
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+} // end ClasspInitializePolling()
+
+NTSTATUS
+ClasspInitializeGesn(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info
+ )
+{
+ PNOTIFICATION_EVENT_STATUS_HEADER header;
+ CLASS_DETECTION_STATE detectionState = ClassDetectionUnknown;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDescriptor;
+ NTSTATUS status = STATUS_NOT_SUPPORTED;
+ PIRP irp;
+ KEVENT event;
+ BOOLEAN retryImmediately;
+ ULONG i;
+ ULONG atapiResets;
+
+
+ PAGED_CODE();
+ ASSERT(Info == FdoExtension->MediaChangeDetectionInfo);
+
+ //
+ // read if we already know the abilities of the device
+ //
+
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ (PULONG)&detectionState);
+
+ if (detectionState == ClassDetectionUnsupported) {
+ goto ExitWithError;
+ }
+
+ //
+ // check if the device has a hack flag saying never to try this.
+ //
+
+ if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_GESN_IS_BAD)) {
+
+ detectionState = ClassDetectionUnsupported;
+ ClassSetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionSupported);
+ goto ExitWithError;
+
+ }
+
+
+ //
+ // else go through the process since we allocate buffers and
+ // get all sorts of device settings.
+ //
+
+ if (Info->Gesn.Buffer == NULL) {
+ Info->Gesn.Buffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ GESN_BUFFER_SIZE,
+ '??cS');
+ }
+ if (Info->Gesn.Buffer == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithError;
+ }
+ if (Info->Gesn.Mdl != NULL) {
+ IoFreeMdl(Info->Gesn.Mdl);
+ }
+ Info->Gesn.Mdl = IoAllocateMdl(Info->Gesn.Buffer,
+ GESN_BUFFER_SIZE,
+ FALSE, FALSE, NULL);
+ if (Info->Gesn.Mdl == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithError;
+ }
+
+ MmBuildMdlForNonPagedPool(Info->Gesn.Mdl);
+ Info->Gesn.BufferSize = GESN_BUFFER_SIZE;
+ Info->Gesn.EventMask = 0;
+
+ //
+ // all items are prepared to use GESN (except the event mask, so don't
+ // optimize this part out!).
+ //
+ // now see if it really works. we have to loop through this because
+ // many SAMSUNG (and one COMPAQ) drives timeout when requesting
+ // NOT_READY events, even when the IMMEDIATE bit is set. :(
+ //
+ // using a drive list is cumbersome, so this might fix the problem.
+ //
+
+ adapterDescriptor = FdoExtension->AdapterDescriptor;
+ atapiResets = 0;
+ retryImmediately = TRUE;
+ for (i = 0; i < 16 && retryImmediately == TRUE; i++) {
+
+ irp = ClasspPrepareMcnIrp(FdoExtension, Info, TRUE);
+ if (irp == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithError;
+ }
+
+ ASSERT(TEST_FLAG(Info->MediaChangeSrb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE));
+
+ //
+ // replace the completion routine with a different one this time...
+ //
+
+ IoSetCompletionRoutine(irp,
+ ClassSignalCompletion,
+ &event,
+ TRUE, TRUE, TRUE);
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ status = IoCallDriver(FdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ if (status == STATUS_PENDING) {
+ status = KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ ASSERT(NT_SUCCESS(status));
+ }
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, irp);
+
+ if (SRB_STATUS(Info->MediaChangeSrb.SrbStatus) != SRB_STATUS_SUCCESS) {
+ ClassInterpretSenseInfo(FdoExtension->DeviceObject,
+ &(Info->MediaChangeSrb),
+ IRP_MJ_SCSI,
+ 0,
+ 0,
+ &status,
+ NULL);
+ }
+
+ if ((adapterDescriptor->BusType == BusTypeAtapi) &&
+ (Info->MediaChangeSrb.SrbStatus == SRB_STATUS_BUS_RESET)
+ ) {
+
+ //
+ // ATAPI unfortunately returns SRB_STATUS_BUS_RESET instead
+ // of SRB_STATUS_TIMEOUT, so we cannot differentiate between
+ // the two. if we get this status four time consecutively,
+ // stop trying this command. it is too late to change ATAPI
+ // at this point, so special-case this here. (07/10/2001)
+ // NOTE: any value more than 4 may cause the device to be
+ // marked missing.
+ //
+
+ atapiResets++;
+ if (atapiResets >= 4) {
+ status = STATUS_IO_DEVICE_ERROR;
+ goto ExitWithError;
+ }
+ }
+
+ if (status == STATUS_DATA_OVERRUN) {
+ status = STATUS_SUCCESS;
+ }
+
+ if ((status == STATUS_INVALID_DEVICE_REQUEST) ||
+ (status == STATUS_TIMEOUT) ||
+ (status == STATUS_IO_DEVICE_ERROR) ||
+ (status == STATUS_IO_TIMEOUT)
+ ) {
+
+ //
+ // with these error codes, we don't ever want to try this command
+ // again on this device, since it reacts poorly.
+ //
+
+ ClassSetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionUnsupported);
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
+ "Classpnp => GESN test failed %x for fdo %p\n",
+ status, FdoExtension->DeviceObject));
+ goto ExitWithError;
+
+
+ }
+
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // this may be other errors that should not disable GESN
+ // for all future start_device calls.
+ //
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugWarning,
+ "Classpnp => GESN test failed %x for fdo %p\n",
+ status, FdoExtension->DeviceObject));
+ goto ExitWithError;
+ }
+
+ if (i == 0) {
+
+ //
+ // the first time, the request was just retrieving a mask of
+ // available bits. use this to mask future requests.
+ //
+
+ header = (PNOTIFICATION_EVENT_STATUS_HEADER)(Info->Gesn.Buffer);
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => Fdo %p supports event mask %x\n",
+ FdoExtension->DeviceObject, header->SupportedEventClasses));
+
+
+ if (TEST_FLAG(header->SupportedEventClasses,
+ NOTIFICATION_MEDIA_STATUS_CLASS_MASK)) {
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN supports MCN\n"));
+ }
+ if (TEST_FLAG(header->SupportedEventClasses,
+ NOTIFICATION_DEVICE_BUSY_CLASS_MASK)) {
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN supports DeviceBusy\n"));
+ }
+ Info->Gesn.EventMask = header->SupportedEventClasses;
+
+ //
+ // realistically, we are only considering the following events:
+ // EXTERNAL REQUEST - this is being tested for play/stop/etc.
+ // MEDIA STATUS - autorun and ejection requests.
+ // DEVICE BUSY - to allow us to predict when media will be ready.
+ // therefore, we should not bother querying for the other,
+ // unknown events. clear all but the above flags.
+ //
+
+ Info->Gesn.EventMask &=
+ NOTIFICATION_EXTERNAL_REQUEST_CLASS_MASK |
+ NOTIFICATION_MEDIA_STATUS_CLASS_MASK |
+ NOTIFICATION_DEVICE_BUSY_CLASS_MASK ;
+
+
+ //
+ // HACKHACK - REF #0001
+ // Some devices will *never* report an event if we've also requested
+ // that it report lower-priority events. this is due to a
+ // misunderstanding in the specification wherein a "No Change" is
+ // interpreted to be a real event. what should occur is that the
+ // device should ignore "No Change" events when multiple event types
+ // are requested unless there are no other events waiting. this
+ // greatly reduces the number of requests that the host must send
+ // to determine if an event has occurred. Since we must work on all
+ // drives, default to enabling the hack until we find evidence of
+ // proper firmware.
+ //
+
+ if (CountOfSetBitsUChar(Info->Gesn.EventMask) == 1) {
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN hack %s for FDO %p\n",
+ "not required", FdoExtension->DeviceObject));
+ } else {
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN hack %s for FDO %p\n",
+ "enabled", FdoExtension->DeviceObject));
+ Info->Gesn.HackEventMask = 1;
+ }
+
+ } else {
+
+ //
+ // not the first time looping through, so interpret the results.
+ //
+
+ ClasspInterpretGesnData(FdoExtension,
+ (PVOID)Info->Gesn.Buffer,
+ &retryImmediately);
+
+ }
+
+ } // end loop of GESN requests....
+
+ //
+ // we can only use this if it can be relied upon for media changes,
+ // since we are (by definition) no longer going to be polling via
+ // a TEST_UNIT_READY irp, and drives will not report UNIT ATTENTION
+ // for this command (although a filter driver, such as one for burning
+ // cd's, might still fake those errors).
+ //
+ // since we also rely upon NOT_READY events to change the cursor
+ // into a "wait" cursor, we can't use GESN without NOT_READY support.
+ //
+
+ if (TEST_FLAG(Info->Gesn.EventMask,
+ NOTIFICATION_MEDIA_STATUS_CLASS_MASK) &&
+ TEST_FLAG(Info->Gesn.EventMask,
+ NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
+ ) {
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => Enabling GESN support for fdo %p\n",
+ FdoExtension->DeviceObject));
+ Info->Gesn.Supported = TRUE;
+
+ ClassSetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_MMC_DETECTION_VALUE_NAME,
+ ClassDetectionSupported);
+
+ return STATUS_SUCCESS;
+
+ }
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, ClassDebugMCN,
+ "Classpnp => GESN available but not enabled for fdo %p\n",
+ FdoExtension->DeviceObject));
+ goto ExitWithError;
+
+ // fall through...
+
+ExitWithError:
+ if (Info->Gesn.Mdl) {
+ IoFreeMdl(Info->Gesn.Mdl);
+ Info->Gesn.Mdl = NULL;
+ }
+ if (Info->Gesn.Buffer) {
+ ExFreePool(Info->Gesn.Buffer);
+ Info->Gesn.Buffer = NULL;
+ }
+ Info->Gesn.Supported = 0;
+ Info->Gesn.EventMask = 0;
+ Info->Gesn.BufferSize = 0;
+ return STATUS_NOT_SUPPORTED;
+
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeTestUnitPolling()
+
+Routine Description:
+
+ This routine will initialize MCN regardless of the settings stored
+ in the registry. This should be used with caution, as some devices
+ react badly to constant io. (i.e. never spin down, continuously cycling
+ media in changers, ejection of media, etc.) It is highly suggested to
+ use ClassInitializeMediaChangeDetection() instead.
+
+Arguments:
+
+ FdoExtension is the device to poll
+
+ AllowDriveToSleep says whether to attempt to allow the drive to sleep
+ or not. This only affects system-known spin down states, so if a
+ drive spins itself down, this has no effect until the system spins
+ it down.
+
+Return Value:
+
+--*/
+NTSTATUS
+ClassInitializeTestUnitPolling(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN BOOLEAN AllowDriveToSleep
+ )
+{
+ return ClasspInitializePolling(FdoExtension, AllowDriveToSleep);
+} // end ClassInitializeTestUnitPolling()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeMediaChangeDetection()
+
+Routine Description:
+
+ This routine checks to see if it is safe to initialize MCN (the back end
+ to autorun) for a given device. It will then check the device-type wide
+ key "Autorun" in the service key (for legacy reasons), and then look in
+ the device-specific key to potentially override that setting.
+
+ If MCN is to be enabled, all neccessary structures and memory are
+ allocated and initialized.
+
+ This routine MUST be called only from the ClassInit() callback.
+
+Arguments:
+
+ FdoExtension - the device to initialize MCN for, if appropriate
+
+ EventPrefix - unused, legacy argument. Set to zero.
+
+Return Value:
+
+--*/
+VOID
+ClassInitializeMediaChangeDetection(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PUCHAR EventPrefix
+ )
+{
+ PDEVICE_OBJECT fdo = FdoExtension->DeviceObject;
+ NTSTATUS status;
+
+ PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(
+ fdo->DriverObject);
+
+ BOOLEAN disabledForBadHardware;
+ BOOLEAN disabled;
+ BOOLEAN instanceOverride;
+
+ PAGED_CODE();
+
+ //
+ // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
+ // called in the context of the ClassInitDevice callback. If called
+ // after then this check will have already been made and the
+ // once a second timer will not have been enabled.
+ //
+
+ disabledForBadHardware = ClasspIsMediaChangeDisabledDueToHardwareLimitation(
+ FdoExtension,
+ &(driverExtension->RegistryPath)
+ );
+
+ if (disabledForBadHardware) {
+ DebugPrint((ClassDebugMCN,
+ "ClassInitializeMCN: Disabled due to hardware"
+ "limitations for this device"));
+ return;
+ }
+
+ //
+ // autorun should now be enabled by default for all media types.
+ //
+
+ disabled = ClasspIsMediaChangeDisabledForClass(
+ FdoExtension,
+ &(driverExtension->RegistryPath)
+ );
+
+ DebugPrint((ClassDebugMCN,
+ "ClassInitializeMCN: Class MCN is %s\n",
+ (disabled ? "disabled" : "enabled")));
+
+ status = ClasspMediaChangeDeviceInstanceOverride(
+ FdoExtension,
+ &instanceOverride); // default value
+
+ if (!NT_SUCCESS(status)) {
+ DebugPrint((ClassDebugMCN,
+ "ClassInitializeMCN: Instance using default\n"));
+ } else {
+ DebugPrint((ClassDebugMCN,
+ "ClassInitializeMCN: Instance override: %s MCN\n",
+ (instanceOverride ? "Enabling" : "Disabling")));
+ disabled = !instanceOverride;
+ }
+
+ DebugPrint((ClassDebugMCN,
+ "ClassInitializeMCN: Instance MCN is %s\n",
+ (disabled ? "disabled" : "enabled")));
+
+ if (disabled) {
+ return;
+ }
+
+ //
+ // if the drive is not a CDROM, allow the drive to sleep
+ //
+ if (FdoExtension->DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) {
+ ClasspInitializePolling(FdoExtension, FALSE);
+ } else {
+ ClasspInitializePolling(FdoExtension, TRUE);
+ }
+
+ return;
+} // end ClassInitializeMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMediaChangeDeviceInstanceOverride()
+
+Routine Description:
+
+ The user can override the global setting to enable or disable Autorun on a
+ specific cdrom device via the control panel. This routine checks and/or
+ sets this value.
+
+Arguments:
+
+ FdoExtension - the device to set/get the value for
+ Value - the value to use in a set
+ SetValue - whether to set the value
+
+Return Value:
+
+ TRUE - Autorun is disabled
+ FALSE - Autorun is not disabled (Default)
+
+--*/
+NTSTATUS
+ClasspMediaChangeDeviceInstanceOverride(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ OUT PBOOLEAN Enabled
+ )
+{
+ HANDLE deviceParameterHandle; // cdrom instance key
+ HANDLE driverParameterHandle; // cdrom specific key
+ RTL_QUERY_REGISTRY_TABLE queryTable[3];
+ OBJECT_ATTRIBUTES objectAttributes;
+ UNICODE_STRING subkeyName;
+ NTSTATUS status;
+ ULONG alwaysEnable;
+ ULONG alwaysDisable;
+ ULONG i;
+
+
+ PAGED_CODE();
+
+ deviceParameterHandle = NULL;
+ driverParameterHandle = NULL;
+ status = STATUS_UNSUCCESSFUL;
+ alwaysEnable = FALSE;
+ alwaysDisable = FALSE;
+
+ TRY {
+
+ status = IoOpenDeviceRegistryKey( FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_ALL_ACCESS,
+ &deviceParameterHandle
+ );
+ if (!NT_SUCCESS(status)) {
+
+ //
+ // this can occur when a new device is added to the system
+ // this is due to cdrom.sys being an 'essential' driver
+ //
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDeviceInstanceDisabled: "
+ "Could not open device registry key [%lx]\n", status));
+ LEAVE;
+ }
+
+ RtlInitUnicodeString(&subkeyName, MCN_REG_SUBKEY_NAME);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ (PSECURITY_DESCRIPTOR) NULL);
+
+ status = ZwCreateKey(&driverParameterHandle,
+ KEY_READ,
+ &objectAttributes,
+ 0,
+ (PUNICODE_STRING) NULL,
+ REG_OPTION_NON_VOLATILE,
+ NULL);
+
+ if (!NT_SUCCESS(status)) {
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDeviceInstanceDisabled: "
+ "subkey could not be created. %lx\n", status));
+ LEAVE;
+ }
+
+ //
+ // Default to not changing autorun behavior, based upon setting
+ // registryValue to zero.
+ //
+
+ for (i=0;i<2;i++) {
+
+ RtlZeroMemory(&queryTable[0], sizeof(queryTable));
+
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ queryTable[0].DefaultType = REG_DWORD;
+ queryTable[0].DefaultLength = 0;
+
+ if (i==0) {
+ queryTable[0].Name = MCN_REG_AUTORUN_DISABLE_INSTANCE_NAME;
+ queryTable[0].EntryContext = &alwaysDisable;
+ queryTable[0].DefaultData = &alwaysDisable;
+ } else {
+ queryTable[0].Name = MCN_REG_AUTORUN_ENABLE_INSTANCE_NAME;
+ queryTable[0].EntryContext = &alwaysEnable;
+ queryTable[0].DefaultData = &alwaysEnable;
+ }
+
+ //
+ // don't care if it succeeds, since we set defaults above
+ //
+
+ RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PWSTR)driverParameterHandle,
+ queryTable,
+ NULL,
+ NULL);
+ }
+
+ } FINALLY {
+
+ if (driverParameterHandle) ZwClose(driverParameterHandle);
+ if (deviceParameterHandle) ZwClose(deviceParameterHandle);
+
+ }
+
+ if (alwaysEnable && alwaysDisable) {
+
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
+ "Both Enable and Disable set -- DISABLE"));
+ ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+ *Enabled = FALSE;
+
+ } else if (alwaysDisable) {
+
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
+ "DISABLE"));
+ ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+ *Enabled = FALSE;
+
+ } else if (alwaysEnable) {
+
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
+ "ENABLE"));
+ ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+ *Enabled = TRUE;
+
+ } else {
+
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDeviceInstanceDisabled: %s selected\n",
+ "DEFAULT"));
+ status = STATUS_UNSUCCESSFUL;
+
+ }
+
+ return status;
+
+} // end ClasspMediaChangeDeviceInstanceOverride()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspIsMediaChangeDisabledDueToHardwareLimitation()
+
+Routine Description:
+
+ The key AutoRunAlwaysDisable contains a MULTI_SZ of hardware IDs for
+ which to never enable MediaChangeNotification.
+
+ The user can override the global setting to enable or disable Autorun on a
+ specific cdrom device via the control panel.
+
+Arguments:
+
+ FdoExtension -
+ RegistryPath - pointer to the unicode string inside
+ ...\CurrentControlSet\Services\Cdrom
+
+Return Value:
+
+ TRUE - no autorun.
+ FALSE - Autorun may be enabled
+
+--*/
+BOOLEAN
+ClasspIsMediaChangeDisabledDueToHardwareLimitation(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
+ OBJECT_ATTRIBUTES objectAttributes;
+ HANDLE serviceKey = NULL;
+ RTL_QUERY_REGISTRY_TABLE parameters[2];
+
+ UNICODE_STRING deviceUnicodeString;
+ ANSI_STRING deviceString;
+ ULONG mediaChangeNotificationDisabled = FALSE;
+
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ //
+ // open the service key.
+ //
+
+ InitializeObjectAttributes(&objectAttributes,
+ RegistryPath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&serviceKey,
+ KEY_READ,
+ &objectAttributes);
+
+ ASSERT(NT_SUCCESS(status));
+
+
+ if(!NT_SUCCESS(status)) {
+
+ //
+ // always take the safe path. if we can't open the service key,
+ // disable autorun
+ //
+
+ return TRUE;
+
+ }
+
+ TRY {
+ //
+ // Determine if drive is in a list of those requiring
+ // autorun to be disabled. this is stored in a REG_MULTI_SZ
+ // named AutoRunAlwaysDisable. this is required as some autochangers
+ // must load the disc to reply to ChkVerify request, causing them
+ // to cycle discs continuously.
+ //
+
+ PWSTR nullMultiSz;
+ PUCHAR vendorId;
+ PUCHAR productId;
+ PUCHAR revisionId;
+ ULONG length;
+ ULONG offset;
+
+ deviceString.Buffer = NULL;
+ deviceUnicodeString.Buffer = NULL;
+
+ //
+ // there may be nothing to check against
+ //
+
+ if ((deviceDescriptor->VendorIdOffset == 0) &&
+ (deviceDescriptor->ProductIdOffset == 0)) {
+ LEAVE;
+ }
+
+ length = 0;
+
+ if (deviceDescriptor->VendorIdOffset == 0) {
+ vendorId = NULL;
+ } else {
+ vendorId = (PUCHAR) deviceDescriptor + deviceDescriptor->VendorIdOffset;
+ length = strlen(vendorId);
+ }
+
+ if ( deviceDescriptor->ProductIdOffset == 0 ) {
+ productId = NULL;
+ } else {
+ productId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductIdOffset;
+ length += strlen(productId);
+ }
+
+ if ( deviceDescriptor->ProductRevisionOffset == 0 ) {
+ revisionId = NULL;
+ } else {
+ revisionId = (PUCHAR) deviceDescriptor + deviceDescriptor->ProductRevisionOffset;
+ length += strlen(revisionId);
+ }
+
+ //
+ // allocate a buffer for the string
+ //
+
+ deviceString.Length = (USHORT)( length );
+ deviceString.MaximumLength = deviceString.Length + 1;
+ deviceString.Buffer = (PUCHAR)ExAllocatePoolWithTag( NonPagedPool,
+ deviceString.MaximumLength,
+ CLASS_TAG_AUTORUN_DISABLE
+ );
+ if (deviceString.Buffer == NULL) {
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDisabledForHardware: Unable to alloc "
+ "string buffer\n" ));
+ LEAVE;
+ }
+
+ //
+ // copy strings to the buffer
+ //
+ offset = 0;
+
+ if (vendorId != NULL) {
+ RtlCopyMemory(deviceString.Buffer + offset,
+ vendorId,
+ strlen(vendorId));
+ offset += strlen(vendorId);
+ }
+
+ if ( productId != NULL ) {
+ RtlCopyMemory(deviceString.Buffer + offset,
+ productId,
+ strlen(productId));
+ offset += strlen(productId);
+ }
+ if ( revisionId != NULL ) {
+ RtlCopyMemory(deviceString.Buffer + offset,
+ revisionId,
+ strlen(revisionId));
+ offset += strlen(revisionId);
+ }
+
+ ASSERT(offset == deviceString.Length);
+
+ deviceString.Buffer[deviceString.Length] = '\0'; // Null-terminated
+
+ //
+ // convert to unicode as registry deals with unicode strings
+ //
+
+ status = RtlAnsiStringToUnicodeString( &deviceUnicodeString,
+ &deviceString,
+ TRUE
+ );
+ if (!NT_SUCCESS(status)) {
+ DebugPrint((ClassDebugMCN,
+ "ClassMediaChangeDisabledForHardware: cannot convert "
+ "to unicode %lx\n", status));
+ LEAVE;
+ }
+
+ //
+ // query the value, setting valueFound to true if found
+ //
+
+ RtlZeroMemory(parameters, sizeof(parameters));
+
+ nullMultiSz = L"\0";
+ parameters[0].QueryRoutine = ClasspMediaChangeRegistryCallBack;
+ parameters[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
+ parameters[0].Name = L"AutoRunAlwaysDisable";
+ parameters[0].EntryContext = &mediaChangeNotificationDisabled;
+ parameters[0].DefaultType = REG_MULTI_SZ;
+ parameters[0].DefaultData = nullMultiSz;
+ parameters[0].DefaultLength = 0;
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ serviceKey,
+ parameters,
+ &deviceUnicodeString,
+ NULL);
+
+ if ( !NT_SUCCESS(status) ) {
+ LEAVE;
+ }
+
+ } FINALLY {
+
+ if (deviceString.Buffer != NULL) {
+ ExFreePool( deviceString.Buffer );
+ }
+ if (deviceUnicodeString.Buffer != NULL) {
+ RtlFreeUnicodeString( &deviceUnicodeString );
+ }
+
+ ZwClose(serviceKey);
+ }
+
+ if (mediaChangeNotificationDisabled) {
+ DebugPrint((ClassDebugMCN, "ClassMediaChangeDisabledForHardware: "
+ "Device is on disable list\n"));
+ return TRUE;
+ }
+ return FALSE;
+
+} // end ClasspIsMediaChangeDisabledDueToHardwareLimitation()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspIsMediaChangeDisabledForClass()
+
+Routine Description:
+
+ The user must specify that AutoPlay is to run on the platform
+ by setting the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\
+ Services\\Autorun:REG_DWORD:1.
+
+ The user can override the global setting to enable or disable Autorun on a
+ specific cdrom device via the control panel.
+
+Arguments:
+
+ FdoExtension -
+ RegistryPath - pointer to the unicode string inside
+ ...\CurrentControlSet\Services\Cdrom
+
+Return Value:
+
+ TRUE - Autorun is disabled for this class
+ FALSE - Autorun is enabled for this class
+
+--*/
+BOOLEAN
+ClasspIsMediaChangeDisabledForClass(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor = FdoExtension->DeviceDescriptor;
+
+ OBJECT_ATTRIBUTES objectAttributes;
+ HANDLE serviceKey = NULL;
+ HANDLE parametersKey = NULL;
+ RTL_QUERY_REGISTRY_TABLE parameters[3];
+
+ UNICODE_STRING paramStr;
+ UNICODE_STRING deviceUnicodeString;
+ ANSI_STRING deviceString;
+
+ //
+ // Default to ENABLING MediaChangeNotification (!)
+ //
+
+ ULONG mcnRegistryValue = 1;
+
+ NTSTATUS status;
+
+
+ PAGED_CODE();
+
+ //
+ // open the service key.
+ //
+
+ InitializeObjectAttributes(&objectAttributes,
+ RegistryPath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&serviceKey,
+ KEY_READ,
+ &objectAttributes);
+
+ ASSERT(NT_SUCCESS(status));
+
+ if(!NT_SUCCESS(status)) {
+
+ //
+ // return the default value, which is the
+ // inverse of the registry setting default
+ // since this routine asks if it's disabled
+ //
+
+ DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: Defaulting to %s\n",
+ (mcnRegistryValue ? "Enabled" : "Disabled")));
+ return (BOOLEAN)(!mcnRegistryValue);
+
+ }
+
+ RtlZeroMemory(parameters, sizeof(parameters));
+
+ //
+ // Open the parameters key (if any) beneath the services key.
+ //
+
+ RtlInitUnicodeString(¶mStr, L"Parameters");
+
+ InitializeObjectAttributes(&objectAttributes,
+ ¶mStr,
+ OBJ_CASE_INSENSITIVE,
+ serviceKey,
+ NULL);
+
+ status = ZwOpenKey(¶metersKey,
+ KEY_READ,
+ &objectAttributes);
+
+ if (!NT_SUCCESS(status)) {
+ parametersKey = NULL;
+ }
+
+
+
+ //
+ // Check for the Autorun value.
+ //
+
+ parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ parameters[0].Name = L"Autorun";
+ parameters[0].EntryContext = &mcnRegistryValue;
+ parameters[0].DefaultType = REG_DWORD;
+ parameters[0].DefaultData = &mcnRegistryValue;
+ parameters[0].DefaultLength = sizeof(ULONG);
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
+ serviceKey,
+ parameters,
+ NULL,
+ NULL);
+
+ DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: "
+ "/Autorun flag = %d\n", mcnRegistryValue));
+
+ if(parametersKey != NULL) {
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
+ parametersKey,
+ parameters,
+ NULL,
+ NULL);
+ DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: "
+ "/Parameters/Autorun flag = %d\n",
+ mcnRegistryValue));
+ ZwClose(parametersKey);
+
+ }
+ ZwClose(serviceKey);
+
+ DebugPrint((ClassDebugMCN, "ClassCheckServiceMCN: "
+ "Autoplay for device %p is %s\n",
+ FdoExtension->DeviceObject,
+ (mcnRegistryValue ? "on" : "off")
+ ));
+
+ //
+ // return if it is _disabled_, which is the
+ // inverse of the registry setting
+ //
+
+ return (BOOLEAN)(!mcnRegistryValue);
+} // end ClasspIsMediaChangeDisabledForClass()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
+ClassEnableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+VOID
+ClassEnableMediaChangeDetection(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+ LONG oldCount;
+
+ PAGED_CODE();
+
+ if(info == NULL) {
+ DebugPrint((ClassDebugMCN,
+ "ClassEnableMediaChangeDetection: not initialized\n"));
+ return;
+ }
+
+ KeWaitForMutexObject(&info->MediaChangeMutex,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ oldCount = --info->MediaChangeDetectionDisableCount;
+
+ ASSERT(oldCount >= 0);
+
+ DebugPrint((ClassDebugMCN, "ClassEnableMediaChangeDetection: Disable count "
+ "reduced to %d - ",
+ info->MediaChangeDetectionDisableCount));
+
+ if(oldCount == 0) {
+
+ //
+ // We don't know what state the media is in anymore.
+ //
+
+ ClasspInternalSetMediaChangeState(FdoExtension,
+ MediaUnknown,
+ FALSE
+ );
+
+ //
+ // Reset the MCN timer.
+ //
+
+ ClassResetMediaChangeTimer(FdoExtension);
+
+ DebugPrint((ClassDebugMCN, "MCD is enabled\n"));
+
+ } else {
+
+ DebugPrint((ClassDebugMCN, "MCD still disabled\n"));
+
+ }
+
+
+ //
+ // Let something else run.
+ //
+
+ KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+ return;
+} // end ClassEnableMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?
+ClassDisableMediaChangeDetection() ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+ULONG BreakOnMcnDisable = FALSE;
+
+VOID
+ClassDisableMediaChangeDetection(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+
+ PAGED_CODE();
+
+ if(info == NULL) {
+ return;
+ }
+
+ KeWaitForMutexObject(&info->MediaChangeMutex,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ info->MediaChangeDetectionDisableCount++;
+
+ DebugPrint((ClassDebugMCN, "ClassDisableMediaChangeDetection: "
+ "disable count is %d\n",
+ info->MediaChangeDetectionDisableCount));
+
+ KeReleaseMutex(&info->MediaChangeMutex, FALSE);
+
+ return;
+} // end ClassDisableMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCleanupMediaChangeDetection() ISSUE-2000/02/20-henrygab - why public?!
+
+Routine Description:
+
+ This routine will cleanup any resources allocated for MCN. It is called
+ by classpnp during remove device, and therefore is not typically required
+ by external drivers.
+
+Arguments:
+
+Return Value:
+
+--*/
+VOID
+ClassCleanupMediaChangeDetection(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PMEDIA_CHANGE_DETECTION_INFO info = FdoExtension->MediaChangeDetectionInfo;
+
+ PAGED_CODE()
+
+ if(info == NULL) {
+ return;
+ }
+
+ FdoExtension->MediaChangeDetectionInfo = NULL;
+
+ if (info->Gesn.Buffer) {
+ ExFreePool(info->Gesn.Buffer);
+ }
+ IoFreeIrp(info->MediaChangeIrp);
+ ExFreePool(info->SenseBuffer);
+ ExFreePool(info);
+ return;
+} // end ClassCleanupMediaChangeDetection()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMcnControl() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspMcnControl(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PIRP Irp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ (PCOMMON_DEVICE_EXTENSION) FdoExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PPREVENT_MEDIA_REMOVAL request = Irp->AssociatedIrp.SystemBuffer;
+
+ PFILE_OBJECT fileObject = irpStack->FileObject;
+ PFILE_OBJECT_EXTENSION fsContext = NULL;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ //
+ // Check to make sure we have a file object extension to keep track of this
+ // request. If not we'll fail it before synchronizing.
+ //
+
+ TRY {
+
+ if(fileObject != NULL) {
+ fsContext = ClasspGetFsContext(commonExtension, fileObject);
+ }else if(Irp->RequestorMode == KernelMode) { // && fileObject == NULL
+ fsContext = &FdoExtension->KernelModeMcnContext;
+ }
+
+ if (fsContext == NULL) {
+
+ //
+ // This handle isn't setup correctly. We can't let the
+ // operation go.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ LEAVE;
+ }
+
+ if(request->PreventMediaRemoval) {
+
+ //
+ // This is a lock command. Reissue the command in case bus or
+ // device was reset and the lock was cleared.
+ //
+
+ ClassDisableMediaChangeDetection(FdoExtension);
+ InterlockedIncrement(&(fsContext->McnDisableCount));
+
+ } else {
+
+ if(fsContext->McnDisableCount == 0) {
+ status = STATUS_INVALID_DEVICE_STATE;
+ LEAVE;
+ }
+
+ InterlockedDecrement(&(fsContext->McnDisableCount));
+ ClassEnableMediaChangeDetection(FdoExtension);
+ }
+
+ } FINALLY {
+
+ Irp->IoStatus.Status = status;
+
+ if(Srb) {
+ ExFreePool(Srb);
+ }
+
+ ClassReleaseRemoveLock(FdoExtension->DeviceObject, Irp);
+ ClassCompleteRequest(FdoExtension->DeviceObject,
+ Irp,
+ IO_NO_INCREMENT);
+ }
+ return status;
+} // end ClasspMcnControl(
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspMediaChangeRegistryCallBack()
+
+Routine Description:
+
+ This callback for a registry SZ or MULTI_SZ is called once for each
+ SZ in the value. It will attempt to match the data with the
+ UNICODE_STRING passed in as Context, and modify EntryContext if a
+ match is found. Written for ClasspCheckRegistryForMediaChangeCompletion
+
+Arguments:
+
+ ValueName - name of the key that was opened
+ ValueType - type of data stored in the value (REG_SZ for this routine)
+ ValueData - data in the registry, in this case a wide string
+ ValueLength - length of the data including the terminating null
+ Context - unicode string to compare against ValueData
+ EntryContext - should be initialized to 0, will be set to 1 if match found
+
+Return Value:
+
+ STATUS_SUCCESS
+ EntryContext will be 1 if found
+
+--*/
+NTSTATUS
+ClasspMediaChangeRegistryCallBack(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ PULONG valueFound;
+ PUNICODE_STRING deviceString;
+ PWSTR keyValue;
+
+ PAGED_CODE();
+ UNREFERENCED_PARAMETER(ValueName);
+
+
+ //
+ // if we have already set the value to true, exit
+ //
+
+ valueFound = EntryContext;
+ if ((*valueFound) != 0) {
+ DebugPrint((ClassDebugMCN, "ClasspMcnRegCB: already set to true\n"));
+ return STATUS_SUCCESS;
+ }
+
+ if (ValueLength == sizeof(WCHAR)) {
+ DebugPrint((ClassDebugError, "ClasspMcnRegCB: NULL string should "
+ "never be passed to registry call-back!\n"));
+ return STATUS_SUCCESS;
+ }
+
+
+ //
+ // if the data is not a terminated string, exit
+ //
+
+ if (ValueType != REG_SZ) {
+ return STATUS_SUCCESS;
+ }
+
+ deviceString = Context;
+ keyValue = ValueData;
+ ValueLength -= sizeof(WCHAR); // ignore the null character
+
+ //
+ // do not compare more memory than is in deviceString
+ //
+
+ if (ValueLength > deviceString->Length) {
+ ValueLength = deviceString->Length;
+ }
+
+ //
+ // if the strings match, disable autorun
+ //
+
+ if (RtlCompareMemory(deviceString->Buffer, keyValue, ValueLength) == ValueLength) {
+ DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: Match found\n"));
+ DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: DeviceString at %p\n",
+ deviceString->Buffer));
+ DebugPrint((ClassDebugMCN, "ClasspRegMcnCB: KeyValue at %p\n",
+ keyValue));
+ (*valueFound) = TRUE;
+ }
+
+ return STATUS_SUCCESS;
+} // end ClasspMediaChangeRegistryCallBack()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspTimerTick() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+VOID
+ClasspTimerTick(
+ PDEVICE_OBJECT DeviceObject,
+ PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ ULONG isRemoved;
+
+ ASSERT(commonExtension->IsFdo);
+
+ //
+ // Do any media change work
+ //
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
+
+ //
+ // 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
+
+ if(!isRemoved) {
+
+ PFAILURE_PREDICTION_INFO info = fdoExtension->FailurePredictionInfo;
+
+ //
+ // Do any media change detection work
+ //
+
+ if (fdoExtension->MediaChangeDetectionInfo != NULL) {
+
+ ClassCheckMediaState(fdoExtension);
+
+ }
+
+ //
+ // Do any failure prediction work
+ //
+ if ((info != NULL) && (info->Method != FailurePredictionNone)) {
+
+ ULONG countDown;
+ ULONG active;
+
+ if (ClasspCanSendPollingIrp(fdoExtension)) {
+
+ //
+ // Synchronization is not required here since the Interlocked
+ // locked instruction guarantees atomicity. Other code that
+ // resets CountDown uses InterlockedExchange which is also
+ // atomic.
+ //
+ countDown = InterlockedDecrement(&info->CountDown);
+ if (countDown == 0) {
+
+ DebugPrint((4, "ClasspTimerTick: Send FP irp for %p\n",
+ DeviceObject));
+
+ if(info->WorkQueueItem == NULL) {
+
+ info->WorkQueueItem =
+ IoAllocateWorkItem(fdoExtension->DeviceObject);
+
+ if(info->WorkQueueItem == NULL) {
+
+ //
+ // Set the countdown to one minute in the future.
+ // we'll try again then in the hopes there's more
+ // free memory.
+ //
+
+ DebugPrint((1, "ClassTimerTick: Couldn't allocate "
+ "item - try again in one minute\n"));
+ InterlockedExchange(&info->CountDown, 60);
+
+ } else {
+
+ //
+ // Grab the remove lock so that removal will block
+ // until the work item is done.
+ //
+
+ ClassAcquireRemoveLock(fdoExtension->DeviceObject,
+ info->WorkQueueItem);
+
+ IoQueueWorkItem(info->WorkQueueItem,
+ ClasspFailurePredict,
+ DelayedWorkQueue,
+ info);
+ }
+
+ } else {
+
+ DebugPrint((3, "ClasspTimerTick: Failure "
+ "Prediction work item is "
+ "already active for device %p\n",
+ DeviceObject));
+
+ }
+ } // end (countdown == 0)
+
+ } else {
+ //
+ // If device is sleeping then just rearm polling timer
+ DebugPrint((4, "ClassTimerTick, SHHHH!!! device is %p is sleeping\n",
+ DeviceObject));
+ }
+
+ } // end failure prediction polling
+
+ //
+ // Give driver a chance to do its own specific work
+ //
+
+ if (commonExtension->DriverExtension->InitData.ClassTick != NULL) {
+
+ commonExtension->DriverExtension->InitData.ClassTick(DeviceObject);
+
+ } // end device specific tick handler
+ } // end check for removed
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP)ClasspTimerTick);
+} // end ClasspTimerTick()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspEnableTimer() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspEnableTimer(
+ PDEVICE_OBJECT DeviceObject
+ )
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if (DeviceObject->Timer == NULL) {
+
+ status = IoInitializeTimer(DeviceObject, ClasspTimerTick, NULL);
+
+ } else {
+
+ status = STATUS_SUCCESS;
+
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ IoStartTimer(DeviceObject);
+ DebugPrint((1, "ClasspEnableTimer: Once a second timer enabled "
+ "for device %p\n", DeviceObject));
+
+ }
+
+ DebugPrint((1, "ClasspEnableTimer: Device %p, Status %lx "
+ "initializing timer\n", DeviceObject, status));
+
+ return status;
+
+} // end ClasspEnableTimer()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspDisableTimer() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspDisableTimer(
+ PDEVICE_OBJECT DeviceObject
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PMEDIA_CHANGE_DETECTION_INFO mCDInfo = fdoExtension->MediaChangeDetectionInfo;
+ PFAILURE_PREDICTION_INFO fPInfo = fdoExtension->FailurePredictionInfo;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if (DeviceObject->Timer != NULL) {
+
+ //
+ // we are only going to stop the actual timer in remove device routine.
+ // it is the responsibility of the code within the timer routine to
+ // check if the device is removed and not processing io for the final
+ // call.
+ // this keeps the code clean and prevents lots of bugs.
+ //
+
+
+ IoStopTimer(DeviceObject);
+ DebugPrint((3, "ClasspDisableTimer: Once a second timer disabled "
+ "for device %p\n", DeviceObject));
+
+ } else {
+
+ DebugPrint((1, "ClasspDisableTimer: Timer never enabled\n"));
+
+ }
+
+ return STATUS_SUCCESS;
+} // end ClasspDisableTimer()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspFailurePredict() - ISSUE-2000/02/20-henrygab - not documented
+
+Routine Description:
+
+ This routine
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+Note: this function can be called (via the workitem callback) after the paging device is shut down,
+ so it must be PAGE LOCKED.
+--*/
+VOID
+ClasspFailurePredict(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PFAILURE_PREDICTION_INFO Info
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PIO_WORKITEM workItem;
+ STORAGE_PREDICT_FAILURE checkFailure;
+ SCSI_ADDRESS scsiAddress;
+
+ NTSTATUS status;
+
+ ASSERT(Info != NULL);
+
+ DebugPrint((1, "ClasspFailurePredict: Polling for failure\n"));
+
+ //
+ // Mark the work item as inactive and reset the countdown timer. we
+ // can't risk freeing the work item until we've released the remove-lock
+ // though - if we do it might get resused as a tag before we can release
+ // the lock.
+ //
+
+ InterlockedExchange(&Info->CountDown, Info->Period);
+ workItem = InterlockedExchangePointer(&(Info->WorkQueueItem), NULL);
+
+ if (ClasspCanSendPollingIrp(fdoExtension)) {
+
+ KEVENT event;
+ PDEVICE_OBJECT topOfStack;
+ PIRP irp = NULL;
+ IO_STATUS_BLOCK ioStatus;
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ topOfStack = IoGetAttachedDeviceReference(DeviceObject);
+
+ //
+ // Send down irp to see if drive is predicting failure
+ //
+
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_STORAGE_PREDICT_FAILURE,
+ topOfStack,
+ NULL,
+ 0,
+ &checkFailure,
+ sizeof(STORAGE_PREDICT_FAILURE),
+ FALSE,
+ &event,
+ &ioStatus);
+
+
+ if (irp != NULL) {
+ status = IoCallDriver(topOfStack, irp);
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ if (NT_SUCCESS(status) && (checkFailure.PredictFailure)) {
+
+ checkFailure.PredictFailure = 512;
+
+ //
+ // Send down irp to get scsi address
+ //
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ RtlZeroMemory(&scsiAddress, sizeof(SCSI_ADDRESS));
+ irp = IoBuildDeviceIoControlRequest(
+ IOCTL_SCSI_GET_ADDRESS,
+ topOfStack,
+ NULL,
+ 0,
+ &scsiAddress,
+ sizeof(SCSI_ADDRESS),
+ FALSE,
+ &event,
+ &ioStatus);
+
+ if (irp != NULL) {
+ status = IoCallDriver(topOfStack, irp);
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+ }
+
+ ClassNotifyFailurePredicted(fdoExtension,
+ (PUCHAR)&checkFailure,
+ sizeof(checkFailure),
+ (BOOLEAN)(fdoExtension->FailurePredicted == FALSE),
+ 2,
+ scsiAddress.PathId,
+ scsiAddress.TargetId,
+ scsiAddress.Lun);
+
+ fdoExtension->FailurePredicted = TRUE;
+
+ }
+
+ ObDereferenceObject(topOfStack);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP) workItem);
+ IoFreeWorkItem(workItem);
+ return;
+} // end ClasspFailurePredict()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassNotifyFailurePredicted() ISSUE-alanwar-2000/02/20 - not documented
+
+Routine Description:
+
+Arguments:
+
+Return Value:
+
+--*/
+VOID
+ClassNotifyFailurePredicted(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ PUCHAR Buffer,
+ ULONG BufferSize,
+ BOOLEAN LogError,
+ ULONG UniqueErrorValue,
+ UCHAR PathId,
+ UCHAR TargetId,
+ UCHAR Lun
+ )
+{
+ PIO_ERROR_LOG_PACKET logEntry;
+
+ DebugPrint((1, "ClasspFailurePredictPollCompletion: Failure predicted for device %p\n", FdoExtension->DeviceObject));
+
+ //
+ // Fire off a WMI event
+ //
+ ClassWmiFireEvent(FdoExtension->DeviceObject,
+ &StoragePredictFailureEventGuid,
+ 0,
+ BufferSize,
+ Buffer);
+
+ //
+ // Log an error into the eventlog
+ //
+
+ if (LogError)
+ {
+ logEntry = IoAllocateErrorLogEntry(
+ FdoExtension->DeviceObject,
+ sizeof(IO_ERROR_LOG_PACKET) + (3 * sizeof(ULONG)));
+
+ if (logEntry != NULL)
+ {
+
+ logEntry->FinalStatus = STATUS_SUCCESS;
+ logEntry->ErrorCode = IO_WRN_FAILURE_PREDICTED;
+ logEntry->SequenceNumber = 0;
+ logEntry->MajorFunctionCode = IRP_MJ_DEVICE_CONTROL;
+ logEntry->IoControlCode = IOCTL_STORAGE_PREDICT_FAILURE;
+ logEntry->RetryCount = 0;
+ logEntry->UniqueErrorValue = UniqueErrorValue;
+ logEntry->DumpDataSize = 3;
+
+ logEntry->DumpData[0] = PathId;
+ logEntry->DumpData[1] = TargetId;
+ logEntry->DumpData[2] = Lun;
+
+ //
+ // Write the error log packet.
+ //
+
+ IoWriteErrorLogEntry(logEntry);
+ }
+ }
+} // end ClassNotifyFailurePredicted()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSetFailurePredictionPoll()
+
+Routine Description:
+
+ This routine enables polling for failure prediction, setting the timer
+ to fire every N seconds as specified by the PollingPeriod.
+
+Arguments:
+
+ FdoExtension - the device to setup failure prediction for.
+
+ FailurePredictionMethod - specific failure prediction method to use
+ if set to FailurePredictionNone, will disable failure detection
+
+ PollingPeriod - if 0 then no change to current polling timer
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS
+ClassSetFailurePredictionPoll(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ FAILURE_PREDICTION_METHOD FailurePredictionMethod,
+ ULONG PollingPeriod
+ )
+{
+ PFAILURE_PREDICTION_INFO info;
+ NTSTATUS status;
+ DEVICE_POWER_STATE powerState;
+
+ PAGED_CODE();
+
+ if (FdoExtension->FailurePredictionInfo == NULL) {
+
+ if (FailurePredictionMethod != FailurePredictionNone) {
+
+ info = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(FAILURE_PREDICTION_INFO),
+ CLASS_TAG_FAILURE_PREDICT);
+
+ if (info == NULL) {
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ KeInitializeEvent(&info->Event, SynchronizationEvent, TRUE);
+
+ info->WorkQueueItem = NULL;
+ info->Period = DEFAULT_FAILURE_PREDICTION_PERIOD;
+
+ } else {
+
+ //
+ // FaultPrediction has not been previously initialized, nor
+ // is it being initialized now. No need to do anything.
+ //
+ return STATUS_SUCCESS;
+
+ }
+
+ FdoExtension->FailurePredictionInfo = info;
+
+ } else {
+
+ info = FdoExtension->FailurePredictionInfo;
+
+ }
+
+ KeWaitForSingleObject(&info->Event,
+ UserRequest,
+ UserMode,
+ FALSE,
+ NULL);
+
+
+ //
+ // Reset polling period and counter. Setup failure detection type
+ //
+
+ if (PollingPeriod != 0) {
+
+ InterlockedExchange(&info->Period, PollingPeriod);
+
+ }
+
+ InterlockedExchange(&info->CountDown, info->Period);
+
+ info->Method = FailurePredictionMethod;
+ if (FailurePredictionMethod != FailurePredictionNone) {
+
+ status = ClasspEnableTimer(FdoExtension->DeviceObject);
+
+ if (NT_SUCCESS(status)) {
+ DebugPrint((3, "ClassEnableFailurePredictPoll: Enabled for "
+ "device %p\n", FdoExtension->DeviceObject));
+ }
+
+ } else {
+
+ status = ClasspDisableTimer(FdoExtension->DeviceObject);
+ DebugPrint((3, "ClassEnableFailurePredictPoll: Disabled for "
+ "device %p\n", FdoExtension->DeviceObject));
+ status = STATUS_SUCCESS;
+
+ }
+
+ KeSetEvent(&info->Event, IO_NO_INCREMENT, FALSE);
+
+ return status;
+} // end ClassSetFailurePredictionPoll()
+
diff --git a/reactos/drivers/storage/classpnp/class.c b/reactos/drivers/storage/classpnp/class.c
new file mode 100644
index 00000000000..0a1c28ea9c5
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/class.c
@@ -0,0 +1,9182 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ class.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#define CLASS_INIT_GUID 1
+#include "classp.h"
+#include "debug.h"
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(INIT, DriverEntry)
+ #pragma alloc_text(PAGE, ClassAddDevice)
+ #pragma alloc_text(PAGE, ClassClaimDevice)
+ #pragma alloc_text(PAGE, ClassCreateDeviceObject)
+ #pragma alloc_text(PAGE, ClassDispatchPnp)
+ #pragma alloc_text(PAGE, ClassGetDescriptor)
+ #pragma alloc_text(PAGE, ClassGetPdoId)
+ #pragma alloc_text(PAGE, ClassInitialize)
+ #pragma alloc_text(PAGE, ClassInitializeEx)
+ #pragma alloc_text(PAGE, ClassInvalidateBusRelations)
+ #pragma alloc_text(PAGE, ClassMarkChildMissing)
+ #pragma alloc_text(PAGE, ClassMarkChildrenMissing)
+ #pragma alloc_text(PAGE, ClassModeSense)
+ #pragma alloc_text(PAGE, ClassPnpQueryFdoRelations)
+ #pragma alloc_text(PAGE, ClassPnpStartDevice)
+ #pragma alloc_text(PAGE, ClassQueryPnpCapabilities)
+ #pragma alloc_text(PAGE, ClassQueryTimeOutRegistryValue)
+ #pragma alloc_text(PAGE, ClassRemoveDevice)
+ #pragma alloc_text(PAGE, ClassRetrieveDeviceRelations)
+ #pragma alloc_text(PAGE, ClassUpdateInformationInRegistry)
+ #pragma alloc_text(PAGE, ClassSendDeviceIoControlSynchronous)
+ #pragma alloc_text(PAGE, ClassUnload)
+ #pragma alloc_text(PAGE, ClasspAllocateReleaseRequest)
+ #pragma alloc_text(PAGE, ClasspFreeReleaseRequest)
+ #pragma alloc_text(PAGE, ClasspInitializeHotplugInfo)
+ #pragma alloc_text(PAGE, ClasspRegisterMountedDeviceInterface)
+ #pragma alloc_text(PAGE, ClasspScanForClassHacks)
+ #pragma alloc_text(PAGE, ClasspScanForSpecialInRegistry)
+#endif
+
+ULONG ClassPnpAllowUnload = TRUE;
+
+
+#define FirstDriveLetter 'C'
+#define LastDriveLetter 'Z'
+
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+DriverEntry()
+
+Routine Description:
+
+ Temporary entry point needed to initialize the class system dll.
+ It doesn't do anything.
+
+Arguments:
+
+ DriverObject - Pointer to the driver object created by the system.
+
+Return Value:
+
+ STATUS_SUCCESS
+
+--*/
+NTSTATUS
+NTAPI
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ return STATUS_SUCCESS;
+}
+
+
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitialize()
+
+Routine Description:
+
+ This routine is called by a class driver during its
+ DriverEntry routine to initialize the driver.
+
+Arguments:
+
+ Argument1 - Driver Object.
+ Argument2 - Registry Path.
+ InitializationData - Device-specific driver's initialization data.
+
+Return Value:
+
+ A valid return code for a DriverEntry routine.
+
+--*/
+ULONG
+ClassInitialize(
+ IN PVOID Argument1,
+ IN PVOID Argument2,
+ IN PCLASS_INIT_DATA InitializationData
+ )
+{
+ PDRIVER_OBJECT DriverObject = Argument1;
+ PUNICODE_STRING RegistryPath = Argument2;
+
+ PCLASS_DRIVER_EXTENSION driverExtension;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DebugPrint((3,"\n\nSCSI Class Driver\n"));
+
+ ClasspInitializeDebugGlobals();
+
+ //
+ // Validate the length of this structure. This is effectively a
+ // version check.
+ //
+
+ if (InitializationData->InitializationDataSize != sizeof(CLASS_INIT_DATA)) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0,"ClassInitialize: Class driver wrong version\n"));
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ //
+ // Check that each required entry is not NULL. Note that Shutdown, Flush and Error
+ // are not required entry points.
+ //
+
+ if ((!InitializationData->FdoData.ClassDeviceControl) ||
+ (!((InitializationData->FdoData.ClassReadWriteVerification) ||
+ (InitializationData->ClassStartIo))) ||
+ (!InitializationData->ClassAddDevice) ||
+ (!InitializationData->FdoData.ClassStartDevice)) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0,
+ "ClassInitialize: Class device-specific driver missing required "
+ "FDO entry\n"));
+
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ if ((InitializationData->ClassEnumerateDevice) &&
+ ((!InitializationData->PdoData.ClassDeviceControl) ||
+ (!InitializationData->PdoData.ClassStartDevice) ||
+ (!((InitializationData->PdoData.ClassReadWriteVerification) ||
+ (InitializationData->ClassStartIo))))) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0, "ClassInitialize: Class device-specific missing "
+ "required PDO entry\n"));
+
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ if((InitializationData->FdoData.ClassStopDevice == NULL) ||
+ ((InitializationData->ClassEnumerateDevice != NULL) &&
+ (InitializationData->PdoData.ClassStopDevice == NULL))) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0, "ClassInitialize: Class device-specific missing "
+ "required PDO entry\n"));
+ ASSERT(FALSE);
+ return (ULONG) STATUS_REVISION_MISMATCH;
+ }
+
+ //
+ // Setup the default power handlers if the class driver didn't provide
+ // any.
+ //
+
+ if(InitializationData->FdoData.ClassPowerDevice == NULL) {
+ InitializationData->FdoData.ClassPowerDevice = ClassMinimalPowerHandler;
+ }
+
+ if((InitializationData->ClassEnumerateDevice != NULL) &&
+ (InitializationData->PdoData.ClassPowerDevice == NULL)) {
+ InitializationData->PdoData.ClassPowerDevice = ClassMinimalPowerHandler;
+ }
+
+ //
+ // warn that unload is not supported
+ //
+ // ISSUE-2000/02/03-peterwie
+ // We should think about making this a fatal error.
+ //
+
+ if(InitializationData->ClassUnload == NULL) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0, "ClassInitialize: driver does not support unload %wZ\n",
+ RegistryPath));
+ }
+
+ //
+ // Create an extension for the driver object
+ //
+
+ status = IoAllocateDriverObjectExtension(DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY,
+ sizeof(CLASS_DRIVER_EXTENSION),
+ &driverExtension);
+
+ if(NT_SUCCESS(status)) {
+
+ //
+ // Copy the registry path into the driver extension so we can use it later
+ //
+
+ driverExtension->RegistryPath.Length = RegistryPath->Length;
+ driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength;
+
+ driverExtension->RegistryPath.Buffer =
+ ExAllocatePoolWithTag(PagedPool,
+ RegistryPath->MaximumLength,
+ '1CcS');
+
+ if(driverExtension->RegistryPath.Buffer == NULL) {
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ return status;
+ }
+
+ RtlCopyUnicodeString(
+ &(driverExtension->RegistryPath),
+ RegistryPath);
+
+ //
+ // Copy the initialization data into the driver extension so we can reuse
+ // it during our add device routine
+ //
+
+ RtlCopyMemory(
+ &(driverExtension->InitData),
+ InitializationData,
+ sizeof(CLASS_INIT_DATA));
+
+ driverExtension->DeviceCount = 0;
+
+ } else if (status == STATUS_OBJECT_NAME_COLLISION) {
+
+ //
+ // The extension already exists - get a pointer to it
+ //
+
+ driverExtension = IoGetDriverObjectExtension(DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+
+ ASSERT(driverExtension != NULL);
+
+ } else {
+
+ DebugPrint((1, "ClassInitialize: Class driver extension could not be "
+ "allocated %lx\n", status));
+ return status;
+ }
+
+ //
+ // Update driver object with entry points.
+ //
+
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreateClose;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassCreateClose;
+ DriverObject->MajorFunction[IRP_MJ_READ] = ClassReadWrite;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = ClassReadWrite;
+ DriverObject->MajorFunction[IRP_MJ_SCSI] = ClassInternalIoControl;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControlDispatch;
+ DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = ClassShutdownFlush;
+ DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = ClassShutdownFlush;
+ DriverObject->MajorFunction[IRP_MJ_PNP] = ClassDispatchPnp;
+ DriverObject->MajorFunction[IRP_MJ_POWER] = ClassDispatchPower;
+ DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ClassSystemControl;
+
+ if (InitializationData->ClassStartIo) {
+ DriverObject->DriverStartIo = ClasspStartIo;
+ }
+
+ if ((InitializationData->ClassUnload) && (ClassPnpAllowUnload == TRUE)) {
+ DriverObject->DriverUnload = ClassUnload;
+ } else {
+ DriverObject->DriverUnload = NULL;
+ }
+
+ DriverObject->DriverExtension->AddDevice = ClassAddDevice;
+
+ DbgPrint("Driver is ready to go\n");
+ status = STATUS_SUCCESS;
+ return status;
+} // end ClassInitialize()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeEx()
+
+Routine Description:
+
+ This routine is allows the caller to do any extra initialization or
+ setup that is not done in ClassInitialize. The operation is
+ controlled by the GUID that is passed and the contents of the Data
+ parameter is dependent upon the GUID.
+
+ This is the list of supported operations:
+
+ Guid - GUID_CLASSPNP_QUERY_REGINFOEX
+ Data - A PCLASS_QUERY_WMI_REGINFO_EX callback function pointer
+
+ Initialized classpnp to callback a PCLASS_QUERY_WMI_REGINFO_EX
+ callback instead of a PCLASS_QUERY_WMI_REGINFO callback. The
+ former callback allows the driver to specify the name of the
+ mof resource.
+
+Arguments:
+
+ DriverObject
+ Guid
+ Data
+
+Return Value:
+
+ Status Code
+
+--*/
+ULONG
+ClassInitializeEx(
+ IN PDRIVER_OBJECT DriverObject,
+ IN LPGUID Guid,
+ IN PVOID Data
+ )
+{
+ PCLASS_DRIVER_EXTENSION driverExtension;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ driverExtension = IoGetDriverObjectExtension( DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY
+ );
+ if (IsEqualGUID(Guid, &ClassGuidQueryRegInfoEx))
+ {
+ PCLASS_QUERY_WMI_REGINFO_EX_LIST List;
+
+ //
+ // Indicate the device supports PCLASS_QUERY_REGINFO_EX
+ // callback instead of PCLASS_QUERY_REGINFO callback.
+ //
+ List = (PCLASS_QUERY_WMI_REGINFO_EX_LIST)Data;
+
+ if (List->Size == sizeof(CLASS_QUERY_WMI_REGINFO_EX_LIST))
+ {
+ driverExtension->ClassFdoQueryWmiRegInfoEx = List->ClassFdoQueryWmiRegInfoEx;
+ driverExtension->ClassPdoQueryWmiRegInfoEx = List->ClassPdoQueryWmiRegInfoEx;
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ status = STATUS_NOT_SUPPORTED;
+ }
+
+ return(status);
+
+} // end ClassInitializeEx()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassUnload()
+
+Routine Description:
+
+ called when there are no more references to the driver. this allows
+ drivers to be updated without rebooting.
+
+Arguments:
+
+ DriverObject - a pointer to the driver object that is being unloaded
+
+Status:
+
+--*/
+VOID
+ClassUnload(
+ IN PDRIVER_OBJECT DriverObject
+ )
+{
+ PCLASS_DRIVER_EXTENSION driverExtension;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ ASSERT( DriverObject->DeviceObject == NULL );
+
+ driverExtension = IoGetDriverObjectExtension( DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY
+ );
+
+ ASSERT(driverExtension != NULL);
+ ASSERT(driverExtension->RegistryPath.Buffer != NULL);
+ ASSERT(driverExtension->InitData.ClassUnload != NULL);
+
+ DebugPrint((1, "ClassUnload: driver unloading %wZ\n",
+ &driverExtension->RegistryPath));
+
+ //
+ // attempt to process the driver's unload routine first.
+ //
+
+ driverExtension->InitData.ClassUnload(DriverObject);
+
+ //
+ // free own allocated resources and return
+ //
+
+ ExFreePool( driverExtension->RegistryPath.Buffer );
+ driverExtension->RegistryPath.Buffer = NULL;
+ driverExtension->RegistryPath.Length = 0;
+ driverExtension->RegistryPath.MaximumLength = 0;
+
+ return;
+} // end ClassUnload()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAddDevice()
+
+Routine Description:
+
+ SCSI class driver add device routine. This is called by pnp when a new
+ physical device come into being.
+
+ This routine will call out to the class driver to verify that it should
+ own this device then will create and attach a device object and then hand
+ it to the driver to initialize and create symbolic links
+
+Arguments:
+
+ DriverObject - a pointer to the driver object that this is being created for
+ PhysicalDeviceObject - a pointer to the physical device object
+
+Status: STATUS_NO_SUCH_DEVICE if the class driver did not want this device
+ STATUS_SUCCESS if the creation and attachment was successful
+ status of device creation and initialization
+
+--*/
+NTSTATUS
+ClassAddDevice(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PDEVICE_OBJECT PhysicalDeviceObject
+ )
+{
+ PCLASS_DRIVER_EXTENSION driverExtension =
+ IoGetDriverObjectExtension(DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DbgPrint("got a device\n");
+ status = driverExtension->InitData.ClassAddDevice(DriverObject,
+ PhysicalDeviceObject);
+ return status;
+} // end ClassAddDevice()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDispatchPnp()
+
+Routine Description:
+
+ Storage class driver pnp routine. This is called by the io system when
+ a PNP request is sent to the device.
+
+Arguments:
+
+ DeviceObject - pointer to the device object
+
+ Irp - pointer to the io request packet
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassDispatchPnp(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ BOOLEAN isFdo = commonExtension->IsFdo;
+
+ PCLASS_DRIVER_EXTENSION driverExtension;
+ PCLASS_INIT_DATA initData;
+ PCLASS_DEV_INFO devInfo;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+
+ NTSTATUS status = Irp->IoStatus.Status;
+ BOOLEAN completeRequest = TRUE;
+ BOOLEAN lockReleased = FALSE;
+
+ ULONG isRemoved;
+
+ PAGED_CODE();
+
+ //
+ // Extract all the useful information out of the driver object
+ // extension
+ //
+
+ driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+ if (driverExtension){
+
+ initData = &(driverExtension->InitData);
+
+ if(isFdo) {
+ devInfo = &(initData->FdoData);
+ } else {
+ devInfo = &(initData->PdoData);
+ }
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): minor code %#x for %s %p\n",
+ DeviceObject, Irp,
+ irpStack->MinorFunction,
+ isFdo ? "fdo" : "pdo",
+ DeviceObject));
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): previous %#x, current %#x\n",
+ DeviceObject, Irp,
+ commonExtension->PreviousState,
+ commonExtension->CurrentState));
+
+ switch(irpStack->MinorFunction) {
+
+ case IRP_MN_START_DEVICE: {
+
+ //
+ // if this is sent to the FDO we should forward it down the
+ // attachment chain before we start the FDO.
+ //
+
+ if (isFdo) {
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+
+ if (NT_SUCCESS(status)){
+ status = Irp->IoStatus.Status = ClassPnpStartDevice(DeviceObject);
+ }
+
+ break;
+ }
+
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS: {
+
+ DEVICE_RELATION_TYPE type =
+ irpStack->Parameters.QueryDeviceRelations.Type;
+
+ PDEVICE_RELATIONS deviceRelations = NULL;
+
+ if(!isFdo) {
+
+ if(type == TargetDeviceRelation) {
+
+ //
+ // Device relations has one entry built in to it's size.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+
+ deviceRelations = ExAllocatePoolWithTag(PagedPool,
+ sizeof(DEVICE_RELATIONS),
+ '2CcS');
+
+ if(deviceRelations != NULL) {
+
+ RtlZeroMemory(deviceRelations,
+ sizeof(DEVICE_RELATIONS));
+
+ Irp->IoStatus.Information = (ULONG_PTR) deviceRelations;
+
+ deviceRelations->Count = 1;
+ deviceRelations->Objects[0] = DeviceObject;
+ ObReferenceObject(deviceRelations->Objects[0]);
+
+ status = STATUS_SUCCESS;
+ }
+
+ } else {
+ //
+ // PDO's just complete enumeration requests without altering
+ // the status.
+ //
+
+ status = Irp->IoStatus.Status;
+ }
+
+ break;
+
+ } else if (type == BusRelations) {
+
+ ASSERT(commonExtension->IsInitialized);
+
+ //
+ // Make sure we support enumeration
+ //
+
+ if(initData->ClassEnumerateDevice == NULL) {
+
+ //
+ // Just send the request down to the lower driver. Perhaps
+ // It can enumerate children.
+ //
+
+ } else {
+
+ //
+ // Re-enumerate the device
+ //
+
+ status = ClassPnpQueryFdoRelations(DeviceObject, Irp);
+
+ if(!NT_SUCCESS(status)) {
+ completeRequest = TRUE;
+ break;
+ }
+ }
+ }
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ completeRequest = FALSE;
+
+ break;
+ }
+
+ case IRP_MN_QUERY_ID: {
+
+ BUS_QUERY_ID_TYPE idType = irpStack->Parameters.QueryId.IdType;
+ UNICODE_STRING unicodeString;
+
+ if(isFdo) {
+
+ //
+ // FDO's should just forward the query down to the lower
+ // device objects
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ completeRequest = FALSE;
+ break;
+ }
+
+ //
+ // PDO's need to give an answer - this is easy for now
+ //
+
+ RtlInitUnicodeString(&unicodeString, NULL);
+
+ status = ClassGetPdoId(DeviceObject,
+ idType,
+ &unicodeString);
+
+ if(status == STATUS_NOT_IMPLEMENTED) {
+ //
+ // The driver doesn't implement this ID (whatever it is).
+ // Use the status out of the IRP so that we don't mangle a
+ // response from someone else.
+ //
+
+ status = Irp->IoStatus.Status;
+ } else if(NT_SUCCESS(status)) {
+ Irp->IoStatus.Information = (ULONG_PTR) unicodeString.Buffer;
+ } else {
+ Irp->IoStatus.Information = (ULONG_PTR) NULL;
+ }
+
+ break;
+ }
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ case IRP_MN_QUERY_REMOVE_DEVICE: {
+
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): Processing QUERY_%s irp\n",
+ DeviceObject, Irp,
+ ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
+ "STOP" : "REMOVE")));
+
+ //
+ // If this device is in use for some reason (paging, etc...)
+ // then we need to fail the request.
+ //
+
+ if(commonExtension->PagingPathCount != 0) {
+
+ DebugPrint((1, "ClassDispatchPnp (%p,%p): device is in paging "
+ "path and cannot be removed\n",
+ DeviceObject, Irp));
+ status = STATUS_DEVICE_BUSY;
+ break;
+ }
+
+ //
+ // Check with the class driver to see if the query operation
+ // can succeed.
+ //
+
+ if(irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) {
+ status = devInfo->ClassStopDevice(DeviceObject,
+ irpStack->MinorFunction);
+ } else {
+ status = devInfo->ClassRemoveDevice(DeviceObject,
+ irpStack->MinorFunction);
+ }
+
+ if(NT_SUCCESS(status)) {
+
+ //
+ // ASSERT that we never get two queries in a row, as
+ // this will severly mess up the state machine
+ //
+ ASSERT(commonExtension->CurrentState != irpStack->MinorFunction);
+ commonExtension->PreviousState = commonExtension->CurrentState;
+ commonExtension->CurrentState = irpStack->MinorFunction;
+
+ if(isFdo) {
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): Forwarding QUERY_"
+ "%s irp\n", DeviceObject, Irp,
+ ((irpStack->MinorFunction == IRP_MN_QUERY_STOP_DEVICE) ?
+ "STOP" : "REMOVE")));
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ }
+ }
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): Final status == %x\n",
+ DeviceObject, Irp, status));
+
+ break;
+ }
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ case IRP_MN_CANCEL_REMOVE_DEVICE: {
+
+ //
+ // Check with the class driver to see if the query or cancel
+ // operation can succeed.
+ //
+
+ if(irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) {
+ status = devInfo->ClassStopDevice(DeviceObject,
+ irpStack->MinorFunction);
+ ASSERTMSG("ClassDispatchPnp !! CANCEL_STOP_DEVICE should "
+ "never be failed\n", NT_SUCCESS(status));
+ } else {
+ status = devInfo->ClassRemoveDevice(DeviceObject,
+ irpStack->MinorFunction);
+ ASSERTMSG("ClassDispatchPnp !! CANCEL_REMOVE_DEVICE should "
+ "never be failed\n", NT_SUCCESS(status));
+ }
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // We got a CANCEL - roll back to the previous state only
+ // if the current state is the respective QUERY state.
+ //
+
+ if(((irpStack->MinorFunction == IRP_MN_CANCEL_STOP_DEVICE) &&
+ (commonExtension->CurrentState == IRP_MN_QUERY_STOP_DEVICE)
+ ) ||
+ ((irpStack->MinorFunction == IRP_MN_CANCEL_REMOVE_DEVICE) &&
+ (commonExtension->CurrentState == IRP_MN_QUERY_REMOVE_DEVICE)
+ )
+ ) {
+
+ commonExtension->CurrentState =
+ commonExtension->PreviousState;
+ commonExtension->PreviousState = 0xff;
+
+ }
+
+ if(isFdo) {
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ completeRequest = FALSE;
+ } else {
+ status = STATUS_SUCCESS;
+ }
+
+ break;
+ }
+
+ case IRP_MN_STOP_DEVICE: {
+
+ //
+ // These all mean nothing to the class driver currently. The
+ // port driver will handle all queueing when necessary.
+ //
+
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): got stop request for %s\n",
+ DeviceObject, Irp,
+ (isFdo ? "fdo" : "pdo")
+ ));
+
+ ASSERT(commonExtension->PagingPathCount == 0);
+
+ //
+ // ISSUE-2000/02/03-peterwie
+ // if we stop the timer here then it means no class driver can
+ // do i/o in its ClassStopDevice routine. This is because the
+ // retry (among other things) is tied into the tick handler
+ // and disabling retries could cause the class driver to deadlock.
+ // Currently no class driver we're aware of issues i/o in its
+ // Stop routine but this is a case we may want to defend ourself
+ // against.
+ //
+
+ if (DeviceObject->Timer) {
+ IoStopTimer(DeviceObject);
+ }
+
+ status = devInfo->ClassStopDevice(DeviceObject, IRP_MN_STOP_DEVICE);
+
+ ASSERTMSG("ClassDispatchPnp !! STOP_DEVICE should "
+ "never be failed\n", NT_SUCCESS(status));
+
+ if(isFdo) {
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ }
+
+ if(NT_SUCCESS(status)) {
+ commonExtension->CurrentState = irpStack->MinorFunction;
+ commonExtension->PreviousState = 0xff;
+ }
+
+ break;
+ }
+
+ case IRP_MN_REMOVE_DEVICE:
+ case IRP_MN_SURPRISE_REMOVAL: {
+
+ PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
+ UCHAR removeType = irpStack->MinorFunction;
+
+ if (commonExtension->PagingPathCount != 0) {
+ DBGTRACE(ClassDebugWarning, ("ClassDispatchPnp (%p,%p): paging device is getting removed!", DeviceObject, Irp));
+ }
+
+ //
+ // Release the lock for this IRP before calling in.
+ //
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ lockReleased = TRUE;
+
+ /*
+ * If a timer was started on the device, stop it.
+ */
+ if (DeviceObject->Timer) {
+ IoStopTimer(DeviceObject);
+ }
+
+ /*
+ * "Fire-and-forget" the remove irp to the lower stack.
+ * Don't touch the irp (or the irp stack!) after this.
+ */
+ if (isFdo) {
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ ASSERT(NT_SUCCESS(status));
+ completeRequest = FALSE;
+ }
+ else {
+ status = STATUS_SUCCESS;
+ }
+
+ /*
+ * Do our own cleanup and call the class driver's remove
+ * cleanup routine.
+ * For IRP_MN_REMOVE_DEVICE, this also deletes our device object,
+ * so don't touch the extension after this.
+ */
+ commonExtension->PreviousState = commonExtension->CurrentState;
+ commonExtension->CurrentState = removeType;
+ ClassRemoveDevice(DeviceObject, removeType);
+
+ break;
+ }
+
+ case IRP_MN_DEVICE_USAGE_NOTIFICATION: {
+
+ switch(irpStack->Parameters.UsageNotification.Type) {
+
+ case DeviceUsageTypePaging: {
+
+ BOOLEAN setPagable;
+
+ if((irpStack->Parameters.UsageNotification.InPath) &&
+ (commonExtension->CurrentState != IRP_MN_START_DEVICE)) {
+
+ //
+ // Device isn't started. Don't allow adding a
+ // paging file, but allow a removal of one.
+ //
+
+ status = STATUS_DEVICE_NOT_READY;
+ break;
+ }
+
+ ASSERT(commonExtension->IsInitialized);
+
+ //
+ // need to synchronize this now...
+ //
+
+ KeEnterCriticalRegion();
+ status = KeWaitForSingleObject(&commonExtension->PathCountEvent,
+ Executive, KernelMode,
+ FALSE, NULL);
+ ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+
+ //
+ // If the volume is removable we should try to lock it in
+ // place or unlock it once per paging path count
+ //
+
+ if (commonExtension->IsFdo){
+ status = ClasspEjectionControl(
+ DeviceObject,
+ Irp,
+ InternalMediaLock,
+ (BOOLEAN)irpStack->Parameters.UsageNotification.InPath);
+ }
+
+ if (!NT_SUCCESS(status)){
+ KeSetEvent(&commonExtension->PathCountEvent, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ break;
+ }
+
+ //
+ // if removing last paging device, need to set DO_POWER_PAGABLE
+ // bit here, and possible re-set it below on failure.
+ //
+
+ setPagable = FALSE;
+
+ if (!irpStack->Parameters.UsageNotification.InPath &&
+ commonExtension->PagingPathCount == 1
+ ) {
+
+ //
+ // removing last paging file
+ // must have DO_POWER_PAGABLE bits set, but only
+ // if noone set the DO_POWER_INRUSH bit
+ //
+
+
+ if (TEST_FLAG(DeviceObject->Flags, DO_POWER_INRUSH)) {
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
+ "paging file removed, but "
+ "DO_POWER_INRUSH was set, so NOT "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ } else {
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): Last "
+ "paging file removed, "
+ "setting DO_POWER_PAGABLE\n",
+ DeviceObject, Irp));
+ SET_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = TRUE;
+ }
+
+ }
+
+ //
+ // forward the irp before finishing handling the
+ // special cases
+ //
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+
+ //
+ // now deal with the failure and success cases.
+ // note that we are not allowed to fail the irp
+ // once it is sent to the lower drivers.
+ //
+
+ if (NT_SUCCESS(status)) {
+
+ IoAdjustPagingPathCount(
+ &commonExtension->PagingPathCount,
+ irpStack->Parameters.UsageNotification.InPath);
+
+ if (irpStack->Parameters.UsageNotification.InPath) {
+ if (commonExtension->PagingPathCount == 1) {
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): "
+ "Clearing PAGABLE bit\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ }
+ }
+
+ } else {
+
+ //
+ // cleanup the changes done above
+ //
+
+ if (setPagable == TRUE) {
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): Unsetting "
+ "PAGABLE bit due to irp failure\n",
+ DeviceObject, Irp));
+ CLEAR_FLAG(DeviceObject->Flags, DO_POWER_PAGABLE);
+ setPagable = FALSE;
+ }
+
+ //
+ // relock or unlock the media if needed.
+ //
+
+ if (commonExtension->IsFdo) {
+
+ ClasspEjectionControl(
+ DeviceObject,
+ Irp,
+ InternalMediaLock,
+ (BOOLEAN)!irpStack->Parameters.UsageNotification.InPath);
+ }
+ }
+
+ //
+ // set the event so the next one can occur.
+ //
+
+ KeSetEvent(&commonExtension->PathCountEvent,
+ IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ break;
+ }
+
+ case DeviceUsageTypeHibernation: {
+
+ IoAdjustPagingPathCount(
+ &commonExtension->HibernationPathCount,
+ irpStack->Parameters.UsageNotification.InPath
+ );
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ if (!NT_SUCCESS(status)) {
+ IoAdjustPagingPathCount(
+ &commonExtension->HibernationPathCount,
+ !irpStack->Parameters.UsageNotification.InPath
+ );
+ }
+
+ break;
+ }
+
+ case DeviceUsageTypeDumpFile: {
+ IoAdjustPagingPathCount(
+ &commonExtension->DumpPathCount,
+ irpStack->Parameters.UsageNotification.InPath
+ );
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ if (!NT_SUCCESS(status)) {
+ IoAdjustPagingPathCount(
+ &commonExtension->DumpPathCount,
+ !irpStack->Parameters.UsageNotification.InPath
+ );
+ }
+
+ break;
+ }
+
+ default: {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_CAPABILITIES: {
+
+ DebugPrint((2, "ClassDispatchPnp (%p,%p): QueryCapabilities\n",
+ DeviceObject, Irp));
+
+ if(!isFdo) {
+
+ status = ClassQueryPnpCapabilities(
+ DeviceObject,
+ irpStack->Parameters.DeviceCapabilities.Capabilities
+ );
+
+ break;
+
+ } else {
+
+ PDEVICE_CAPABILITIES deviceCapabilities;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+
+ fdoExtension = DeviceObject->DeviceExtension;
+ fdoData = fdoExtension->PrivateFdoData;
+ deviceCapabilities =
+ irpStack->Parameters.DeviceCapabilities.Capabilities;
+
+ //
+ // forward the irp before handling the special cases
+ //
+
+ status = ClassForwardIrpSynchronous(commonExtension, Irp);
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+
+ //
+ // we generally want to remove the device from the hotplug
+ // applet, which requires the SR-OK bit to be set.
+ // only when the user specifies that they are capable of
+ // safely removing things do we want to clear this bit
+ // (saved in WriteCacheEnableOverride)
+ //
+ // setting of this bit is done either above, or by the
+ // lower driver.
+ //
+ // note: may not be started, so check we have FDO data first.
+ //
+
+ if (fdoData &&
+ fdoData->HotplugInfo.WriteCacheEnableOverride) {
+ if (deviceCapabilities->SurpriseRemovalOK) {
+ DebugPrint((1, "Classpnp: Clearing SR-OK bit in "
+ "device capabilities due to hotplug "
+ "device or media\n"));
+ }
+ deviceCapabilities->SurpriseRemovalOK = FALSE;
+ }
+ break;
+
+ } // end QUERY_CAPABILITIES for FDOs
+
+ ASSERT(FALSE);
+ break;
+
+
+ } // end QUERY_CAPABILITIES
+
+ default: {
+
+ if (isFdo){
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ completeRequest = FALSE;
+ }
+
+ break;
+ }
+ }
+ }
+ else {
+ ASSERT(driverExtension);
+ status = STATUS_INTERNAL_ERROR;
+ }
+
+ if (completeRequest){
+ Irp->IoStatus.Status = status;
+
+ if (!lockReleased){
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ }
+
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving with previous %#x, current %#x.", DeviceObject, Irp, commonExtension->PreviousState, commonExtension->CurrentState));
+ }
+ else {
+ /*
+ * The irp is already completed so don't touch it.
+ * This may be a remove so don't touch the device extension.
+ */
+ DBGTRACE(ClassDebugTrace, ("ClassDispatchPnp (%p,%p): leaving.", DeviceObject, Irp));
+ }
+
+ return status;
+} // end ClassDispatchPnp()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassPnpStartDevice()
+
+Routine Description:
+
+ Storage class driver routine for IRP_MN_START_DEVICE requests.
+ This routine kicks off any device specific initialization
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+
+ Irp - a pointer to the io request packet
+
+Return Value:
+
+ none
+
+--*/
+NTSTATUS ClassPnpStartDevice(IN PDEVICE_OBJECT DeviceObject)
+{
+ PCLASS_DRIVER_EXTENSION driverExtension;
+ PCLASS_INIT_DATA initData;
+
+ PCLASS_DEV_INFO devInfo;
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ BOOLEAN isFdo = commonExtension->IsFdo;
+
+ BOOLEAN isMountedDevice = TRUE;
+ UNICODE_STRING interfaceName;
+
+ BOOLEAN timerStarted;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+
+ initData = &(driverExtension->InitData);
+ if(isFdo) {
+ devInfo = &(initData->FdoData);
+ } else {
+ devInfo = &(initData->PdoData);
+ }
+
+ ASSERT(devInfo->ClassInitDevice != NULL);
+ ASSERT(devInfo->ClassStartDevice != NULL);
+
+ if (!commonExtension->IsInitialized){
+
+ //
+ // perform FDO/PDO specific initialization
+ //
+
+ if (isFdo){
+ STORAGE_PROPERTY_ID propertyId;
+
+ //
+ // allocate a private extension for class data
+ //
+
+ if (fdoExtension->PrivateFdoData == NULL) {
+ fdoExtension->PrivateFdoData =
+ ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(CLASS_PRIVATE_FDO_DATA),
+ CLASS_TAG_PRIVATE_DATA
+ );
+ }
+
+ if (fdoExtension->PrivateFdoData == NULL) {
+ DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
+ "private fdo data\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // initialize the struct's various fields.
+ //
+
+ RtlZeroMemory(fdoExtension->PrivateFdoData,
+ sizeof(CLASS_PRIVATE_FDO_DATA)
+ );
+ KeInitializeTimer(&fdoExtension->PrivateFdoData->Retry.Timer);
+ KeInitializeDpc(&fdoExtension->PrivateFdoData->Retry.Dpc,
+ ClasspRetryRequestDpc,
+ DeviceObject);
+ KeInitializeSpinLock(&fdoExtension->PrivateFdoData->Retry.Lock);
+ fdoExtension->PrivateFdoData->Retry.Granularity =
+ KeQueryTimeIncrement();
+ commonExtension->Reserved4 = (ULONG_PTR)(' GPH'); // debug aid
+
+ //
+ // NOTE: the old interface allowed the class driver to allocate
+ // this. this was unsafe for low-memory conditions. allocate one
+ // unconditionally now, and modify our internal functions to use
+ // our own exclusively as it is the only safe way to do this.
+ //
+
+ status = ClasspAllocateReleaseQueueIrp(fdoExtension);
+ if (!NT_SUCCESS(status)) {
+ DebugPrint((0, "ClassPnpStartDevice: Cannot allocate the "
+ "private release queue irp\n"));
+ return status;
+ }
+
+ //
+ // Call port driver to get adapter capabilities.
+ //
+
+ propertyId = StorageAdapterProperty;
+
+ status = ClassGetDescriptor(
+ commonExtension->LowerDeviceObject,
+ &propertyId,
+ &fdoExtension->AdapterDescriptor);
+
+ if(!NT_SUCCESS(status)) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
+ "[ADAPTER] failed %lx\n", status));
+ return status;
+ }
+
+ //
+ // Call port driver to get device descriptor.
+ //
+
+ propertyId = StorageDeviceProperty;
+
+ status = ClassGetDescriptor(
+ commonExtension->LowerDeviceObject,
+ &propertyId,
+ &fdoExtension->DeviceDescriptor);
+
+ if(!NT_SUCCESS(status)) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0, "ClassPnpStartDevice: ClassGetDescriptor "
+ "[DEVICE] failed %lx\n", status));
+ return status;
+ }
+
+ ClasspScanForSpecialInRegistry(fdoExtension);
+ ClassScanForSpecial(fdoExtension,
+ ClassBadItems,
+ ClasspScanForClassHacks);
+
+ //
+ // allow perf to be re-enabled after a given number of failed IOs
+ // require this number to be at least CLASS_PERF_RESTORE_MINIMUM
+ //
+
+ {
+ ULONG t = 0;
+ ClassGetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_PERF_RESTORE_VALUE_NAME,
+ &t);
+ if (t >= CLASS_PERF_RESTORE_MINIMUM) {
+ fdoExtension->PrivateFdoData->Perf.ReEnableThreshhold = t;
+ }
+ }
+
+
+ //
+ // compatibility comes first. writable cd media will not
+ // get a SYNCH_CACHE on power down.
+ //
+
+ if (fdoExtension->DeviceObject->DeviceType != FILE_DEVICE_DISK) {
+ SET_FLAG(fdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_NO_SYNC_CACHE);
+ }
+
+ //
+ // initialize the hotplug information only after the ScanForSpecial
+ // routines, as it relies upon the hack flags.
+ //
+
+ status = ClasspInitializeHotplugInfo(fdoExtension);
+
+ if (!NT_SUCCESS(status)) {
+ DebugPrint((1, "ClassPnpStartDevice: Could not initialize "
+ "hotplug information %lx\n", status));
+ return status;
+ }
+
+ /*
+ * Allocate/initialize TRANSFER_PACKETs and related resources.
+ */
+ status = InitializeTransferPackets(DeviceObject);
+ }
+
+ //
+ // ISSUE - drivers need to disable write caching on the media
+ // if hotplug and !useroverride. perhaps we should
+ // allow registration of a callback to enable/disable
+ // write cache instead.
+ //
+
+ if (NT_SUCCESS(status)){
+ status = devInfo->ClassInitDevice(DeviceObject);
+ }
+
+ }
+
+ if (!NT_SUCCESS(status)){
+
+ //
+ // Just bail out - the remove that comes down will clean up the
+ // initialized scraps.
+ //
+
+ return status;
+ } else {
+ commonExtension->IsInitialized = TRUE;
+
+ if (commonExtension->IsFdo) {
+ fdoExtension->PrivateFdoData->Perf.OriginalSrbFlags = fdoExtension->SrbFlags;
+ }
+
+ }
+
+ //
+ // If device requests autorun functionality or a once a second callback
+ // then enable the once per second timer.
+ //
+ // NOTE: This assumes that ClassInitializeMediaChangeDetection is always
+ // called in the context of the ClassInitDevice callback. If called
+ // after then this check will have already been made and the
+ // once a second timer will not have been enabled.
+ //
+ if ((isFdo) &&
+ ((initData->ClassTick != NULL) ||
+ (fdoExtension->MediaChangeDetectionInfo != NULL) ||
+ ((fdoExtension->FailurePredictionInfo != NULL) &&
+ (fdoExtension->FailurePredictionInfo->Method != FailurePredictionNone))))
+ {
+ ClasspEnableTimer(DeviceObject);
+ timerStarted = TRUE;
+ } else {
+ timerStarted = FALSE;
+ }
+
+ //
+ // NOTE: the timer looks at commonExtension->CurrentState now
+ // to prevent Media Change Notification code from running
+ // until the device is started, but allows the device
+ // specific tick handler to run. therefore it is imperative
+ // that commonExtension->CurrentState not be updated until
+ // the device specific startdevice handler has finished.
+ //
+
+ status = devInfo->ClassStartDevice(DeviceObject);
+
+ if(NT_SUCCESS(status)) {
+ commonExtension->CurrentState = IRP_MN_START_DEVICE;
+
+ if((isFdo) && (initData->ClassEnumerateDevice != NULL)) {
+ isMountedDevice = FALSE;
+ }
+
+ if((DeviceObject->DeviceType != FILE_DEVICE_DISK) &&
+ (DeviceObject->DeviceType != FILE_DEVICE_CD_ROM)) {
+
+ isMountedDevice = FALSE;
+ }
+
+
+ if(isMountedDevice) {
+ ClasspRegisterMountedDeviceInterface(DeviceObject);
+ }
+
+ if((commonExtension->IsFdo) &&
+ (devInfo->ClassWmiInfo.GuidRegInfo != NULL)) {
+
+ IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_REGISTER);
+ }
+ } else {
+
+ if (timerStarted) {
+ ClasspDisableTimer(DeviceObject);
+ }
+ }
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReadWrite()
+
+Routine Description:
+
+ This is the system entry point for read and write requests. The
+ device-specific handler is invoked to perform any validation necessary.
+
+ If the device object is a PDO (partition object) then the request will
+ simply be adjusted for Partition0 and issued to the lower device driver.
+
+ IF the device object is an FDO (paritition 0 object), the number of bytes
+ in the request are checked against the maximum byte counts that the adapter
+ supports and requests are broken up into
+ smaller sizes if necessary.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object for this request
+
+ Irp - IO request
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS ClassReadWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+ LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
+ ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
+ ULONG isRemoved;
+ NTSTATUS status;
+
+ /*
+ * Grab the remove lock. If we can't acquire it, bail out.
+ */
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+ if (isRemoved) {
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+ else if (TEST_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME) &&
+ (currentIrpStack->MinorFunction != CLASSP_VOLUME_VERIFY_CHECKED) &&
+ !TEST_FLAG(currentIrpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME)){
+
+ /*
+ * DO_VERIFY_VOLUME is set for the device object,
+ * but this request is not itself a verify request.
+ * So fail this request.
+ */
+ IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
+ Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
+ Irp->IoStatus.Information = 0;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, 0);
+ status = STATUS_VERIFY_REQUIRED;
+ }
+ else {
+
+ /*
+ * Since we've bypassed the verify-required tests we don't need to repeat
+ * them with this IRP - in particular we don't want to worry about
+ * hitting them at the partition 0 level if the request has come through
+ * a non-zero partition.
+ */
+ currentIrpStack->MinorFunction = CLASSP_VOLUME_VERIFY_CHECKED;
+
+ /*
+ * Call the miniport driver's pre-pass filter to check if we
+ * should continue with this transfer.
+ */
+ ASSERT(commonExtension->DevInfo->ClassReadWriteVerification);
+ status = commonExtension->DevInfo->ClassReadWriteVerification(DeviceObject, Irp);
+ if (!NT_SUCCESS(status)){
+ ASSERT(Irp->IoStatus.Status == status);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest (DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+ else if (status == STATUS_PENDING){
+ /*
+ * ClassReadWriteVerification queued this request.
+ * So don't touch the irp anymore.
+ */
+ }
+ else {
+
+ if (transferByteCount == 0) {
+ /*
+ * Several parts of the code turn 0 into 0xffffffff,
+ * so don't process a zero-length request any further.
+ */
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_SUCCESS;
+ }
+ else {
+ /*
+ * If the driver has its own StartIo routine, call it.
+ */
+ if (commonExtension->DriverExtension->InitData.ClassStartIo) {
+ IoMarkIrpPending(Irp);
+ IoStartPacket(DeviceObject, Irp, NULL, NULL);
+ status = STATUS_PENDING;
+ }
+ else {
+ /*
+ * The driver does not have its own StartIo routine.
+ * So process this request ourselves.
+ */
+
+ /*
+ * Add partition byte offset to make starting byte relative to
+ * beginning of disk.
+ */
+ currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
+ commonExtension->StartingOffset.QuadPart;
+
+ if (commonExtension->IsFdo){
+
+ /*
+ * Add in any skew for the disk manager software.
+ */
+ currentIrpStack->Parameters.Read.ByteOffset.QuadPart +=
+ commonExtension->PartitionZeroExtension->DMByteSkew;
+
+ /*
+ * Perform the actual transfer(s) on the hardware
+ * to service this request.
+ */
+ ServiceTransferRequest(DeviceObject, Irp);
+ status = STATUS_PENDING;
+ }
+ else {
+ /*
+ * This is a child PDO enumerated for our FDO by e.g. disk.sys
+ * and owned by e.g. partmgr. Send it down to the next device
+ * and the same irp will come back to us for the FDO.
+ */
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(lowerDeviceObject, Irp);
+ }
+ }
+ }
+ }
+ }
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReadDriveCapacity()
+
+Routine Description:
+
+ This routine sends a READ CAPACITY to the requested device, updates
+ the geometry information in the device object and returns
+ when it is complete. This routine is synchronous.
+
+ This routine must be called with the remove lock held or some other
+ assurance that the Fdo will not be removed while processing.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object that represents
+ the device whose capacity is to be read.
+
+Return Value:
+
+ Status is returned.
+
+--*/
+NTSTATUS ClassReadDriveCapacity(IN PDEVICE_OBJECT Fdo)
+{
+ READ_CAPACITY_DATA readCapacityBuffer = {0};
+ NTSTATUS status;
+ PMDL driveCapMdl;
+
+ driveCapMdl = BuildDeviceInputMdl(&readCapacityBuffer, sizeof(READ_CAPACITY_DATA));
+ if (driveCapMdl){
+
+ TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ KEVENT event;
+ NTSTATUS pktStatus;
+ IRP pseudoIrp = {0};
+
+ /*
+ * Our engine needs an "original irp" to write the status back to
+ * and to count down packets (one in this case).
+ * Just use a pretend irp for this.
+ */
+ pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp.IoStatus.Information = 0;
+ pseudoIrp.MdlAddress = driveCapMdl;
+
+ /*
+ * Set this up as a SYNCHRONOUS transfer, submit it,
+ * and wait for the packet to complete. The result
+ * status will be written to the original irp.
+ */
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupDriveCapacityTransferPacket( pkt,
+ &readCapacityBuffer,
+ sizeof(READ_CAPACITY_DATA),
+ &event,
+ &pseudoIrp);
+ SubmitTransferPacket(pkt);
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ status = pseudoIrp.IoStatus.Status;
+
+ /*
+ * If we got an UNDERRUN, retry exactly once.
+ * (The transfer_packet engine didn't retry because the result
+ * status was success).
+ */
+ if (NT_SUCCESS(status) &&
+ (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA))){
+ DBGERR(("ClassReadDriveCapacity: read len (%xh) < %xh, retrying ...", (ULONG)pseudoIrp.IoStatus.Information, sizeof(READ_CAPACITY_DATA)));
+
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp.IoStatus.Information = 0;
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupDriveCapacityTransferPacket( pkt,
+ &readCapacityBuffer,
+ sizeof(READ_CAPACITY_DATA),
+ &event,
+ &pseudoIrp);
+ SubmitTransferPacket(pkt);
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = pseudoIrp.IoStatus.Status;
+ if (pseudoIrp.IoStatus.Information < sizeof(READ_CAPACITY_DATA)){
+ status = STATUS_DEVICE_BUSY;
+ }
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+
+
+ if (NT_SUCCESS(status)){
+ /*
+ * The request succeeded.
+ * Read out and store the drive information.
+ */
+ ULONG cylinderSize;
+ ULONG bytesPerSector;
+ ULONG tmp;
+ ULONG lastSector;
+
+ /*
+ * Read the bytesPerSector value,
+ * which is big-endian in the returned buffer.
+ * Default to the standard 512 bytes.
+ */
+ tmp = readCapacityBuffer.BytesPerBlock;
+ ((PFOUR_BYTE)&bytesPerSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
+ ((PFOUR_BYTE)&bytesPerSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
+ ((PFOUR_BYTE)&bytesPerSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
+ ((PFOUR_BYTE)&bytesPerSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
+ if (bytesPerSector == 0) {
+ bytesPerSector = 512;
+ }
+ else {
+ /*
+ * Clear all but the highest set bit.
+ * That will give us a bytesPerSector value that is a power of 2.
+ */
+ while (bytesPerSector & (bytesPerSector-1)) {
+ bytesPerSector &= bytesPerSector-1;
+ }
+ }
+ fdoExt->DiskGeometry.BytesPerSector = bytesPerSector;
+
+ //
+ // Copy last sector in reverse byte order.
+ //
+
+ tmp = readCapacityBuffer.LogicalBlockAddress;
+ ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3;
+ ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2;
+ ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1;
+ ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
+
+ //
+ // Calculate sector to byte shift.
+ //
+
+ WHICH_BIT(fdoExt->DiskGeometry.BytesPerSector, fdoExt->SectorShift);
+
+ DebugPrint((2,"SCSI ClassReadDriveCapacity: Sector size is %d\n",
+ fdoExt->DiskGeometry.BytesPerSector));
+
+ DebugPrint((2,"SCSI ClassReadDriveCapacity: Number of Sectors is %d\n",
+ lastSector + 1));
+
+ if (fdoExt->DMActive){
+ DebugPrint((1, "SCSI ClassReadDriveCapacity: reducing number of sectors by %d\n",
+ fdoExt->DMSkew));
+ lastSector -= fdoExt->DMSkew;
+ }
+
+ /*
+ * Check to see if we have a geometry we should be using already.
+ */
+ cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
+ fdoExt->DiskGeometry.SectorsPerTrack);
+ if (cylinderSize == 0){
+ DebugPrint((1, "ClassReadDriveCapacity: resetting H & S geometry "
+ "values from %#x/%#x to %#x/%#x\n",
+ fdoExt->DiskGeometry.TracksPerCylinder,
+ fdoExt->DiskGeometry.SectorsPerTrack,
+ 0xff,
+ 0x3f));
+
+ fdoExt->DiskGeometry.TracksPerCylinder = 0xff;
+ fdoExt->DiskGeometry.SectorsPerTrack = 0x3f;
+
+
+ cylinderSize = (fdoExt->DiskGeometry.TracksPerCylinder *
+ fdoExt->DiskGeometry.SectorsPerTrack);
+ }
+
+ //
+ // Calculate number of cylinders.
+ //
+
+ fdoExt->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/cylinderSize);
+
+ //
+ // if there are zero cylinders, then the device lied AND it's
+ // smaller than 0xff*0x3f (about 16k sectors, usually 8 meg)
+ // this can fit into a single LONGLONG, so create another usable
+ // geometry, even if it's unusual looking. This allows small,
+ // non-standard devices, such as Sony's Memory Stick, to show
+ // up as having a partition.
+ //
+
+ if (fdoExt->DiskGeometry.Cylinders.QuadPart == (LONGLONG)0) {
+ fdoExt->DiskGeometry.SectorsPerTrack = 1;
+ fdoExt->DiskGeometry.TracksPerCylinder = 1;
+ fdoExt->DiskGeometry.Cylinders.QuadPart = lastSector;
+ }
+
+
+ //
+ // Calculate media capacity in bytes.
+ //
+
+ fdoExt->CommonExtension.PartitionLength.QuadPart =
+ ((LONGLONG)(lastSector + 1)) << fdoExt->SectorShift;
+
+ /*
+ * Is this removable or fixed media
+ */
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
+ fdoExt->DiskGeometry.MediaType = RemovableMedia;
+ }
+ else {
+ fdoExt->DiskGeometry.MediaType = FixedMedia;
+ }
+ }
+ else {
+ /*
+ * The request failed.
+ */
+
+ //
+ // ISSUE - 2000/02/04 - henrygab - non-512-byte sector sizes and failed geometry update
+ // what happens when the disk's sector size is bigger than
+ // 512 bytes and we hit this code path? this is untested.
+ //
+ // If the read capacity fails, set the geometry to reasonable parameter
+ // so things don't fail at unexpected places. Zero the geometry
+ // except for the bytes per sector and sector shift.
+ //
+
+ /*
+ * This request can sometimes fail legitimately
+ * (e.g. when a SCSI device is attached but turned off)
+ * so this is not necessarily a device/driver bug.
+ */
+ DBGTRACE(ClassDebugWarning, ("ClassReadDriveCapacity on Fdo %xh failed with status %xh.", Fdo, status));
+
+ /*
+ * Write in a default disk geometry which we HOPE is right (??).
+ * BUGBUG !!
+ */
+ RtlZeroMemory(&fdoExt->DiskGeometry, sizeof(DISK_GEOMETRY));
+ fdoExt->DiskGeometry.BytesPerSector = 512;
+ fdoExt->SectorShift = 9;
+ fdoExt->CommonExtension.PartitionLength.QuadPart = (LONGLONG) 0;
+
+ /*
+ * Is this removable or fixed media
+ */
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
+ fdoExt->DiskGeometry.MediaType = RemovableMedia;
+ }
+ else {
+ fdoExt->DiskGeometry.MediaType = FixedMedia;
+ }
+ }
+
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ FreeDeviceInputMdl(driveCapMdl);
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendStartUnit()
+
+Routine Description:
+
+ Send command to SCSI unit to start or power up.
+ Because this command is issued asynchronounsly, that is, without
+ waiting on it to complete, the IMMEDIATE flag is not set. This
+ means that the CDB will not return until the drive has powered up.
+ This should keep subsequent requests from being submitted to the
+ device before it has completely spun up.
+
+ This routine is called from the InterpretSense routine, when a
+ request sense returns data indicating that a drive must be
+ powered up.
+
+ This routine may also be called from a class driver's error handler,
+ or anytime a non-critical start device should be sent to the device.
+
+Arguments:
+
+ Fdo - The functional device object for the stopped device.
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassSendStartUnit(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PIO_STACK_LOCATION irpStack;
+ PIRP irp;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PSCSI_REQUEST_BLOCK srb;
+ PCOMPLETION_CONTEXT context;
+ PCDB cdb;
+
+ //
+ // Allocate Srb from nonpaged pool.
+ //
+
+ context = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(COMPLETION_CONTEXT),
+ '6CcS');
+
+ if(context == NULL) {
+
+ //
+ // ISSUE-2000/02/03-peterwie
+ // This code path was inheritted from the NT 4.0 class2.sys driver.
+ // It needs to be changed to survive low-memory conditions.
+ //
+
+ KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
+ }
+
+ //
+ // Save the device object in the context for use by the completion
+ // routine.
+ //
+
+ context->DeviceObject = Fdo;
+ srb = &context->Srb;
+
+ //
+ // Zero out srb.
+ //
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ //
+ // Write length to SRB.
+ //
+
+ srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // Set timeout value large enough for drive to spin up.
+ //
+
+ srb->TimeOutValue = START_UNIT_TIMEOUT;
+
+ //
+ // Set the transfer length.
+ //
+
+ srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
+
+ //
+ // Build the start unit CDB.
+ //
+
+ srb->CdbLength = 6;
+ cdb = (PCDB)srb->Cdb;
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.Start = 1;
+ cdb->START_STOP.Immediate = 0;
+ cdb->START_STOP.LogicalUnitNumber = srb->Lun;
+
+ //
+ // Build the asynchronous request to be sent to the port driver.
+ // Since this routine is called from a DPC the IRP should always be
+ // available.
+ //
+
+ irp = IoAllocateIrp(Fdo->StackSize, FALSE);
+
+ if(irp == NULL) {
+
+ //
+ // ISSUE-2000/02/03-peterwie
+ // This code path was inheritted from the NT 4.0 class2.sys driver.
+ // It needs to be changed to survive low-memory conditions.
+ //
+
+ KeBugCheck(SCSI_DISK_DRIVER_INTERNAL);
+
+ }
+
+ ClassAcquireRemoveLock(Fdo, irp);
+
+ IoSetCompletionRoutine(irp,
+ (PIO_COMPLETION_ROUTINE)ClassAsynchronousCompletion,
+ context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+ srb->OriginalRequest = irp;
+
+ //
+ // Store the SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = srb;
+
+ //
+ // Call the port driver with the IRP.
+ //
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ return;
+
+} // end StartUnit()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAsynchronousCompletion() ISSUE-2000/02/18-henrygab - why public?!
+
+Routine Description:
+
+ This routine is called when an asynchronous I/O request
+ which was issused by the class driver completes. Examples of such requests
+ are release queue or START UNIT. This routine releases the queue if
+ necessary. It then frees the context and the IRP.
+
+Arguments:
+
+ DeviceObject - The device object for the logical unit; however since this
+ is the top stack location the value is NULL.
+
+ Irp - Supplies a pointer to the Irp to be processed.
+
+ Context - Supplies the context to be used to process this request.
+
+Return Value:
+
+ None.
+
+--*/
+NTSTATUS
+ClassAsynchronousCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+{
+ PCOMPLETION_CONTEXT context = Context;
+ PSCSI_REQUEST_BLOCK srb;
+
+ if(DeviceObject == NULL) {
+
+ DeviceObject = context->DeviceObject;
+ }
+
+ srb = &context->Srb;
+
+ //
+ // If this is an execute srb, then check the return status and make sure.
+ // the queue is not frozen.
+ //
+
+ if (srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
+
+ //
+ // Check for a frozen queue.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+
+ //
+ // Unfreeze the queue getting the device object from the context.
+ //
+
+ ClassReleaseQueue(context->DeviceObject);
+ }
+ }
+
+ { // free port-allocated sense buffer if we can detect
+
+ if (((PCOMMON_DEVICE_EXTENSION)(DeviceObject->DeviceExtension))->IsFdo) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ } else {
+
+ ASSERT(!TEST_FLAG(srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ }
+ }
+
+
+ //
+ // Free the context and the Irp.
+ //
+
+ if (Irp->MdlAddress != NULL) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+
+ Irp->MdlAddress = NULL;
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ ExFreePool(context);
+ IoFreeIrp(Irp);
+
+ //
+ // Indicate the I/O system should stop processing the Irp completion.
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // end ClassAsynchronousCompletion()
+
+
+
+VOID ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp)
+{
+ PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
+ PIO_STACK_LOCATION currentSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG entireXferLen = currentSp->Parameters.Read.Length;
+ PUCHAR bufPtr = MmGetMdlVirtualAddress(Irp->MdlAddress);
+ LARGE_INTEGER targetLocation = currentSp->Parameters.Read.ByteOffset;
+ PTRANSFER_PACKET pkt;
+ SINGLE_LIST_ENTRY pktList;
+ PSINGLE_LIST_ENTRY slistEntry;
+ ULONG numPackets;
+ KIRQL oldIrql;
+ ULONG i;
+
+ /*
+ * Compute the number of hw xfers we'll have to do.
+ * Calculate this without allowing for an overflow condition.
+ */
+ ASSERT(fdoData->HwMaxXferLen >= PAGE_SIZE);
+ numPackets = entireXferLen/fdoData->HwMaxXferLen;
+ if (entireXferLen % fdoData->HwMaxXferLen){
+ numPackets++;
+ }
+
+ /*
+ * First get all the TRANSFER_PACKETs that we'll need at once.
+ * Use our 'simple' slist functions since we don't need interlocked.
+ */
+ SimpleInitSlistHdr(&pktList);
+ for (i = 0; i < numPackets; i++){
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ SimplePushSlist(&pktList, &pkt->SlistEntry);
+ }
+ else {
+ break;
+ }
+ }
+
+ if (i == numPackets){
+ /*
+ * Initialize the original IRP's status to success.
+ * If any of the packets fail, they will set it to an error status.
+ * The IoStatus.Information field will be incremented to the
+ * transfer length as the pieces complete.
+ */
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+
+ /*
+ * Store the number of transfer pieces inside the original IRP.
+ * It will be used to count down the pieces as they complete.
+ */
+ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(numPackets);
+
+ /*
+ * We are proceeding with the transfer.
+ * Mark the client IRP pending since it may complete on a different thread.
+ */
+ IoMarkIrpPending(Irp);
+
+ /*
+ * Transmit the pieces of the transfer.
+ */
+ while (entireXferLen > 0){
+ ULONG thisPieceLen = MIN(fdoData->HwMaxXferLen, entireXferLen);
+
+ /*
+ * Set up a TRANSFER_PACKET for this piece and send it.
+ */
+ slistEntry = SimplePopSlist(&pktList);
+ ASSERT(slistEntry);
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ SetupReadWriteTransferPacket( pkt,
+ bufPtr,
+ thisPieceLen,
+ targetLocation,
+ Irp);
+ SubmitTransferPacket(pkt);
+
+ entireXferLen -= thisPieceLen;
+ bufPtr += thisPieceLen;
+ targetLocation.QuadPart += thisPieceLen;
+ }
+ ASSERT(SimpleIsSlistEmpty(&pktList));
+ }
+ else if (i >= 1){
+ /*
+ * We were unable to get all the TRANSFER_PACKETs we need,
+ * but we did get at least one.
+ * That means that we are in extreme low-memory stress.
+ * We'll try doing this transfer using a single packet.
+ * The port driver is certainly also in stress, so use one-page
+ * transfers.
+ */
+
+ /*
+ * Free all but one of the TRANSFER_PACKETs.
+ */
+ while (i-- > 1){
+ slistEntry = SimplePopSlist(&pktList);
+ ASSERT(slistEntry);
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ EnqueueFreeTransferPacket(Fdo, pkt);
+ }
+
+ /*
+ * Get the single TRANSFER_PACKET that we'll be using.
+ */
+ slistEntry = SimplePopSlist(&pktList);
+ ASSERT(slistEntry);
+ ASSERT(SimpleIsSlistEmpty(&pktList));
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ DBGWARN(("Insufficient packets available in ServiceTransferRequest - entering lowMemRetry with pkt=%xh.", pkt));
+
+ /*
+ * Set default status and the number of transfer packets (one)
+ * inside the original irp.
+ */
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+
+ /*
+ * Mark the client irp pending since it may complete on
+ * another thread.
+ */
+ IoMarkIrpPending(Irp);
+
+ /*
+ * Set up the TRANSFER_PACKET for a lowMem transfer and launch.
+ */
+ SetupReadWriteTransferPacket( pkt,
+ bufPtr,
+ entireXferLen,
+ targetLocation,
+ Irp);
+ InitLowMemRetry(pkt, bufPtr, entireXferLen, targetLocation);
+ StepLowMemRetry(pkt);
+ }
+ else {
+ /*
+ * We were unable to get ANY TRANSFER_PACKETs.
+ * Defer this client irp until some TRANSFER_PACKETs free up.
+ */
+ DBGWARN(("No packets available in ServiceTransferRequest - deferring transfer (Irp=%xh)...", Irp));
+ IoMarkIrpPending(Irp);
+ EnqueueDeferredClientIrp(fdoData, Irp);
+ }
+
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassIoComplete()
+
+Routine Description:
+
+ This routine executes when the port driver has completed a request.
+ It looks at the SRB status in the completing SRB and if not success
+ it checks for valid request sense buffer information. If valid, the
+ info is used to update status with more precise message of type of
+ error. This routine deallocates the SRB.
+
+ This routine should only be placed on the stack location for a class
+ driver FDO.
+
+Arguments:
+
+ Fdo - Supplies the device object which represents the logical
+ unit.
+
+ Irp - Supplies the Irp which has completed.
+
+ Context - Supplies a pointer to the SRB.
+
+Return Value:
+
+ NT status
+
+--*/
+NTSTATUS
+ClassIoComplete(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = Context;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ NTSTATUS status;
+ BOOLEAN retry;
+ BOOLEAN callStartNextPacket;
+
+ ASSERT(fdoExtension->CommonExtension.IsFdo);
+
+ //
+ // Check SRB status for success of completing request.
+ //
+
+ if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+ ULONG retryInterval;
+
+ DebugPrint((2, "ClassIoComplete: IRP %p, SRB %p\n", Irp, srb));
+
+ //
+ // Release the queue if it is frozen.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+ ClassReleaseQueue(Fdo);
+ }
+
+ retry = ClassInterpretSenseInfo(
+ Fdo,
+ srb,
+ irpStack->MajorFunction,
+ irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
+ irpStack->Parameters.DeviceIoControl.IoControlCode :
+ 0,
+ MAXIMUM_RETRIES -
+ ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
+ &status,
+ &retryInterval);
+
+ //
+ // If the status is verified required and the this request
+ // should bypass verify required then retry the request.
+ //
+
+ if (TEST_FLAG(irpStack->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ status == STATUS_VERIFY_REQUIRED) {
+
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ if (retry && (irpStack->Parameters.Others.Argument4--)) {
+
+ //
+ // Retry request.
+ //
+
+ DebugPrint((1, "Retry request %p\n", Irp));
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ RetryRequest(Fdo, Irp, srb, FALSE, retryInterval);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ } else {
+
+ //
+ // Set status for successful request
+ //
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+ ClasspPerfIncrementSuccessfulIo(fdoExtension);
+ status = STATUS_SUCCESS;
+ } // end if (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)
+
+
+ //
+ // ensure we have returned some info, and it matches what the
+ // original request wanted for PAGING operations only
+ //
+
+ if ((NT_SUCCESS(status)) && TEST_FLAG(Irp->Flags, IRP_PAGING_IO)) {
+ ASSERT(Irp->IoStatus.Information != 0);
+ ASSERT(irpStack->Parameters.Read.Length == Irp->IoStatus.Information);
+ }
+
+ //
+ // remember if the caller wanted to skip calling IoStartNextPacket.
+ // for legacy reasons, we cannot call IoStartNextPacket for IoDeviceControl
+ // calls. this setting only affects device objects with StartIo routines.
+ //
+
+ callStartNextPacket = !TEST_FLAG(srb->SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET);
+ if (irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
+ callStartNextPacket = FALSE;
+ }
+
+ //
+ // Free the srb
+ //
+
+ if(!TEST_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_PERSISTANT)) {
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ if (fdoExtension->CommonExtension.IsSrbLookasideListInitialized){
+ ClassFreeOrReuseSrb(fdoExtension, srb);
+ }
+ else {
+ DBGWARN(("ClassIoComplete is freeing an SRB (possibly) on behalf of another driver."));
+ ExFreePool(srb);
+ }
+
+ } else {
+
+ DebugPrint((2, "ClassIoComplete: Not Freeing srb @ %p because "
+ "SRB_CLASS_FLAGS_PERSISTANT set\n", srb));
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ DebugPrint((2, "ClassIoComplete: Not Freeing sensebuffer @ %p "
+ " because SRB_CLASS_FLAGS_PERSISTANT set\n",
+ srb->SenseInfoBuffer));
+ }
+
+ }
+
+ //
+ // Set status in completing IRP.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ //
+ // Set the hard error if necessary.
+ //
+
+ if (!NT_SUCCESS(status) &&
+ IoIsErrorUserInduced(status) &&
+ (Irp->Tail.Overlay.Thread != NULL)
+ ) {
+
+ //
+ // Store DeviceObject for filesystem, and clear
+ // in IoStatus.Information field.
+ //
+
+ IoSetHardErrorOrVerifyDevice(Irp, Fdo);
+ Irp->IoStatus.Information = 0;
+ }
+
+ //
+ // If pending has be returned for this irp then mark the current stack as
+ // pending.
+ //
+
+ if (Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
+ if (callStartNextPacket) {
+ KIRQL oldIrql;
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+ IoStartNextPacket(Fdo, FALSE);
+ KeLowerIrql(oldIrql);
+ }
+ }
+
+ ClassReleaseRemoveLock(Fdo, Irp);
+
+ return status;
+
+} // end ClassIoComplete()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendSrbSynchronous()
+
+Routine Description:
+
+ This routine is called by SCSI device controls to complete an
+ SRB and send it to the port driver synchronously (ie wait for
+ completion). The CDB is already completed along with the SRB CDB
+ size and request timeout value.
+
+Arguments:
+
+ Fdo - Supplies the functional device object which represents the target.
+
+ Srb - Supplies a partially initialized SRB. The SRB cannot come from zone.
+
+ BufferAddress - Supplies the address of the buffer.
+
+ BufferLength - Supplies the length in bytes of the buffer.
+
+ WriteToDevice - Indicates the data should be transfer to the device.
+
+Return Value:
+
+ NTSTATUS indicating the final results of the operation.
+
+ If NT_SUCCESS(), then the amount of usable data is contained in the field
+ Srb->DataTransferLength
+
+--*/
+NTSTATUS
+ClassSendSrbSynchronous(
+ PDEVICE_OBJECT Fdo,
+ PSCSI_REQUEST_BLOCK Srb,
+ PVOID BufferAddress,
+ ULONG BufferLength,
+ BOOLEAN WriteToDevice
+ )
+{
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+ IO_STATUS_BLOCK ioStatus;
+ ULONG controlType;
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+ KEVENT event;
+ PUCHAR senseInfoBuffer;
+ ULONG retryCount = MAXIMUM_RETRIES;
+ NTSTATUS status;
+ BOOLEAN retry;
+
+ //
+ // NOTE: This code is only pagable because we are not freezing
+ // the queue. Allowing the queue to be frozen from a pagable
+ // routine could leave the queue frozen as we try to page in
+ // the code to unfreeze the queue. The result would be a nice
+ // case of deadlock. Therefore, since we are unfreezing the
+ // queue regardless of the result, just set the NO_FREEZE_QUEUE
+ // flag in the SRB.
+ //
+
+ ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
+ ASSERT(fdoExtension->CommonExtension.IsFdo);
+
+ //
+ // Write length to SRB.
+ //
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ //
+ // Set SCSI bus address.
+ //
+
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // Enable auto request sense.
+ //
+
+ Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+ //
+ // Sense buffer is in aligned nonpaged pool.
+ //
+ //
+ senseInfoBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ SENSE_BUFFER_SIZE,
+ '7CcS');
+
+ if (senseInfoBuffer == NULL) {
+
+ DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate request sense "
+ "buffer\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ Srb->SenseInfoBuffer = senseInfoBuffer;
+ Srb->DataBuffer = BufferAddress;
+
+ //
+ // Start retries here.
+ //
+
+retry:
+
+ //
+ // use fdoextension's flags by default.
+ // do not move out of loop, as the flag may change due to errors
+ // sending this command.
+ //
+
+ Srb->SrbFlags = fdoExtension->SrbFlags;
+
+ if(BufferAddress != NULL) {
+ if(WriteToDevice) {
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
+ } else {
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
+ }
+ }
+
+ //
+ // Initialize the QueueAction field.
+ //
+
+ Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+
+ //
+ // Disable synchronous transfer for these requests.
+ // Disable freezing the queue, since all we do is unfreeze it anyways.
+ //
+
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ //
+ // Set the event object to the unsignaled state.
+ // It will be used to signal request completion.
+ //
+
+ KeInitializeEvent(&event, NotificationEvent, FALSE);
+
+ //
+ // Build device I/O control request with METHOD_NEITHER data transfer.
+ // We'll queue a completion routine to cleanup the MDL's and such ourself.
+ //
+
+ irp = IoAllocateIrp(
+ (CCHAR) (fdoExtension->CommonExtension.LowerDeviceObject->StackSize + 1),
+ FALSE);
+
+ if(irp == NULL) {
+ ExFreePool(senseInfoBuffer);
+ DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate Irp\n"));
+ return(STATUS_INSUFFICIENT_RESOURCES);
+ }
+
+ //
+ // Get next stack location.
+ //
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Set up SRB for execute scsi request. Save SRB address in next stack
+ // for the port driver.
+ //
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+ irpStack->Parameters.Scsi.Srb = Srb;
+
+ IoSetCompletionRoutine(irp,
+ ClasspSendSynchronousCompletion,
+ Srb,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ irp->UserIosb = &ioStatus;
+ irp->UserEvent = &event;
+
+ if(BufferAddress) {
+ //
+ // Build an MDL for the data buffer and stick it into the irp. The
+ // completion routine will unlock the pages and free the MDL.
+ //
+
+ irp->MdlAddress = IoAllocateMdl( BufferAddress,
+ BufferLength,
+ FALSE,
+ FALSE,
+ irp );
+ if (irp->MdlAddress == NULL) {
+ ExFreePool(senseInfoBuffer);
+ Srb->SenseInfoBuffer = NULL;
+ IoFreeIrp( irp );
+ DebugPrint((1, "ClassSendSrbSynchronous: Can't allocate MDL\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ _SEH2_TRY {
+
+ //
+ // the io manager unlocks these pages upon completion
+ //
+
+ MmProbeAndLockPages( irp->MdlAddress,
+ KernelMode,
+ (WriteToDevice ? IoReadAccess :
+ IoWriteAccess));
+
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ status = _SEH2_GetExceptionCode();
+
+ ExFreePool(senseInfoBuffer);
+ Srb->SenseInfoBuffer = NULL;
+ IoFreeMdl(irp->MdlAddress);
+ IoFreeIrp(irp);
+
+ DebugPrint((1, "ClassSendSrbSynchronous: Exception %lx "
+ "locking buffer\n", status));
+ return status;
+ } _SEH2_END;
+ }
+
+ //
+ // Set the transfer length.
+ //
+
+ Srb->DataTransferLength = BufferLength;
+
+ //
+ // Zero out status.
+ //
+
+ Srb->ScsiStatus = Srb->SrbStatus = 0;
+ Srb->NextSrb = 0;
+
+ //
+ // Set up IRP Address.
+ //
+
+ Srb->OriginalRequest = irp;
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
+
+ if (status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ //
+ // Check that request completed without error.
+ //
+
+ if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ ULONG retryInterval;
+
+ DBGTRACE(ClassDebugWarning, ("ClassSendSrbSynchronous - srb %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)", Srb, DBGGETSCSIOPSTR(Srb), DBGGETSRBSTATUSSTR(Srb), (ULONG)Srb->SrbStatus, status, DBGGETSENSECODESTR(Srb), DBGGETADSENSECODESTR(Srb), DBGGETADSENSEQUALIFIERSTR(Srb)));
+
+ //
+ // assert that the queue is not frozen
+ //
+
+ ASSERT(!TEST_FLAG(Srb->SrbStatus, SRB_STATUS_QUEUE_FROZEN));
+
+ //
+ // Update status and determine if request should be retried.
+ //
+
+ retry = ClassInterpretSenseInfo(Fdo,
+ Srb,
+ IRP_MJ_SCSI,
+ 0,
+ MAXIMUM_RETRIES - retryCount,
+ &status,
+ &retryInterval);
+
+
+ if (retry) {
+
+ if ((status == STATUS_DEVICE_NOT_READY &&
+ ((PSENSE_DATA) senseInfoBuffer)->AdditionalSenseCode ==
+ SCSI_ADSENSE_LUN_NOT_READY) ||
+ (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)) {
+
+ LARGE_INTEGER delay;
+
+ //
+ // Delay for at least 2 seconds.
+ //
+
+ if(retryInterval < 2) {
+ retryInterval = 2;
+ }
+
+ delay.QuadPart = (LONGLONG)( - 10 * 1000 * (LONGLONG)1000 * retryInterval);
+
+ //
+ // Stall for a while to let the device become ready
+ //
+
+ KeDelayExecutionThread(KernelMode, FALSE, &delay);
+
+ }
+
+ //
+ // If retries are not exhausted then retry this operation.
+ //
+
+ if (retryCount--) {
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
+ }
+
+ goto retry;
+ }
+ }
+
+ } else {
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+ status = STATUS_SUCCESS;
+ }
+
+ //
+ // required even though we allocated our own, since the port driver may
+ // have allocated one also
+ //
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
+ }
+
+ Srb->SenseInfoBuffer = NULL;
+ ExFreePool(senseInfoBuffer);
+
+ return status;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInterpretSenseInfo()
+
+Routine Description:
+
+ This routine interprets the data returned from the SCSI
+ request sense. It determines the status to return in the
+ IRP and whether this request can be retried.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Srb - Supplies the scsi request block which failed.
+
+ MajorFunctionCode - Supplies the function code to be used for logging.
+
+ IoDeviceCode - Supplies the device code to be used for logging.
+
+ Status - Returns the status for the request.
+
+Return Value:
+
+ BOOLEAN TRUE: Drivers should retry this request.
+ FALSE: Drivers should not retry this request.
+
+--*/
+BOOLEAN
+ClassInterpretSenseInfo(
+ IN PDEVICE_OBJECT Fdo,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN UCHAR MajorFunctionCode,
+ IN ULONG IoDeviceCode,
+ IN ULONG RetryCount,
+ OUT NTSTATUS *Status,
+ OUT OPTIONAL ULONG *RetryInterval
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+
+ PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
+
+ BOOLEAN retry = TRUE;
+ BOOLEAN logError = FALSE;
+ BOOLEAN unhandledError = FALSE;
+ BOOLEAN incrementErrorCount = FALSE;
+
+ ULONG badSector = 0;
+ ULONG uniqueId = 0;
+
+ NTSTATUS logStatus;
+
+ ULONG readSector;
+ ULONG index;
+
+ ULONG retryInterval = 0;
+ KIRQL oldIrql;
+
+
+ logStatus = -1;
+
+ if(TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
+
+ //
+ // Log anything remotely incorrect about paging i/o
+ //
+
+ logError = TRUE;
+ uniqueId = 301;
+ logStatus = IO_WARNING_PAGING_FAILURE;
+ }
+
+ //
+ // Check that request sense buffer is valid.
+ //
+
+ ASSERT(fdoExtension->CommonExtension.IsFdo);
+
+
+ //
+ // must handle the SRB_STATUS_INTERNAL_ERROR case first,
+ // as it has all the flags set.
+ //
+
+ if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_INTERNAL_ERROR) {
+
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: Internal Error code is %x\n",
+ Srb->InternalStatus));
+
+ retry = FALSE;
+ *Status = Srb->InternalStatus;
+
+ } else if ((Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) &&
+ (Srb->SenseInfoBufferLength >=
+ offsetof(SENSE_DATA, CommandSpecificInformation))) {
+
+ //
+ // Zero the additional sense code and additional sense code qualifier
+ // if they were not returned by the device.
+ //
+
+ readSector = senseBuffer->AdditionalSenseLength +
+ offsetof(SENSE_DATA, AdditionalSenseLength);
+
+ if (readSector > Srb->SenseInfoBufferLength) {
+ readSector = Srb->SenseInfoBufferLength;
+ }
+
+ if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCode)) {
+ senseBuffer->AdditionalSenseCode = 0;
+ }
+
+ if (readSector <= offsetof(SENSE_DATA, AdditionalSenseCodeQualifier)) {
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+ }
+
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: Error code is %x\n",
+ senseBuffer->ErrorCode));
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: Sense key is %x\n",
+ senseBuffer->SenseKey));
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: Additional sense code is %x\n",
+ senseBuffer->AdditionalSenseCode));
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: Additional sense code qualifier "
+ "is %x\n",
+ senseBuffer->AdditionalSenseCodeQualifier));
+
+
+ switch (senseBuffer->SenseKey & 0xf) {
+
+ case SCSI_SENSE_NOT_READY: {
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Device not ready\n"));
+ *Status = STATUS_DEVICE_NOT_READY;
+
+ switch (senseBuffer->AdditionalSenseCode) {
+
+ case SCSI_ADSENSE_LUN_NOT_READY: {
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Lun not ready\n"));
+
+ switch (senseBuffer->AdditionalSenseCodeQualifier) {
+
+ case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
+ DEVICE_EVENT_BECOMING_READY notReady;
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Operation In Progress\n"));
+ retryInterval = NOT_READY_RETRY_INTERVAL;
+
+ RtlZeroMemory(¬Ready, sizeof(DEVICE_EVENT_BECOMING_READY));
+ notReady.Version = 1;
+ notReady.Reason = 2;
+ notReady.Estimated100msToReady = retryInterval * 10;
+ ClasspSendNotification(fdoExtension,
+ &GUID_IO_DEVICE_BECOMING_READY,
+ sizeof(DEVICE_EVENT_BECOMING_READY),
+ ¬Ready);
+
+ break;
+ }
+
+ case SCSI_SENSEQ_BECOMING_READY: {
+ DEVICE_EVENT_BECOMING_READY notReady;
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "In process of becoming ready\n"));
+ retryInterval = NOT_READY_RETRY_INTERVAL;
+
+ RtlZeroMemory(¬Ready, sizeof(DEVICE_EVENT_BECOMING_READY));
+ notReady.Version = 1;
+ notReady.Reason = 1;
+ notReady.Estimated100msToReady = retryInterval * 10;
+ ClasspSendNotification(fdoExtension,
+ &GUID_IO_DEVICE_BECOMING_READY,
+ sizeof(DEVICE_EVENT_BECOMING_READY),
+ ¬Ready);
+ break;
+ }
+
+ case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Long write in progress\n"));
+ retry = FALSE;
+ break;
+ }
+
+ case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Manual intervention required\n"));
+ *Status = STATUS_NO_MEDIA_IN_DEVICE;
+ retry = FALSE;
+ break;
+ }
+
+ case SCSI_SENSEQ_FORMAT_IN_PROGRESS: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Format in progress\n"));
+ retry = FALSE;
+ break;
+ }
+
+ case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE: {
+
+ if(!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_CAUSE_NOT_REPORTABLE_HACK)) {
+
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: "
+ "not ready, cause unknown\n"));
+ /*
+ Many non-WHQL certified drives (mostly CD-RW) return
+ this 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.
+
+ */
+ retry = FALSE;
+ break;
+
+ } else {
+
+ //
+ // Treat this as init command required and fall through.
+ //
+ }
+ }
+
+ case SCSI_SENSEQ_INIT_COMMAND_REQUIRED:
+ default: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Initializing command required\n"));
+
+ //
+ // This sense code/additional sense code
+ // combination may indicate that the device
+ // needs to be started. Send an start unit if this
+ // is a disk device.
+ //
+
+ if(TEST_FLAG(fdoExtension->DeviceFlags,
+ DEV_SAFE_START_UNIT) &&
+ !TEST_FLAG(Srb->SrbFlags,
+ SRB_CLASS_FLAGS_LOW_PRIORITY)) {
+ ClassSendStartUnit(Fdo);
+ }
+ break;
+ }
+
+
+ } // end switch (senseBuffer->AdditionalSenseCodeQualifier)
+ break;
+ }
+
+ case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "No Media in device.\n"));
+ *Status = STATUS_NO_MEDIA_IN_DEVICE;
+ retry = FALSE;
+
+ //
+ // signal MCN that there isn't any media in the device
+ //
+ if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
+ "No Media in a non-removable device %p\n",
+ Fdo));
+ }
+ ClassSetMediaChangeState(fdoExtension, MediaNotPresent, FALSE);
+
+ break;
+ }
+ } // end switch (senseBuffer->AdditionalSenseCode)
+
+ break;
+ } // end SCSI_SENSE_NOT_READY
+
+ case SCSI_SENSE_DATA_PROTECT: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Media write protected\n"));
+ *Status = STATUS_MEDIA_WRITE_PROTECTED;
+ retry = FALSE;
+ break;
+ } // end SCSI_SENSE_DATA_PROTECT
+
+ case SCSI_SENSE_MEDIUM_ERROR: {
+ DebugPrint((ClassDebugSenseInfo,"ClassInterpretSenseInfo: "
+ "Medium Error (bad block)\n"));
+ *Status = STATUS_DEVICE_DATA_ERROR;
+
+ retry = FALSE;
+ logError = TRUE;
+ uniqueId = 256;
+ logStatus = IO_ERR_BAD_BLOCK;
+
+ //
+ // Check if this error is due to unknown format
+ //
+ if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_INVALID_MEDIA){
+
+ switch (senseBuffer->AdditionalSenseCodeQualifier) {
+
+ case SCSI_SENSEQ_UNKNOWN_FORMAT: {
+
+ *Status = STATUS_UNRECOGNIZED_MEDIA;
+
+ //
+ // Log error only if this is a paging request
+ //
+ if(!TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
+ logError = FALSE;
+ }
+ break;
+ }
+
+ case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
+
+ *Status = STATUS_CLEANER_CARTRIDGE_INSTALLED;
+ logError = FALSE;
+ break;
+
+ }
+ default: {
+ break;
+ }
+ } // end switch AdditionalSenseCodeQualifier
+
+ } // end SCSI_ADSENSE_INVALID_MEDIA
+
+ break;
+
+ } // end SCSI_SENSE_MEDIUM_ERROR
+
+ case SCSI_SENSE_HARDWARE_ERROR: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Hardware error\n"));
+ *Status = STATUS_IO_DEVICE_ERROR;
+ logError = TRUE;
+ uniqueId = 257;
+ logStatus = IO_ERR_CONTROLLER_ERROR;
+ break;
+ } // end SCSI_SENSE_HARDWARE_ERROR
+
+ case SCSI_SENSE_ILLEGAL_REQUEST: {
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Illegal SCSI request\n"));
+ *Status = STATUS_INVALID_DEVICE_REQUEST;
+ retry = FALSE;
+
+ switch (senseBuffer->AdditionalSenseCode) {
+
+ case SCSI_ADSENSE_ILLEGAL_COMMAND: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Illegal command\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_ILLEGAL_BLOCK: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Illegal block address\n"));
+ *Status = STATUS_NONEXISTENT_SECTOR;
+ break;
+ }
+
+ case SCSI_ADSENSE_INVALID_LUN: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Invalid LUN\n"));
+ *Status = STATUS_NO_SUCH_DEVICE;
+ break;
+ }
+
+ case SCSI_ADSENSE_MUSIC_AREA: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Music area\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_DATA_AREA: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Data area\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_VOLUME_OVERFLOW: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Volume overflow\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Copy protection failure\n"));
+
+ *Status = STATUS_COPY_PROTECTION_FAILURE;
+
+ switch (senseBuffer->AdditionalSenseCodeQualifier) {
+ case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: "
+ "Authentication failure\n"));
+ *Status = STATUS_CSS_AUTHENTICATION_FAILURE;
+ break;
+ case SCSI_SENSEQ_KEY_NOT_PRESENT:
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: "
+ "Key not present\n"));
+ *Status = STATUS_CSS_KEY_NOT_PRESENT;
+ break;
+ case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: "
+ "Key not established\n"));
+ *Status = STATUS_CSS_KEY_NOT_ESTABLISHED;
+ break;
+ case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: "
+ "Read of scrambled sector w/o "
+ "authentication\n"));
+ *Status = STATUS_CSS_SCRAMBLED_SECTOR;
+ break;
+ case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: "
+ "Media region does not logical unit "
+ "region\n"));
+ *Status = STATUS_CSS_REGION_MISMATCH;
+ break;
+ case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
+ DebugPrint((ClassDebugSenseInfo,
+ "ClassInterpretSenseInfo: "
+ "Region set error -- region may "
+ "be permanent\n"));
+ *Status = STATUS_CSS_RESETS_EXHAUSTED;
+ break;
+ } // end switch of ASCQ for COPY_PROTECTION_FAILURE
+
+ break;
+ }
+
+
+ case SCSI_ADSENSE_INVALID_CDB: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Invalid CDB\n"));
+
+ //
+ // Note: the retry interval is not typically used.
+ // it is set here only because a ClassErrorHandler
+ // cannot set the retryInterval, and the error may
+ // require a few commands to be sent to clear whatever
+ // caused this condition (i.e. disk clears the write
+ // cache, requiring at least two commands)
+ //
+ // hopefully, this shortcoming can be changed for
+ // blackcomb.
+ //
+
+ retryInterval = 3;
+ break;
+ }
+
+ } // end switch (senseBuffer->AdditionalSenseCode)
+
+ break;
+ } // end SCSI_SENSE_ILLEGAL_REQUEST
+
+ case SCSI_SENSE_UNIT_ATTENTION: {
+
+ PVPB vpb;
+ ULONG count;
+
+ //
+ // A media change may have occured so increment the change
+ // count for the physical device
+ //
+
+ count = InterlockedIncrement(&fdoExtension->MediaChangeCount);
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Media change count for device %d incremented to %#lx\n",
+ fdoExtension->DeviceNumber, count));
+
+
+ switch (senseBuffer->AdditionalSenseCode) {
+ case SCSI_ADSENSE_MEDIUM_CHANGED: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Media changed\n"));
+
+ if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ DebugPrint((ClassDebugError, "ClassInterpretSenseInfo: "
+ "Media Changed on non-removable device %p\n",
+ Fdo));
+ }
+ ClassSetMediaChangeState(fdoExtension, MediaPresent, FALSE);
+ break;
+ }
+
+ case SCSI_ADSENSE_BUS_RESET: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Bus reset\n"));
+ break;
+ }
+
+ case SCSI_ADSENSE_OPERATOR_REQUEST: {
+ switch (senseBuffer->AdditionalSenseCodeQualifier) {
+
+ case SCSI_SENSEQ_MEDIUM_REMOVAL: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Ejection request received!\n"));
+ ClassSendEjectionNotification(fdoExtension);
+ break;
+ }
+
+ case SCSI_SENSEQ_WRITE_PROTECT_ENABLE: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Operator selected write permit?! "
+ "(unsupported!)\n"));
+ break;
+ }
+
+ case SCSI_SENSEQ_WRITE_PROTECT_DISABLE: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Operator selected write protect?! "
+ "(unsupported!)\n"));
+ break;
+ }
+
+ }
+ }
+
+ default: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Unit attention\n"));
+ break;
+ }
+
+ } // end switch (senseBuffer->AdditionalSenseCode)
+
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA))
+ {
+ //
+ // TODO : Is the media lockable?
+ //
+
+ if ((ClassGetVpb(Fdo) != NULL) && (ClassGetVpb(Fdo)->Flags & VPB_MOUNTED))
+ {
+ //
+ // Set bit to indicate that media may have changed
+ // and volume needs verification.
+ //
+
+ SET_FLAG(Fdo->Flags, DO_VERIFY_VOLUME);
+
+ *Status = STATUS_VERIFY_REQUIRED;
+ retry = FALSE;
+ }
+ }
+ else
+ {
+ *Status = STATUS_IO_DEVICE_ERROR;
+ }
+
+ break;
+
+ } // end SCSI_SENSE_UNIT_ATTENTION
+
+ case SCSI_SENSE_ABORTED_COMMAND: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Command aborted\n"));
+ *Status = STATUS_IO_DEVICE_ERROR;
+ retryInterval = 1;
+ break;
+ } // end SCSI_SENSE_ABORTED_COMMAND
+
+ case SCSI_SENSE_BLANK_CHECK: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Media blank check\n"));
+ retry = FALSE;
+ *Status = STATUS_NO_DATA_DETECTED;
+ break;
+ } // end SCSI_SENSE_BLANK_CHECK
+
+ case SCSI_SENSE_RECOVERED_ERROR: {
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Recovered error\n"));
+ *Status = STATUS_SUCCESS;
+ retry = FALSE;
+ logError = TRUE;
+ uniqueId = 258;
+
+ switch(senseBuffer->AdditionalSenseCode) {
+ case SCSI_ADSENSE_SEEK_ERROR:
+ case SCSI_ADSENSE_TRACK_ERROR: {
+ logStatus = IO_ERR_SEEK_ERROR;
+ break;
+ }
+
+ case SCSI_ADSENSE_REC_DATA_NOECC:
+ case SCSI_ADSENSE_REC_DATA_ECC: {
+ logStatus = IO_RECOVERED_VIA_ECC;
+ break;
+ }
+
+ case SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED: {
+ UCHAR wmiEventData[5];
+
+ *((PULONG)wmiEventData) = sizeof(UCHAR);
+ wmiEventData[sizeof(ULONG)] = senseBuffer->AdditionalSenseCodeQualifier;
+
+ //
+ // Don't log another eventlog if we have already logged once
+ // NOTE: this should have been interlocked, but the structure
+ // was publicly defined to use a BOOLEAN (char). Since
+ // media only reports these errors once per X minutes,
+ // the potential race condition is nearly non-existant.
+ // the worst case is duplicate log entries, so ignore.
+ //
+
+ if (fdoExtension->FailurePredicted == 0) {
+ logError = TRUE;
+ }
+ fdoExtension->FailurePredicted = TRUE;
+ fdoExtension->FailureReason = senseBuffer->AdditionalSenseCodeQualifier;
+ logStatus = IO_WRN_FAILURE_PREDICTED;
+
+ ClassNotifyFailurePredicted(fdoExtension,
+ (PUCHAR)&wmiEventData,
+ sizeof(wmiEventData),
+ 0,
+ 4,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+ break;
+ }
+
+ default: {
+ logStatus = IO_ERR_CONTROLLER_ERROR;
+ break;
+ }
+
+ } // end switch(senseBuffer->AdditionalSenseCode)
+
+ if (senseBuffer->IncorrectLength) {
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Incorrect length detected.\n"));
+ *Status = STATUS_INVALID_BLOCK_LENGTH ;
+ }
+
+ break;
+ } // end SCSI_SENSE_RECOVERED_ERROR
+
+ case SCSI_SENSE_NO_SENSE: {
+
+ //
+ // Check other indicators.
+ //
+
+ if (senseBuffer->IncorrectLength) {
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Incorrect length detected.\n"));
+ *Status = STATUS_INVALID_BLOCK_LENGTH ;
+ retry = FALSE;
+
+ } else {
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "No specific sense key\n"));
+ *Status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ break;
+ } // end SCSI_SENSE_NO_SENSE
+
+ default: {
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Unrecognized sense code\n"));
+ *Status = STATUS_IO_DEVICE_ERROR;
+ break;
+ }
+
+ } // end switch (senseBuffer->SenseKey & 0xf)
+
+ //
+ // Try to determine the bad sector from the inquiry data.
+ //
+
+ if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_READ ||
+ ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_VERIFY ||
+ ((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_WRITE)) {
+
+ for (index = 0; index < 4; index++) {
+ badSector = (badSector << 8) | senseBuffer->Information[index];
+ }
+
+ readSector = 0;
+ for (index = 0; index < 4; index++) {
+ readSector = (readSector << 8) | Srb->Cdb[index+2];
+ }
+
+ index = (((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8) |
+ ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb;
+
+ //
+ // Make sure the bad sector is within the read sectors.
+ //
+
+ if (!(badSector >= readSector && badSector < readSector + index)) {
+ badSector = readSector;
+ }
+ }
+
+ } else {
+
+ //
+ // Request sense buffer not valid. No sense information
+ // to pinpoint the error. Return general request fail.
+ //
+
+ DebugPrint((ClassDebugSenseInfo, "ClassInterpretSenseInfo: "
+ "Request sense info not valid. SrbStatus %2x\n",
+ SRB_STATUS(Srb->SrbStatus)));
+ retry = TRUE;
+
+ switch (SRB_STATUS(Srb->SrbStatus)) {
+ case SRB_STATUS_INVALID_LUN:
+ case SRB_STATUS_INVALID_TARGET_ID:
+ case SRB_STATUS_NO_DEVICE:
+ case SRB_STATUS_NO_HBA:
+ case SRB_STATUS_INVALID_PATH_ID: {
+ *Status = STATUS_NO_SUCH_DEVICE;
+ retry = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_COMMAND_TIMEOUT:
+ case SRB_STATUS_TIMEOUT: {
+
+ //
+ // Update the error count for the device.
+ //
+
+ incrementErrorCount = TRUE;
+ *Status = STATUS_IO_TIMEOUT;
+ break;
+ }
+
+ case SRB_STATUS_ABORTED: {
+
+ //
+ // Update the error count for the device.
+ //
+
+ incrementErrorCount = TRUE;
+ *Status = STATUS_IO_TIMEOUT;
+ retryInterval = 1;
+ break;
+ }
+
+
+ case SRB_STATUS_SELECTION_TIMEOUT: {
+ logError = TRUE;
+ logStatus = IO_ERR_NOT_READY;
+ uniqueId = 260;
+ *Status = STATUS_DEVICE_NOT_CONNECTED;
+ retry = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_DATA_OVERRUN: {
+ *Status = STATUS_DATA_OVERRUN;
+ retry = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_PHASE_SEQUENCE_FAILURE: {
+
+ //
+ // Update the error count for the device.
+ //
+
+ incrementErrorCount = TRUE;
+ *Status = STATUS_IO_DEVICE_ERROR;
+
+ //
+ // If there was phase sequence error then limit the number of
+ // retries.
+ //
+
+ if (RetryCount > 1 ) {
+ retry = FALSE;
+ }
+
+ break;
+ }
+
+ case SRB_STATUS_REQUEST_FLUSHED: {
+
+ //
+ // If the status needs verification bit is set. Then set
+ // the status to need verification and no retry; otherwise,
+ // just retry the request.
+ //
+
+ if (TEST_FLAG(Fdo->Flags, DO_VERIFY_VOLUME)) {
+
+ *Status = STATUS_VERIFY_REQUIRED;
+ retry = FALSE;
+
+ } else {
+ *Status = STATUS_IO_DEVICE_ERROR;
+ }
+
+ break;
+ }
+
+ case SRB_STATUS_INVALID_REQUEST: {
+ *Status = STATUS_INVALID_DEVICE_REQUEST;
+ retry = FALSE;
+ break;
+ }
+
+ case SRB_STATUS_UNEXPECTED_BUS_FREE:
+ case SRB_STATUS_PARITY_ERROR:
+
+ //
+ // Update the error count for the device
+ // and fall through to below
+ //
+
+ incrementErrorCount = TRUE;
+
+ case SRB_STATUS_BUS_RESET: {
+ *Status = STATUS_IO_DEVICE_ERROR;
+ break;
+ }
+
+ case SRB_STATUS_ERROR: {
+
+ *Status = STATUS_IO_DEVICE_ERROR;
+ if (Srb->ScsiStatus == 0) {
+
+ //
+ // This is some strange return code. Update the error
+ // count for the device.
+ //
+
+ incrementErrorCount = TRUE;
+
+ } if (Srb->ScsiStatus == SCSISTAT_BUSY) {
+
+ *Status = STATUS_DEVICE_NOT_READY;
+
+ } if (Srb->ScsiStatus == SCSISTAT_RESERVATION_CONFLICT) {
+
+ *Status = STATUS_DEVICE_BUSY;
+ retry = FALSE;
+ logError = FALSE;
+
+ }
+
+ break;
+ }
+
+ default: {
+ logError = TRUE;
+ logStatus = IO_ERR_CONTROLLER_ERROR;
+ uniqueId = 259;
+ *Status = STATUS_IO_DEVICE_ERROR;
+ unhandledError = TRUE;
+ break;
+ }
+
+ }
+
+ //
+ // NTRAID #183546 - if we support GESN subtype NOT_READY events, and
+ // we know from a previous poll when the device will be ready (ETA)
+ // we should delay the retry more appropriately than just guessing.
+ //
+ /*
+ if (fdoExtension->MediaChangeDetectionInfo &&
+ fdoExtension->MediaChangeDetectionInfo->Gesn.Supported &&
+ TEST_FLAG(fdoExtension->MediaChangeDetectionInfo->Gesn.EventMask,
+ NOTIFICATION_DEVICE_BUSY_CLASS_MASK)
+ ) {
+ // check if Gesn.ReadyTime if greater than current tick count
+ // if so, delay that long (from 1 to 30 seconds max?)
+ // else, leave the guess of time alone.
+ }
+ */
+
+ }
+
+ if (incrementErrorCount) {
+
+ //
+ // if any error count occurred, delay the retry of this io by
+ // at least one second, if caller supports it.
+ //
+
+ if (retryInterval == 0) {
+ retryInterval = 1;
+ }
+ ClasspPerfIncrementErrorCount(fdoExtension);
+ }
+
+ //
+ // If there is a class specific error handler call it.
+ //
+
+ if (fdoExtension->CommonExtension.DevInfo->ClassError != NULL) {
+
+ fdoExtension->CommonExtension.DevInfo->ClassError(Fdo,
+ Srb,
+ Status,
+ &retry);
+ }
+
+ //
+ // If the caller wants to know the suggested retry interval tell them.
+ //
+
+ if(ARGUMENT_PRESENT(RetryInterval)) {
+ *RetryInterval = retryInterval;
+ }
+
+
+ /*
+ * LOG the error:
+ * Always log the error in our internal log.
+ * If logError is set, also log the error in the system log.
+ */
+ {
+ ULONG totalSize;
+ ULONG senseBufferSize = 0;
+ IO_ERROR_LOG_PACKET staticErrLogEntry = {0};
+ CLASS_ERROR_LOG_DATA staticErrLogData = {0};
+
+ //
+ // Calculate the total size of the error log entry.
+ // add to totalSize in the order that they are used.
+ // the advantage to calculating all the sizes here is
+ // that we don't have to do a bunch of extraneous checks
+ // later on in this code path.
+ //
+ totalSize = sizeof(IO_ERROR_LOG_PACKET) // required
+ - sizeof(ULONG) // struct includes one ULONG
+ + sizeof(CLASS_ERROR_LOG_DATA);// struct for ease
+
+ //
+ // also save any available extra sense data, up to the maximum errlog
+ // packet size . WMI should be used for real-time analysis.
+ // the event log should only be used for post-mortem debugging.
+ //
+ if (TEST_FLAG(Srb->SrbStatus, SRB_STATUS_AUTOSENSE_VALID)) {
+ ULONG validSenseBytes;
+ BOOLEAN validSense;
+
+ //
+ // make sure we can at least access the AdditionalSenseLength field
+ //
+ validSense = RTL_CONTAINS_FIELD(senseBuffer,
+ Srb->SenseInfoBufferLength,
+ AdditionalSenseLength);
+ if (validSense) {
+
+ //
+ // if extra info exists, copy the maximum amount of available
+ // sense data that is safe into the the errlog.
+ //
+ validSenseBytes = senseBuffer->AdditionalSenseLength
+ + offsetof(SENSE_DATA, AdditionalSenseLength);
+
+ //
+ // this is invalid because it causes overflow!
+ // whoever sent this type of request would cause
+ // a system crash.
+ //
+ ASSERT(validSenseBytes < MAX_ADDITIONAL_SENSE_BYTES);
+
+ //
+ // set to save the most sense buffer possible
+ //
+ senseBufferSize = max(validSenseBytes, sizeof(SENSE_DATA));
+ senseBufferSize = min(senseBufferSize, Srb->SenseInfoBufferLength);
+ } else {
+ //
+ // it's smaller than required to read the total number of
+ // valid bytes, so just use the SenseInfoBufferLength field.
+ //
+ senseBufferSize = Srb->SenseInfoBufferLength;
+ }
+
+ /*
+ * Bump totalSize by the number of extra senseBuffer bytes
+ * (beyond the default sense buffer within CLASS_ERROR_LOG_DATA).
+ * Make sure to never allocate more than ERROR_LOG_MAXIMUM_SIZE.
+ */
+ if (senseBufferSize > sizeof(SENSE_DATA)){
+ totalSize += senseBufferSize-sizeof(SENSE_DATA);
+ if (totalSize > ERROR_LOG_MAXIMUM_SIZE){
+ senseBufferSize -= totalSize-ERROR_LOG_MAXIMUM_SIZE;
+ totalSize = ERROR_LOG_MAXIMUM_SIZE;
+ }
+ }
+ }
+
+ //
+ // If we've used up all of our retry attempts, set the final status to
+ // reflect the appropriate result.
+ //
+ if (retry && RetryCount < MAXIMUM_RETRIES) {
+ staticErrLogEntry.FinalStatus = STATUS_SUCCESS;
+ staticErrLogData.ErrorRetried = TRUE;
+ } else {
+ staticErrLogEntry.FinalStatus = *Status;
+ }
+ if (TEST_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING)) {
+ staticErrLogData.ErrorPaging = TRUE;
+ }
+ if (unhandledError) {
+ staticErrLogData.ErrorUnhandled = TRUE;
+ }
+
+ //
+ // Calculate the device offset if there is a geometry.
+ //
+ staticErrLogEntry.DeviceOffset.QuadPart = (LONGLONG)badSector;
+ staticErrLogEntry.DeviceOffset.QuadPart *= (LONGLONG)fdoExtension->DiskGeometry.BytesPerSector;
+ if (logStatus == -1){
+ staticErrLogEntry.ErrorCode = STATUS_IO_DEVICE_ERROR;
+ } else {
+ staticErrLogEntry.ErrorCode = logStatus;
+ }
+
+ /*
+ * The dump data follows the IO_ERROR_LOG_PACKET,
+ * with the first ULONG of dump data inside the packet.
+ */
+ staticErrLogEntry.DumpDataSize = (USHORT)totalSize - sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG);
+
+ staticErrLogEntry.SequenceNumber = 0;
+ staticErrLogEntry.MajorFunctionCode = MajorFunctionCode;
+ staticErrLogEntry.IoControlCode = IoDeviceCode;
+ staticErrLogEntry.RetryCount = (UCHAR) RetryCount;
+ staticErrLogEntry.UniqueErrorValue = uniqueId;
+
+ KeQueryTickCount(&staticErrLogData.TickCount);
+ staticErrLogData.PortNumber = (ULONG)-1;
+
+ /*
+ * Save the entire contents of the SRB.
+ */
+ staticErrLogData.Srb = *Srb;
+
+ /*
+ * For our private log, save just the default length of the SENSE_DATA.
+ */
+ if (senseBufferSize != 0){
+ RtlCopyMemory(&staticErrLogData.SenseData, senseBuffer, min(senseBufferSize, sizeof(SENSE_DATA)));
+ }
+
+ /*
+ * Save the error log in our context.
+ * We only save the default sense buffer length.
+ */
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ fdoData->ErrorLogs[fdoData->ErrorLogNextIndex] = staticErrLogData;
+ fdoData->ErrorLogNextIndex++;
+ fdoData->ErrorLogNextIndex %= NUM_ERROR_LOG_ENTRIES;
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ /*
+ * If logError is set, also save this log in the system's error log.
+ * But make sure we don't log TUR failures over and over
+ * (e.g. if an external drive was switched off and we're still sending TUR's to it every second).
+ */
+ if ((((PCDB)Srb->Cdb)->CDB10.OperationCode == SCSIOP_TEST_UNIT_READY) && logError){
+ if (fdoData->LoggedTURFailureSinceLastIO){
+ logError = FALSE;
+ }
+ else {
+ fdoData->LoggedTURFailureSinceLastIO = TRUE;
+ }
+ }
+ if (logError){
+ PIO_ERROR_LOG_PACKET errorLogEntry;
+ PCLASS_ERROR_LOG_DATA errlogData;
+
+ errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(Fdo, (UCHAR)totalSize);
+ if (errorLogEntry){
+ errlogData = (PCLASS_ERROR_LOG_DATA)errorLogEntry->DumpData;
+
+ *errorLogEntry = staticErrLogEntry;
+ *errlogData = staticErrLogData;
+
+ /*
+ * For the system log, copy as much of the sense buffer as possible.
+ */
+ if (senseBufferSize != 0) {
+ RtlCopyMemory(&errlogData->SenseData, senseBuffer, senseBufferSize);
+ }
+
+ /*
+ * Write the error log packet to the system error logging thread.
+ */
+ IoWriteErrorLogEntry(errorLogEntry);
+ }
+ }
+ }
+
+ return retry;
+
+} // end ClassInterpretSenseInfo()
+
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassModeSense()
+
+Routine Description:
+
+ This routine sends a mode sense command to a target ID and returns
+ when it is complete.
+
+Arguments:
+
+ Fdo - Supplies the functional device object associated with this request.
+
+ ModeSenseBuffer - Supplies a buffer to store the sense data.
+
+ Length - Supplies the length in bytes of the mode sense buffer.
+
+ PageMode - Supplies the page or pages of mode sense data to be retrived.
+
+Return Value:
+
+ Length of the transferred data is returned.
+
+--*/
+ULONG ClassModeSense( IN PDEVICE_OBJECT Fdo,
+ IN PCHAR ModeSenseBuffer,
+ IN ULONG Length,
+ IN UCHAR PageMode)
+{
+ ULONG lengthTransferred = 0;
+ PMDL senseBufferMdl;
+
+ PAGED_CODE();
+
+ senseBufferMdl = BuildDeviceInputMdl(ModeSenseBuffer, Length);
+ if (senseBufferMdl){
+
+ TRANSFER_PACKET *pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ KEVENT event;
+ NTSTATUS pktStatus;
+ IRP pseudoIrp = {0};
+
+ /*
+ * Store the number of packets servicing the irp (one)
+ * inside the original IRP. It will be used to counted down
+ * to zero when the packet completes.
+ * Initialize the original IRP's status to success.
+ * If the packet fails, we will set it to the error status.
+ */
+ pseudoIrp.Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ pseudoIrp.IoStatus.Status = STATUS_SUCCESS;
+ pseudoIrp.IoStatus.Information = 0;
+ pseudoIrp.MdlAddress = senseBufferMdl;
+
+ /*
+ * Set this up as a SYNCHRONOUS transfer, submit it,
+ * and wait for the packet to complete. The result
+ * status will be written to the original irp.
+ */
+ ASSERT(Length <= 0x0ff);
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupModeSenseTransferPacket(pkt, &event, ModeSenseBuffer, (UCHAR)Length, PageMode, &pseudoIrp);
+ SubmitTransferPacket(pkt);
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+
+ if (NT_SUCCESS(pseudoIrp.IoStatus.Status)){
+ lengthTransferred = (ULONG)pseudoIrp.IoStatus.Information;
+ }
+ else {
+ /*
+ * This request can sometimes fail legitimately
+ * (e.g. when a SCSI device is attached but turned off)
+ * so this is not necessarily a device/driver bug.
+ */
+ DBGTRACE(ClassDebugWarning, ("ClassModeSense on Fdo %ph failed with status %xh.", Fdo, pseudoIrp.IoStatus.Status));
+ }
+ }
+
+ FreeDeviceInputMdl(senseBufferMdl);
+ }
+
+ return lengthTransferred;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassFindModePage()
+
+Routine Description:
+
+ This routine scans through the mode sense data and finds the requested
+ mode sense page code.
+
+Arguments:
+ ModeSenseBuffer - Supplies a pointer to the mode sense data.
+
+ Length - Indicates the length of valid data.
+
+ PageMode - Supplies the page mode to be searched for.
+
+ Use6Byte - Indicates whether 6 or 10 byte mode sense was used.
+
+Return Value:
+
+ A pointer to the the requested mode page. If the mode page was not found
+ then NULL is return.
+
+--*/
+PVOID
+ClassFindModePage(
+ IN PCHAR ModeSenseBuffer,
+ IN ULONG Length,
+ IN UCHAR PageMode,
+ IN BOOLEAN Use6Byte
+ )
+{
+ PUCHAR limit;
+ ULONG parameterHeaderLength;
+ PVOID result = NULL;
+
+ limit = ModeSenseBuffer + Length;
+ parameterHeaderLength = (Use6Byte) ? sizeof(MODE_PARAMETER_HEADER) : sizeof(MODE_PARAMETER_HEADER10);
+
+ if (Length >= parameterHeaderLength) {
+
+ PMODE_PARAMETER_HEADER10 modeParam10;
+ ULONG blockDescriptorLength;
+
+ /*
+ * Skip the mode select header and block descriptors.
+ */
+ if (Use6Byte){
+ blockDescriptorLength = ((PMODE_PARAMETER_HEADER) ModeSenseBuffer)->BlockDescriptorLength;
+ }
+ else {
+ modeParam10 = (PMODE_PARAMETER_HEADER10) ModeSenseBuffer;
+ blockDescriptorLength = modeParam10->BlockDescriptorLength[1];
+ }
+
+ ModeSenseBuffer += parameterHeaderLength + blockDescriptorLength;
+
+ //
+ // ModeSenseBuffer now points at pages. Walk the pages looking for the
+ // requested page until the limit is reached.
+ //
+
+ while (ModeSenseBuffer +
+ RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength) < limit) {
+
+ if (((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageCode == PageMode) {
+
+ /*
+ * found the mode page. make sure it's safe to touch it all
+ * before returning the pointer to caller
+ */
+
+ if (ModeSenseBuffer + ((PMODE_DISCONNECT_PAGE)ModeSenseBuffer)->PageLength > limit) {
+ /*
+ * Return NULL since the page is not safe to access in full
+ */
+ result = NULL;
+ }
+ else {
+ result = ModeSenseBuffer;
+ }
+ break;
+ }
+
+ //
+ // Advance to the next page which is 4-byte-aligned offset after this page.
+ //
+ ModeSenseBuffer +=
+ ((PMODE_DISCONNECT_PAGE) ModeSenseBuffer)->PageLength +
+ RTL_SIZEOF_THROUGH_FIELD(MODE_DISCONNECT_PAGE, PageLength);
+
+ }
+ }
+
+ return result;
+} // end ClassFindModePage()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendSrbAsynchronous()
+
+Routine Description:
+
+ This routine takes a partially built Srb and an Irp and sends it down to
+ the port driver.
+
+ This routine must be called with the remove lock held for the specified
+ Irp.
+
+Arguments:
+
+ Fdo - Supplies the functional device object for the orginal request.
+
+ Srb - Supplies a paritally build ScsiRequestBlock. In particular, the
+ CDB and the SRB timeout value must be filled in. The SRB must not be
+ allocated from zone.
+
+ Irp - Supplies the requesting Irp.
+
+ BufferAddress - Supplies a pointer to the buffer to be transfered.
+
+ BufferLength - Supplies the length of data transfer.
+
+ WriteToDevice - Indicates the data transfer will be from system memory to
+ device.
+
+Return Value:
+
+ Returns STATUS_PENDING if the request is dispatched (since the
+ completion routine may change the irp's status value we cannot simply
+ return the value of the dispatch)
+
+ or returns a status value to indicate why it failed.
+
+--*/
+NTSTATUS
+ClassSendSrbAsynchronous(
+ PDEVICE_OBJECT Fdo,
+ PSCSI_REQUEST_BLOCK Srb,
+ PIRP Irp,
+ PVOID BufferAddress,
+ ULONG BufferLength,
+ BOOLEAN WriteToDevice
+ )
+{
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PIO_STACK_LOCATION irpStack;
+
+ ULONG savedFlags;
+
+ //
+ // Write length to SRB.
+ //
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ //
+ // Set SCSI bus address.
+ //
+
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ //
+ // This is a violation of the SCSI spec but it is required for
+ // some targets.
+ //
+
+ // Srb->Cdb[1] |= deviceExtension->Lun << 5;
+
+ //
+ // Indicate auto request sense by specifying buffer and size.
+ //
+
+ Srb->SenseInfoBuffer = fdoExtension->SenseData;
+ Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+ Srb->DataBuffer = BufferAddress;
+
+ //
+ // Save the class driver specific flags away.
+ //
+
+ savedFlags = Srb->SrbFlags & SRB_FLAGS_CLASS_DRIVER_RESERVED;
+
+ //
+ // Allow the caller to specify that they do not wish
+ // IoStartNextPacket() to be called in the completion routine.
+ //
+
+ SET_FLAG(savedFlags, (Srb->SrbFlags & SRB_FLAGS_DONT_START_NEXT_PACKET));
+
+ if (BufferAddress != NULL) {
+
+ //
+ // Build Mdl if necessary.
+ //
+
+ if (Irp->MdlAddress == NULL) {
+
+ if (IoAllocateMdl(BufferAddress,
+ BufferLength,
+ FALSE,
+ FALSE,
+ Irp) == NULL) {
+
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ //
+ // ClassIoComplete() would have free'd the srb
+ //
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, Srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, Srb);
+ }
+ ClassFreeOrReuseSrb(fdoExtension, Srb);
+ ClassReleaseRemoveLock(Fdo, Irp);
+ ClassCompleteRequest(Fdo, Irp, IO_NO_INCREMENT);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ MmBuildMdlForNonPagedPool(Irp->MdlAddress);
+
+ } else {
+
+ //
+ // Make sure the buffer requested matches the MDL.
+ //
+
+ ASSERT(BufferAddress == MmGetMdlVirtualAddress(Irp->MdlAddress));
+ }
+
+ //
+ // Set read flag.
+ //
+
+ Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
+
+ } else {
+
+ //
+ // Clear flags.
+ //
+
+ Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
+ }
+
+ //
+ // Restore saved flags.
+ //
+
+ SET_FLAG(Srb->SrbFlags, savedFlags);
+
+ //
+ // Disable synchronous transfer for these requests.
+ //
+
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ //
+ // Set the transfer length.
+ //
+
+ Srb->DataTransferLength = BufferLength;
+
+ //
+ // Zero out status.
+ //
+
+ Srb->ScsiStatus = Srb->SrbStatus = 0;
+
+ Srb->NextSrb = 0;
+
+ //
+ // Save a few parameters in the current stack location.
+ //
+
+ irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ //
+ // Save retry count in current Irp stack.
+ //
+
+ irpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+ //
+ // Set up IoCompletion routine address.
+ //
+
+ IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
+
+ //
+ // Get next stack location and
+ // set major function code.
+ //
+
+ irpStack = IoGetNextIrpStackLocation(Irp);
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = Srb;
+
+ //
+ // Set up Irp Address.
+ //
+
+ Srb->OriginalRequest = Irp;
+
+ //
+ // Call the port driver to process the request.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, Irp);
+
+ return STATUS_PENDING;
+
+} // end ClassSendSrbAsynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDeviceControlDispatch()
+
+Routine Description:
+
+ The routine is the common class driver device control dispatch entry point.
+ This routine is invokes the device-specific drivers DeviceControl routine,
+ (which may call the Class driver's common DeviceControl routine).
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ Returns the status returned from the device-specific driver.
+
+--*/
+NTSTATUS
+ClassDeviceControlDispatch(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ ULONG isRemoved;
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ if(isRemoved) {
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ //
+ // Call the class specific driver DeviceControl routine.
+ // If it doesn't handle it, it will call back into ClassDeviceControl.
+ //
+
+ ASSERT(commonExtension->DevInfo->ClassDeviceControl);
+
+ return commonExtension->DevInfo->ClassDeviceControl(DeviceObject,Irp);
+} // end ClassDeviceControlDispatch()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDeviceControl()
+
+Routine Description:
+
+ The routine is the common class driver device control dispatch function.
+ This routine is called by a class driver when it get an unrecognized
+ device control request. This routine will perform the correct action for
+ common requests such as lock media. If the device request is unknown it
+ passed down to the next level.
+
+ This routine must be called with the remove lock held for the specified
+ irp.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ Returns back a STATUS_PENDING or a completion status.
+
+--*/
+NTSTATUS
+ClassDeviceControl(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextStack = NULL;
+
+ ULONG controlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;
+
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ PCDB cdb = NULL;
+
+ NTSTATUS status;
+ ULONG modifiedIoControlCode;
+
+ //
+ // If this is a pass through I/O control, set the minor function code
+ // and device address and pass it to the port driver.
+ //
+
+ if ((controlCode == IOCTL_SCSI_PASS_THROUGH) ||
+ (controlCode == IOCTL_SCSI_PASS_THROUGH_DIRECT)) {
+
+ PSCSI_PASS_THROUGH scsiPass;
+
+ //
+ // Validiate the user buffer.
+ //
+ #if defined (_WIN64)
+
+ if (IoIs32bitProcess(Irp)) {
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SCSI_PASS_THROUGH32)){
+
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ status = STATUS_INVALID_PARAMETER;
+ goto SetStatusAndReturn;
+ }
+ }
+ else
+ #endif
+ {
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(SCSI_PASS_THROUGH)) {
+
+ Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ status = STATUS_INVALID_PARAMETER;
+ goto SetStatusAndReturn;
+ }
+ }
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ nextStack = IoGetNextIrpStackLocation(Irp);
+ nextStack->MinorFunction = 1;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+ }
+
+ Irp->IoStatus.Information = 0;
+
+ switch (controlCode) {
+
+ case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: {
+
+ PMOUNTDEV_UNIQUE_ID uniqueId;
+
+ if (!commonExtension->MountedDeviceInterfaceName.Buffer) {
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(MOUNTDEV_UNIQUE_ID)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
+ break;
+ }
+
+ uniqueId = Irp->AssociatedIrp.SystemBuffer;
+ uniqueId->UniqueIdLength =
+ commonExtension->MountedDeviceInterfaceName.Length;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(USHORT) + uniqueId->UniqueIdLength) {
+
+ status = STATUS_BUFFER_OVERFLOW;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
+ break;
+ }
+
+ RtlCopyMemory(uniqueId->UniqueId,
+ commonExtension->MountedDeviceInterfaceName.Buffer,
+ uniqueId->UniqueIdLength);
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(USHORT) +
+ uniqueId->UniqueIdLength;
+ break;
+ }
+
+ case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: {
+
+ PMOUNTDEV_NAME name;
+
+ ASSERT(commonExtension->DeviceName.Buffer);
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(MOUNTDEV_NAME)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
+ break;
+ }
+
+ name = Irp->AssociatedIrp.SystemBuffer;
+ name->NameLength = commonExtension->DeviceName.Length;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(USHORT) + name->NameLength) {
+
+ status = STATUS_BUFFER_OVERFLOW;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
+ break;
+ }
+
+ RtlCopyMemory(name->Name, commonExtension->DeviceName.Buffer,
+ name->NameLength);
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(USHORT) + name->NameLength;
+ break;
+ }
+
+ case IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME: {
+
+ PMOUNTDEV_SUGGESTED_LINK_NAME suggestedName;
+ WCHAR driveLetterNameBuffer[10];
+ RTL_QUERY_REGISTRY_TABLE queryTable[2];
+ PWSTR valueName;
+ UNICODE_STRING driveLetterName;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME)) {
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
+ break;
+ }
+
+ valueName = ExAllocatePoolWithTag(
+ PagedPool,
+ commonExtension->DeviceName.Length + sizeof(WCHAR),
+ '8CcS');
+
+ if (!valueName) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ RtlCopyMemory(valueName, commonExtension->DeviceName.Buffer,
+ commonExtension->DeviceName.Length);
+ valueName[commonExtension->DeviceName.Length/sizeof(WCHAR)] = 0;
+
+ driveLetterName.Buffer = driveLetterNameBuffer;
+ driveLetterName.MaximumLength = 20;
+ driveLetterName.Length = 0;
+
+ RtlZeroMemory(queryTable, 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED |
+ RTL_QUERY_REGISTRY_DIRECT;
+ queryTable[0].Name = valueName;
+ queryTable[0].EntryContext = &driveLetterName;
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+ L"\\Registry\\Machine\\System\\DISK",
+ queryTable, NULL, NULL);
+
+ if (!NT_SUCCESS(status)) {
+ ExFreePool(valueName);
+ break;
+ }
+
+ if (driveLetterName.Length == 4 &&
+ driveLetterName.Buffer[0] == '%' &&
+ driveLetterName.Buffer[1] == ':') {
+
+ driveLetterName.Buffer[0] = 0xFF;
+
+ } else if (driveLetterName.Length != 4 ||
+ driveLetterName.Buffer[0] < FirstDriveLetter ||
+ driveLetterName.Buffer[0] > LastDriveLetter ||
+ driveLetterName.Buffer[1] != ':') {
+
+ status = STATUS_NOT_FOUND;
+ ExFreePool(valueName);
+ break;
+ }
+
+ suggestedName = Irp->AssociatedIrp.SystemBuffer;
+ suggestedName->UseOnlyIfThereAreNoOtherLinks = TRUE;
+ suggestedName->NameLength = 28;
+
+ Irp->IoStatus.Information =
+ FIELD_OFFSET(MOUNTDEV_SUGGESTED_LINK_NAME, Name) + 28;
+
+ if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ Irp->IoStatus.Information) {
+
+ Irp->IoStatus.Information =
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
+ status = STATUS_BUFFER_OVERFLOW;
+ ExFreePool(valueName);
+ break;
+ }
+
+ RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
+ L"\\Registry\\Machine\\System\\DISK",
+ valueName);
+
+ ExFreePool(valueName);
+
+ RtlCopyMemory(suggestedName->Name, L"\\DosDevices\\", 24);
+ suggestedName->Name[12] = driveLetterName.Buffer[0];
+ suggestedName->Name[13] = ':';
+
+ //
+ // NT_SUCCESS(status) based on RtlQueryRegistryValues
+ //
+ status = STATUS_SUCCESS;
+
+ break;
+ }
+
+ default:
+ status = STATUS_PENDING;
+ break;
+ }
+
+ if (status != STATUS_PENDING) {
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ return status;
+ }
+
+ if (commonExtension->IsFdo){
+
+ PULONG_PTR function;
+
+ srb = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(SCSI_REQUEST_BLOCK) +
+ (sizeof(ULONG_PTR) * 2),
+ '9CcS');
+
+ if (srb == NULL) {
+
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SetStatusAndReturn;
+ }
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ cdb = (PCDB)srb->Cdb;
+
+ //
+ // Save the function code and the device object in the memory after
+ // the SRB.
+ //
+
+ function = (PULONG_PTR) ((PSCSI_REQUEST_BLOCK) (srb + 1));
+ *function = (ULONG_PTR) DeviceObject;
+ function++;
+ *function = (ULONG_PTR) controlCode;
+
+ } else {
+ srb = NULL;
+ }
+
+ //
+ // Change the device type to storage for the switch statement, but only
+ // if from a legacy device type
+ //
+
+ if (((controlCode & 0xffff0000) == (IOCTL_DISK_BASE << 16)) ||
+ ((controlCode & 0xffff0000) == (IOCTL_TAPE_BASE << 16)) ||
+ ((controlCode & 0xffff0000) == (IOCTL_CDROM_BASE << 16))
+ ) {
+
+ modifiedIoControlCode = (controlCode & ~0xffff0000);
+ modifiedIoControlCode |= (IOCTL_STORAGE_BASE << 16);
+
+ } else {
+
+ modifiedIoControlCode = controlCode;
+
+ }
+
+ DBGTRACE(ClassDebugTrace, ("> ioctl %xh (%s)", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode)));
+
+ switch (modifiedIoControlCode) {
+
+ case IOCTL_STORAGE_GET_HOTPLUG_INFO: {
+
+ if (srb) {
+ ExFreePool(srb);
+ srb = NULL;
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(STORAGE_HOTPLUG_INFO)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if(!commonExtension->IsFdo) {
+
+ //
+ // Just forward this down and return
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ } else {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PSTORAGE_HOTPLUG_INFO info;
+
+ fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
+ info = Irp->AssociatedIrp.SystemBuffer;
+
+ *info = fdoExtension->PrivateFdoData->HotplugInfo;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(STORAGE_HOTPLUG_INFO);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_SUCCESS;
+
+ }
+ break;
+ }
+
+ case IOCTL_STORAGE_SET_HOTPLUG_INFO: {
+
+ if (srb)
+ {
+ ExFreePool(srb);
+ srb = NULL;
+ }
+
+ if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(STORAGE_HOTPLUG_INFO)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto SetStatusAndReturn;
+
+ }
+
+ if(!commonExtension->IsFdo) {
+
+ //
+ // Just forward this down and return
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ } else {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)commonExtension;
+ PSTORAGE_HOTPLUG_INFO info = Irp->AssociatedIrp.SystemBuffer;
+
+ status = STATUS_SUCCESS;
+
+ if (info->Size != fdoExtension->PrivateFdoData->HotplugInfo.Size)
+ {
+ status = STATUS_INVALID_PARAMETER_1;
+ }
+
+ if (info->MediaRemovable != fdoExtension->PrivateFdoData->HotplugInfo.MediaRemovable)
+ {
+ status = STATUS_INVALID_PARAMETER_2;
+ }
+
+ if (info->MediaHotplug != fdoExtension->PrivateFdoData->HotplugInfo.MediaHotplug)
+ {
+ status = STATUS_INVALID_PARAMETER_3;
+ }
+
+ if (info->WriteCacheEnableOverride != fdoExtension->PrivateFdoData->HotplugInfo.WriteCacheEnableOverride)
+ {
+ status = STATUS_INVALID_PARAMETER_5;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ fdoExtension->PrivateFdoData->HotplugInfo.DeviceHotplug = info->DeviceHotplug;
+
+ //
+ // Store the user-defined override in the registry
+ //
+
+ ClassSetDeviceParameter(fdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
+ (info->DeviceHotplug) ? RemovalPolicyExpectSurpriseRemoval : RemovalPolicyExpectOrderlyRemoval);
+ }
+
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ break;
+ }
+
+ case IOCTL_STORAGE_CHECK_VERIFY:
+ case IOCTL_STORAGE_CHECK_VERIFY2: {
+
+ PIRP irp2 = NULL;
+ PIO_STACK_LOCATION newStack;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
+
+ DebugPrint((1,"DeviceIoControl: Check verify\n"));
+
+ //
+ // If a buffer for a media change count was provided, make sure it's
+ // big enough to hold the result
+ //
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
+
+ //
+ // If the buffer is too small to hold the media change count
+ // then return an error to the caller
+ //
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength <
+ sizeof(ULONG)) {
+
+ DebugPrint((3,"DeviceIoControl: media count "
+ "buffer too small\n"));
+
+ Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(ULONG);
+
+ if(srb != NULL) {
+ ExFreePool(srb);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto SetStatusAndReturn;
+
+ }
+ }
+
+ if(!commonExtension->IsFdo) {
+
+ //
+ // If this is a PDO then we should just forward the request down
+ //
+ ASSERT(!srb);
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ goto SetStatusAndReturn;
+
+ } else {
+
+ fdoExtension = DeviceObject->DeviceExtension;
+
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength) {
+
+ //
+ // The caller has provided a valid buffer. Allocate an additional
+ // irp and stick the CheckVerify completion routine on it. We will
+ // then send this down to the port driver instead of the irp the
+ // caller sent in
+ //
+
+ DebugPrint((2,"DeviceIoControl: Check verify wants "
+ "media count\n"));
+
+ //
+ // Allocate a new irp to send the TestUnitReady to the port driver
+ //
+
+ irp2 = IoAllocateIrp((CCHAR) (DeviceObject->StackSize + 3), FALSE);
+
+ if(irp2 == NULL) {
+ Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Information = 0;
+ ASSERT(srb);
+ ExFreePool(srb);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto SetStatusAndReturn;
+
+ break;
+ }
+
+ //
+ // Make sure to acquire the lock for the new irp.
+ //
+
+ ClassAcquireRemoveLock(DeviceObject, irp2);
+
+ irp2->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
+ IoSetNextIrpStackLocation(irp2);
+
+ //
+ // Set the top stack location and shove the master Irp into the
+ // top location
+ //
+
+ newStack = IoGetCurrentIrpStackLocation(irp2);
+ newStack->Parameters.Others.Argument1 = Irp;
+ newStack->DeviceObject = DeviceObject;
+
+ //
+ // Stick the check verify completion routine onto the stack
+ // and prepare the irp for the port driver
+ //
+
+ IoSetCompletionRoutine(irp2,
+ ClassCheckVerifyComplete,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoSetNextIrpStackLocation(irp2);
+ newStack = IoGetCurrentIrpStackLocation(irp2);
+ newStack->DeviceObject = DeviceObject;
+ newStack->MajorFunction = irpStack->MajorFunction;
+ newStack->MinorFunction = irpStack->MinorFunction;
+
+ //
+ // Mark the master irp as pending - whether the lower level
+ // driver completes it immediately or not this should allow it
+ // to go all the way back up.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ Irp = irp2;
+
+ }
+
+ //
+ // Test Unit Ready
+ //
+
+ srb->CdbLength = 6;
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
+ //
+ // If this was a CV2 then mark the request as low-priority so we don't
+ // spin up the drive just to satisfy it.
+ //
+
+ if(controlCode == IOCTL_STORAGE_CHECK_VERIFY2) {
+ SET_FLAG(srb->SrbFlags, SRB_CLASS_FLAGS_LOW_PRIORITY);
+ }
+
+ //
+ // Since this routine will always hand the request to the
+ // port driver if there isn't a data transfer to be done
+ // we don't have to worry about completing the request here
+ // on an error
+ //
+
+ //
+ // This routine uses a completion routine so we don't want to release
+ // the remove lock until then.
+ //
+
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ srb,
+ Irp,
+ NULL,
+ 0,
+ FALSE);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_MEDIA_REMOVAL:
+ case IOCTL_STORAGE_EJECTION_CONTROL: {
+
+ PPREVENT_MEDIA_REMOVAL mediaRemoval = Irp->AssociatedIrp.SystemBuffer;
+
+ DebugPrint((3, "DiskIoControl: ejection control\n"));
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(PREVENT_MEDIA_REMOVAL)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto SetStatusAndReturn;
+ }
+
+ if(!commonExtension->IsFdo) {
+
+ //
+ // Just forward this down and return
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+ else {
+
+ // i don't believe this assertion is valid. this is a request
+ // from user-mode, so they could request this for any device
+ // they want? also, we handle it properly.
+ // ASSERT(TEST_FLAG(DeviceObject->Characteristics, FILE_REMOVABLE_MEDIA));
+ status = ClasspEjectionControl(
+ DeviceObject,
+ Irp,
+ ((modifiedIoControlCode ==
+ IOCTL_STORAGE_EJECTION_CONTROL) ? SecureMediaLock :
+ SimpleMediaLock),
+ mediaRemoval->PreventMediaRemoval);
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+
+ break;
+ }
+
+ case IOCTL_STORAGE_MCN_CONTROL: {
+
+ DebugPrint((3, "DiskIoControl: MCN control\n"));
+
+ if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
+ sizeof(PREVENT_MEDIA_REMOVAL)) {
+
+ //
+ // Indicate unsuccessful status and no data transferred.
+ //
+
+ Irp->IoStatus.Status = STATUS_INFO_LENGTH_MISMATCH;
+ Irp->IoStatus.Information = 0;
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ status = STATUS_INFO_LENGTH_MISMATCH;
+ goto SetStatusAndReturn;
+ }
+
+ if(!commonExtension->IsFdo) {
+
+ //
+ // Just forward this down and return
+ //
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ } else {
+
+ //
+ // Call to the FDO - handle the ejection control.
+ //
+
+ status = ClasspMcnControl(DeviceObject->DeviceExtension,
+ Irp,
+ srb);
+ }
+ goto SetStatusAndReturn;
+ }
+
+ case IOCTL_STORAGE_RESERVE:
+ case IOCTL_STORAGE_RELEASE: {
+
+ //
+ // Reserve logical unit.
+ //
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
+
+ if(!commonExtension->IsFdo) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+ } else {
+ fdoExtension = DeviceObject->DeviceExtension;
+ }
+
+ srb->CdbLength = 6;
+
+ if(modifiedIoControlCode == IOCTL_STORAGE_RESERVE) {
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_RESERVE_UNIT;
+ } else {
+ cdb->CDB6GENERIC.OperationCode = SCSIOP_RELEASE_UNIT;
+ }
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ srb,
+ Irp,
+ NULL,
+ 0,
+ FALSE);
+
+ break;
+ }
+
+ case IOCTL_STORAGE_EJECT_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA:
+ case IOCTL_STORAGE_LOAD_MEDIA2:{
+
+ //
+ // Eject media.
+ //
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
+
+ if(!commonExtension->IsFdo) {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ goto SetStatusAndReturn;
+ } else {
+ fdoExtension = DeviceObject->DeviceExtension;
+ }
+
+ if(commonExtension->PagingPathCount != 0) {
+
+ DebugPrint((1, "ClassDeviceControl: call to eject paging device - "
+ "failure\n"));
+
+ status = STATUS_FILES_OPEN;
+ Irp->IoStatus.Status = status;
+
+ Irp->IoStatus.Information = 0;
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ goto SetStatusAndReturn;
+ }
+
+ //
+ // Synchronize with ejection control and ejection cleanup code as
+ // well as other eject/load requests.
+ //
+
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
+ UserRequest,
+ UserMode,
+ FALSE,
+ NULL);
+
+ if(fdoExtension->ProtectedLockCount != 0) {
+
+ DebugPrint((1, "ClassDeviceControl: call to eject protected locked "
+ "device - failure\n"));
+
+ status = STATUS_DEVICE_BUSY;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+ KeLeaveCriticalRegion();
+
+ goto SetStatusAndReturn;
+ }
+
+ srb->CdbLength = 6;
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.LoadEject = 1;
+
+ if(modifiedIoControlCode == IOCTL_STORAGE_EJECT_MEDIA) {
+ cdb->START_STOP.Start = 0;
+ } else {
+ cdb->START_STOP.Start = 1;
+ }
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = fdoExtension->TimeOutValue;
+ status = ClassSendSrbAsynchronous(DeviceObject,
+ srb,
+ Irp,
+ NULL,
+ 0,
+ FALSE);
+
+ KeSetEvent(&fdoExtension->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+
+ break;
+ }
+
+ case IOCTL_STORAGE_FIND_NEW_DEVICES: {
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ if(commonExtension->IsFdo) {
+
+ IoInvalidateDeviceRelations(
+ ((PFUNCTIONAL_DEVICE_EXTENSION) commonExtension)->LowerPdo,
+ BusRelations);
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Status = status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ }
+ else {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ }
+ break;
+ }
+
+ case IOCTL_STORAGE_GET_DEVICE_NUMBER: {
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >=
+ sizeof(STORAGE_DEVICE_NUMBER)) {
+
+ PSTORAGE_DEVICE_NUMBER deviceNumber =
+ Irp->AssociatedIrp.SystemBuffer;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ commonExtension->PartitionZeroExtension;
+
+ deviceNumber->DeviceType = fdoExtension->CommonExtension.DeviceObject->DeviceType;
+ deviceNumber->DeviceNumber = fdoExtension->DeviceNumber;
+ deviceNumber->PartitionNumber = commonExtension->PartitionNumber;
+
+ status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
+
+ } else {
+ status = STATUS_BUFFER_TOO_SMALL;
+ Irp->IoStatus.Information = sizeof(STORAGE_DEVICE_NUMBER);
+ }
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ break;
+ }
+
+ default: {
+
+ DebugPrint((4, "IoDeviceControl: Unsupported device IOCTL %x for %p\n",
+ controlCode, DeviceObject));
+
+ //
+ // Pass the device control to the next driver.
+ //
+
+ if(srb) {
+ ExFreePool(srb);
+ }
+
+ //
+ // Copy the Irp stack parameters to the next stack location.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ break;
+ }
+
+ } // end switch( ...
+
+SetStatusAndReturn:
+
+ DBGTRACE(ClassDebugTrace, ("< ioctl %xh (%s): status %xh.", modifiedIoControlCode, DBGGETIOCTLSTR(modifiedIoControlCode), status));
+
+ return status;
+} // end ClassDeviceControl()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassShutdownFlush()
+
+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.
+ If it exists, the device-specific driver's routine will be invoked. If there
+ wasn't one specified, the Irp will be completed with an Invalid device request.
+
+Arguments:
+
+ DriverObject - Pointer to device object to being shutdown by system.
+
+ Irp - IRP involved.
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS
+ClassShutdownFlush(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ ULONG isRemoved;
+
+ NTSTATUS status;
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ if(isRemoved) {
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ if (commonExtension->DevInfo->ClassShutdownFlush) {
+
+ //
+ // Call the device-specific driver's routine.
+ //
+
+ return commonExtension->DevInfo->ClassShutdownFlush(DeviceObject, Irp);
+ }
+
+ //
+ // Device-specific driver doesn't support this.
+ //
+
+ Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return STATUS_INVALID_DEVICE_REQUEST;
+} // end ClassShutdownFlush()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCreateDeviceObject()
+
+Routine Description:
+
+ This routine creates an object for the physical device specified and
+ sets up the deviceExtension's function pointers for each entry point
+ in the device-specific driver.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ ObjectNameBuffer - Dir. name of the object to create.
+
+ LowerDeviceObject - Pointer to the lower device object
+
+ IsFdo - should this be an fdo or a pdo
+
+ DeviceObject - Pointer to the device object pointer we will return.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+NTSTATUS
+ClassCreateDeviceObject(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PCCHAR ObjectNameBuffer,
+ IN PDEVICE_OBJECT LowerDevice,
+ IN BOOLEAN IsFdo,
+ IN OUT PDEVICE_OBJECT *DeviceObject
+ )
+{
+ BOOLEAN isPartitionable;
+ STRING ntNameString;
+ UNICODE_STRING ntUnicodeString;
+ NTSTATUS status, status2;
+ PDEVICE_OBJECT deviceObject = NULL;
+
+ ULONG characteristics;
+
+ PCLASS_DRIVER_EXTENSION
+ driverExtension = IoGetDriverObjectExtension(DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+
+ PCLASS_DEV_INFO devInfo;
+
+ PAGED_CODE();
+
+ *DeviceObject = NULL;
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+
+ DebugPrint((2, "ClassCreateFdo: Create device object\n"));
+
+ ASSERT(LowerDevice);
+
+ //
+ // Make sure that if we're making PDO's we have an enumeration routine
+ //
+
+ isPartitionable = (driverExtension->InitData.ClassEnumerateDevice != NULL);
+
+ ASSERT(IsFdo || isPartitionable);
+
+ //
+ // Grab the correct dev-info structure out of the init data
+ //
+
+ if(IsFdo) {
+ devInfo = &(driverExtension->InitData.FdoData);
+ } else {
+ devInfo = &(driverExtension->InitData.PdoData);
+ }
+
+ characteristics = devInfo->DeviceCharacteristics;
+
+ if(ARGUMENT_PRESENT(ObjectNameBuffer)) {
+ DebugPrint((2, "ClassCreateFdo: Name is %s\n", ObjectNameBuffer));
+
+ RtlInitString(&ntNameString, ObjectNameBuffer);
+
+ status = RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE);
+
+ if (!NT_SUCCESS(status)) {
+
+ DebugPrint((1,
+ "ClassCreateFdo: Cannot convert string %s\n",
+ ObjectNameBuffer));
+
+ ntUnicodeString.Buffer = NULL;
+ return status;
+ }
+ } else {
+ DebugPrint((2, "ClassCreateFdo: Object will be unnamed\n"));
+
+ if(IsFdo == FALSE) {
+
+ //
+ // PDO's have to have some sort of name.
+ //
+
+ SET_FLAG(characteristics, FILE_AUTOGENERATED_DEVICE_NAME);
+ }
+
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+ }
+
+ status = IoCreateDevice(DriverObject,
+ devInfo->DeviceExtensionSize,
+ &ntUnicodeString,
+ devInfo->DeviceType,
+ devInfo->DeviceCharacteristics,
+ FALSE,
+ &deviceObject);
+
+ if (!NT_SUCCESS(status)) {
+
+ DebugPrint((1, "ClassCreateFdo: Can not create device object %lx\n",
+ status));
+ ASSERT(deviceObject == NULL);
+
+ //
+ // buffer is not used any longer here.
+ //
+
+ if (ntUnicodeString.Buffer != NULL) {
+ DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
+ ExFreePool(ntUnicodeString.Buffer);
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+ }
+
+ } else {
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = deviceObject->DeviceExtension;
+
+ RtlZeroMemory(
+ deviceObject->DeviceExtension,
+ devInfo->DeviceExtensionSize);
+
+ //
+ // Setup version code
+ //
+
+ commonExtension->Version = 0x03;
+
+ //
+ // Setup the remove lock and event
+ //
+
+ commonExtension->IsRemoved = NO_REMOVE;
+ commonExtension->RemoveLock = 0;
+ KeInitializeEvent(&commonExtension->RemoveEvent,
+ SynchronizationEvent,
+ FALSE);
+
+ #if DBG
+ KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
+ commonExtension->RemoveTrackingList = NULL;
+ #else
+ commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
+ commonExtension->RemoveTrackingList = (PVOID) -1;
+ #endif
+
+ //
+ // Acquire the lock once. This reference will be released when the
+ // remove IRP has been received.
+ //
+
+ ClassAcquireRemoveLock(deviceObject, (PIRP) deviceObject);
+
+ //
+ // Store a pointer to the driver extension so we don't have to do
+ // lookups to get it.
+ //
+
+ commonExtension->DriverExtension = driverExtension;
+
+ //
+ // Fill in entry points
+ //
+
+ commonExtension->DevInfo = devInfo;
+
+ //
+ // Initialize some of the common values in the structure
+ //
+
+ commonExtension->DeviceObject = deviceObject;
+
+ commonExtension->LowerDeviceObject = NULL;
+
+ if(IsFdo) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = (PVOID) commonExtension;
+
+ commonExtension->PartitionZeroExtension = deviceObject->DeviceExtension;
+
+ //
+ // Set the initial device object flags.
+ //
+
+ SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
+
+ //
+ // Clear the PDO list
+ //
+
+ commonExtension->ChildList = NULL;
+
+ commonExtension->DriverData =
+ ((PFUNCTIONAL_DEVICE_EXTENSION) deviceObject->DeviceExtension + 1);
+
+ if(isPartitionable) {
+
+ commonExtension->PartitionNumber = 0;
+ } else {
+ commonExtension->PartitionNumber = (ULONG) (-1L);
+ }
+
+ fdoExtension->DevicePowerState = PowerDeviceD0;
+
+ KeInitializeEvent(&fdoExtension->EjectSynchronizationEvent,
+ SynchronizationEvent,
+ TRUE);
+
+ KeInitializeEvent(&fdoExtension->ChildLock,
+ SynchronizationEvent,
+ TRUE);
+
+ status = ClasspAllocateReleaseRequest(deviceObject);
+
+ if(!NT_SUCCESS(status)) {
+ IoDeleteDevice(deviceObject);
+ *DeviceObject = NULL;
+
+ if (ntUnicodeString.Buffer != NULL) {
+ DebugPrint((1, "ClassCreateFdo: Freeing unicode name buffer\n"));
+ ExFreePool(ntUnicodeString.Buffer);
+ RtlInitUnicodeString(&ntUnicodeString, NULL);
+ }
+
+ return status;
+ }
+
+ } else {
+
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension =
+ deviceObject->DeviceExtension;
+
+ PFUNCTIONAL_DEVICE_EXTENSION p0Extension =
+ LowerDevice->DeviceExtension;
+
+ SET_FLAG(deviceObject->Flags, DO_POWER_PAGABLE);
+
+ commonExtension->PartitionZeroExtension = p0Extension;
+
+ //
+ // Stick this onto the PDO list
+ //
+
+ ClassAddChild(p0Extension, pdoExtension, TRUE);
+
+ commonExtension->DriverData = (PVOID) (pdoExtension + 1);
+
+ //
+ // Get the top of stack for the lower device - this allows
+ // filters to get stuck in between the partitions and the
+ // physical disk.
+ //
+
+ commonExtension->LowerDeviceObject =
+ IoGetAttachedDeviceReference(LowerDevice);
+
+ //
+ // Pnp will keep a reference to the lower device object long
+ // after this partition has been deleted. Dereference now so
+ // we don't have to deal with it later.
+ //
+
+ ObDereferenceObject(commonExtension->LowerDeviceObject);
+ }
+
+ KeInitializeEvent(&commonExtension->PathCountEvent, SynchronizationEvent, TRUE);
+
+ commonExtension->IsFdo = IsFdo;
+
+ commonExtension->DeviceName = ntUnicodeString;
+
+ commonExtension->PreviousState = 0xff;
+
+ InitializeDictionary(&(commonExtension->FileObjectDictionary));
+
+ commonExtension->CurrentState = IRP_MN_STOP_DEVICE;
+ }
+
+ *DeviceObject = deviceObject;
+
+ return status;
+} // end ClassCreateDeviceObject()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassClaimDevice()
+
+Routine Description:
+
+ This function claims a device in the port driver. The port driver object
+ is updated with the correct driver object if the device is successfully
+ claimed.
+
+Arguments:
+
+ LowerDeviceObject - Supplies the base port device object.
+
+ Release - Indicates the logical unit should be released rather than claimed.
+
+Return Value:
+
+ Returns a status indicating success or failure of the operation.
+
+--*/
+NTSTATUS
+ClassClaimDevice(
+ IN PDEVICE_OBJECT LowerDeviceObject,
+ IN BOOLEAN Release
+ )
+{
+ IO_STATUS_BLOCK ioStatus;
+ PIRP irp;
+ PIO_STACK_LOCATION irpStack;
+ KEVENT event;
+ NTSTATUS status;
+ SCSI_REQUEST_BLOCK srb;
+
+ PAGED_CODE();
+
+ //
+ // Clear the SRB fields.
+ //
+
+ RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ //
+ // Write length to SRB.
+ //
+
+ srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ srb.Function = Release ? SRB_FUNCTION_RELEASE_DEVICE :
+ SRB_FUNCTION_CLAIM_DEVICE;
+
+ //
+ // Set the event object to the unsignaled state.
+ // It will be used to signal request completion
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ //
+ // Build synchronous request with no transfer.
+ //
+
+ irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_NONE,
+ LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ TRUE,
+ &event,
+ &ioStatus);
+
+ if (irp == NULL) {
+ DebugPrint((1, "ClassClaimDevice: Can't allocate Irp\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = &srb;
+
+ //
+ // Set up IRP Address.
+ //
+
+ srb.OriginalRequest = irp;
+
+ //
+ // Call the port driver with the request and wait for it to complete.
+ //
+
+ status = IoCallDriver(LowerDeviceObject, irp);
+ if (status == STATUS_PENDING) {
+
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = ioStatus.Status;
+ }
+
+ //
+ // If this is a release request, then just decrement the reference count
+ // and return. The status does not matter.
+ //
+
+ if (Release) {
+
+ // ObDereferenceObject(LowerDeviceObject);
+ return STATUS_SUCCESS;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ ASSERT(srb.DataBuffer != NULL);
+ ASSERT(!TEST_FLAG(srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ return status;
+} // end ClassClaimDevice()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInternalIoControl()
+
+Routine Description:
+
+ This routine passes internal device controls to the port driver.
+ Internal device controls are used by higher level drivers both for ioctls
+ and to pass through scsi requests.
+
+ If the IoControlCode does not match any of the handled ioctls and is
+ a valid system address then the request will be treated as an SRB and
+ passed down to the lower driver. If the IoControlCode is not a valid
+ system address the ioctl will be failed.
+
+ Callers must therefore be extremely cautious to pass correct, initialized
+ values to this function.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ Returns back a STATUS_PENDING or a completion status.
+
+--*/
+NTSTATUS
+ClassInternalIoControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
+
+ ULONG isRemoved;
+
+ PSCSI_REQUEST_BLOCK srb;
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ if(isRemoved) {
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ //
+ // Get a pointer to the SRB.
+ //
+
+ srb = irpStack->Parameters.Scsi.Srb;
+
+ //
+ // Set the parameters in the next stack location.
+ //
+
+ if(commonExtension->IsFdo) {
+ nextStack->Parameters.Scsi.Srb = srb;
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+ nextStack->MinorFunction = IRP_MN_SCSI_CLASS;
+
+ } else {
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ }
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ return IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+} // end ClassInternalIoControl()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassQueryTimeOutRegistryValue()
+
+Routine Description:
+
+ This routine determines whether a reg key for a user-specified timeout
+ value exists. This should be called at initialization time.
+
+Arguments:
+
+ DeviceObject - Pointer to the device object we are retrieving the timeout
+ value for
+
+Return Value:
+
+ None, but it sets a new default timeout for a class of devices.
+
+--*/
+ULONG
+ClassQueryTimeOutRegistryValue(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+{
+ //
+ // Find the appropriate reg. key
+ //
+
+ PCLASS_DRIVER_EXTENSION
+ driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+
+ PUNICODE_STRING registryPath = &(driverExtension->RegistryPath);
+
+ PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
+ PWSTR path;
+ NTSTATUS status;
+ LONG timeOut = 0;
+ ULONG zero = 0;
+ ULONG size;
+
+ PAGED_CODE();
+
+ if (!registryPath) {
+ return 0;
+ }
+
+ parameters = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(RTL_QUERY_REGISTRY_TABLE)*2,
+ '1BcS');
+
+ if (!parameters) {
+ return 0;
+ }
+
+ size = registryPath->MaximumLength + sizeof(WCHAR);
+ path = ExAllocatePoolWithTag(NonPagedPool, size, '2BcS');
+
+ if (!path) {
+ ExFreePool(parameters);
+ return 0;
+ }
+
+ RtlZeroMemory(path,size);
+ RtlCopyMemory(path, registryPath->Buffer, size - sizeof(WCHAR));
+
+
+ //
+ // Check for the Timeout value.
+ //
+
+ RtlZeroMemory(parameters,
+ (sizeof(RTL_QUERY_REGISTRY_TABLE)*2));
+
+ parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ parameters[0].Name = L"TimeOutValue";
+ parameters[0].EntryContext = &timeOut;
+ parameters[0].DefaultType = REG_DWORD;
+ parameters[0].DefaultData = &zero;
+ parameters[0].DefaultLength = sizeof(ULONG);
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
+ path,
+ parameters,
+ NULL,
+ NULL);
+
+ if (!(NT_SUCCESS(status))) {
+ timeOut = 0;
+ }
+
+ ExFreePool(parameters);
+ ExFreePool(path);
+
+ DebugPrint((2,
+ "ClassQueryTimeOutRegistryValue: Timeout value %d\n",
+ timeOut));
+
+
+ return timeOut;
+
+} // end ClassQueryTimeOutRegistryValue()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCheckVerifyComplete() ISSUE-2000/02/18-henrygab - why public?!
+
+Routine Description:
+
+ This routine executes when the port driver has completed a check verify
+ ioctl. It will set the status of the master Irp, copy the media change
+ count and complete the request.
+
+Arguments:
+
+ Fdo - Supplies the functional device object which represents the logical unit.
+
+ Irp - Supplies the Irp which has completed.
+
+ Context - NULL
+
+Return Value:
+
+ NT status
+
+--*/
+NTSTATUS
+ClassCheckVerifyComplete(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PIRP originalIrp;
+
+ ASSERT_FDO(Fdo);
+
+ originalIrp = irpStack->Parameters.Others.Argument1;
+
+ //
+ // Copy the media change count and status
+ //
+
+ *((PULONG) (originalIrp->AssociatedIrp.SystemBuffer)) =
+ fdoExtension->MediaChangeCount;
+
+ DebugPrint((2, "ClassCheckVerifyComplete - Media change count for"
+ "device %d is %lx - saved as %lx\n",
+ fdoExtension->DeviceNumber,
+ fdoExtension->MediaChangeCount,
+ *((PULONG) originalIrp->AssociatedIrp.SystemBuffer)));
+
+ originalIrp->IoStatus.Status = Irp->IoStatus.Status;
+ originalIrp->IoStatus.Information = sizeof(ULONG);
+
+ ClassReleaseRemoveLock(Fdo, originalIrp);
+ ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
+
+ IoFreeIrp(Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // end ClassCheckVerifyComplete()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetDescriptor()
+
+Routine Description:
+
+ This routine will perform a query for the specified property id and will
+ allocate a non-paged buffer to store the data in. It is the responsibility
+ of the caller to ensure that this buffer is freed.
+
+ This routine must be run at IRQL_PASSIVE_LEVEL
+
+Arguments:
+
+ DeviceObject - the device to query
+ DeviceInfo - a location to store a pointer to the buffer we allocate
+
+Return Value:
+
+ status
+ if status is unsuccessful *DeviceInfo will be set to NULL, else the
+ buffer allocated on behalf of the caller.
+
+--*/
+NTSTATUS
+ClassGetDescriptor(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PSTORAGE_PROPERTY_ID PropertyId,
+ OUT PSTORAGE_DESCRIPTOR_HEADER *Descriptor
+ )
+{
+ STORAGE_PROPERTY_QUERY query;
+ IO_STATUS_BLOCK ioStatus;
+
+ PSTORAGE_DESCRIPTOR_HEADER descriptor = NULL;
+ ULONG length;
+
+ UCHAR pass = 0;
+
+ PAGED_CODE();
+
+ //
+ // Set the passed-in descriptor pointer to NULL as default
+ //
+
+ *Descriptor = NULL;
+
+
+ RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
+ query.PropertyId = *PropertyId;
+ query.QueryType = PropertyStandardQuery;
+
+ //
+ // On the first pass we just want to get the first few
+ // bytes of the descriptor so we can read it's size
+ //
+
+ descriptor = (PVOID)&query;
+
+ ASSERT(sizeof(STORAGE_PROPERTY_QUERY) >= (sizeof(ULONG)*2));
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_STORAGE_QUERY_PROPERTY,
+ DeviceObject,
+ &query,
+ sizeof(STORAGE_PROPERTY_QUERY),
+ sizeof(ULONG) * 2,
+ FALSE,
+ &ioStatus
+ );
+
+ if(!NT_SUCCESS(ioStatus.Status)) {
+
+ DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
+ "query properties #1\n", ioStatus.Status));
+ return ioStatus.Status;
+ }
+
+ if (descriptor->Size == 0) {
+
+ //
+ // This DebugPrint is to help third-party driver writers
+ //
+
+ DebugPrint((0, "ClassGetDescriptor: size returned was zero?! (status "
+ "%x\n", ioStatus.Status));
+ return STATUS_UNSUCCESSFUL;
+
+ }
+
+ //
+ // This time we know how much data there is so we can
+ // allocate a buffer of the correct size
+ //
+
+ length = descriptor->Size;
+
+ descriptor = ExAllocatePoolWithTag(NonPagedPool, length, '4BcS');
+
+ if(descriptor == NULL) {
+
+ DebugPrint((1, "ClassGetDescriptor: unable to memory for descriptor "
+ "(%d bytes)\n", length));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // setup the query again, as it was overwritten above
+ //
+
+ RtlZeroMemory(&query, sizeof(STORAGE_PROPERTY_QUERY));
+ query.PropertyId = *PropertyId;
+ query.QueryType = PropertyStandardQuery;
+
+ //
+ // copy the input to the new outputbuffer
+ //
+
+ RtlCopyMemory(descriptor,
+ &query,
+ sizeof(STORAGE_PROPERTY_QUERY)
+ );
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_STORAGE_QUERY_PROPERTY,
+ DeviceObject,
+ descriptor,
+ sizeof(STORAGE_PROPERTY_QUERY),
+ length,
+ FALSE,
+ &ioStatus
+ );
+
+ if(!NT_SUCCESS(ioStatus.Status)) {
+
+ DebugPrint((1, "ClassGetDescriptor: error %lx trying to "
+ "query properties #1\n", ioStatus.Status));
+ ExFreePool(descriptor);
+ return ioStatus.Status;
+ }
+
+ //
+ // return the memory we've allocated to the caller
+ //
+
+ *Descriptor = descriptor;
+ return ioStatus.Status;
+} // end ClassGetDescriptor()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSignalCompletion()
+
+Routine Description:
+
+ This completion routine will signal the event given as context and then
+ return STATUS_MORE_PROCESSING_REQUIRED to stop event completion. It is
+ the responsibility of the routine waiting on the event to complete the
+ request and free the event.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object
+
+ Irp - a pointer to the irp
+
+ Event - a pointer to the event to signal
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED
+
+--*/
+NTSTATUS
+ClassSignalCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PKEVENT Event
+ )
+{
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClassSignalCompletion()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassPnpQueryFdoRelations()
+
+Routine Description:
+
+ This routine will call the driver's enumeration routine to update the
+ list of PDO's. It will then build a response to the
+ IRP_MN_QUERY_DEVICE_RELATIONS and place it into the information field in
+ the irp.
+
+Arguments:
+
+ Fdo - a pointer to the functional device object we are enumerating
+
+ Irp - a pointer to the enumeration request
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassPnpQueryFdoRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_DRIVER_EXTENSION
+ driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // If there's already an enumeration in progress then don't start another
+ // one.
+ //
+
+ if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
+ status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
+ }
+
+ Irp->IoStatus.Information = (ULONG_PTR) NULL;
+
+ Irp->IoStatus.Status = ClassRetrieveDeviceRelations(
+ Fdo,
+ BusRelations,
+ (PDEVICE_RELATIONS)&Irp->IoStatus.Information);
+ InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
+
+ return Irp->IoStatus.Status;
+} // end ClassPnpQueryFdoRelations()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassMarkChildrenMissing()
+
+Routine Description:
+
+ This routine will call ClassMarkChildMissing() for all children.
+ It acquires the ChildLock before calling ClassMarkChildMissing().
+
+Arguments:
+
+ Fdo - the "bus's" device object, such as the disk FDO for non-removable
+ disks with multiple partitions.
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClassMarkChildrenMissing(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Fdo
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = &(Fdo->CommonExtension);
+ PPHYSICAL_DEVICE_EXTENSION nextChild = commonExtension->ChildList;
+
+ PAGED_CODE();
+
+ ClassAcquireChildLock(Fdo);
+
+ while (nextChild){
+ PPHYSICAL_DEVICE_EXTENSION tmpChild;
+
+ /*
+ * ClassMarkChildMissing will also dequeue the child extension.
+ * So get the next pointer before calling ClassMarkChildMissing.
+ */
+ tmpChild = nextChild;
+ nextChild = tmpChild->CommonExtension.ChildList;
+ ClassMarkChildMissing(tmpChild, FALSE);
+ }
+ ClassReleaseChildLock(Fdo);
+ return;
+} // end ClassMarkChildrenMissing()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassMarkChildMissing()
+
+Routine Description:
+
+ This routine will make an active child "missing." If the device has never
+ been enumerated then it will be deleted on the spot. If the device has
+ not been enumerated then it will be marked as missing so that we can
+ not report it in the next device enumeration.
+
+Arguments:
+
+ Child - the child device to be marked as missing.
+
+ AcquireChildLock - TRUE if the child lock should be acquired before removing
+ the missing child. FALSE if the child lock is already
+ acquired by this thread.
+
+Return Value:
+
+ returns whether or not the child device object has previously been reported
+ to PNP.
+
+--*/
+BOOLEAN
+ClassMarkChildMissing(
+ IN PPHYSICAL_DEVICE_EXTENSION Child,
+ IN BOOLEAN AcquireChildLock
+ )
+{
+ BOOLEAN returnValue = Child->IsEnumerated;
+
+ PAGED_CODE();
+ ASSERT_PDO(Child->DeviceObject);
+
+ Child->IsMissing = TRUE;
+
+ //
+ // Make sure this child is not in the active list.
+ //
+
+ ClassRemoveChild(Child->CommonExtension.PartitionZeroExtension,
+ Child,
+ AcquireChildLock);
+
+ if(Child->IsEnumerated == FALSE) {
+ ClassRemoveDevice(Child->DeviceObject, IRP_MN_REMOVE_DEVICE);
+ }
+
+ return returnValue;
+} // end ClassMarkChildMissing()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassRetrieveDeviceRelations()
+
+Routine Description:
+
+ This routine will allocate a buffer to hold the specified list of
+ relations. It will then fill in the list with referenced device pointers
+ and will return the request.
+
+Arguments:
+
+ Fdo - pointer to the FDO being queried
+
+ RelationType - what type of relations are being queried
+
+ DeviceRelations - a location to store a pointer to the response
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassRetrieveDeviceRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN DEVICE_RELATION_TYPE RelationType,
+ OUT PDEVICE_RELATIONS *DeviceRelations
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ ULONG count = 0;
+ ULONG i;
+
+ PPHYSICAL_DEVICE_EXTENSION nextChild;
+
+ ULONG relationsSize;
+ PDEVICE_RELATIONS deviceRelations = NULL;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ ClassAcquireChildLock(fdoExtension);
+
+ nextChild = fdoExtension->CommonExtension.ChildList;
+
+ //
+ // Count the number of PDO's attached to this disk
+ //
+
+ while(nextChild != NULL) {
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+
+ commonExtension = &(nextChild->CommonExtension);
+
+ ASSERTMSG("ClassPnp internal error: missing child on active list\n",
+ (nextChild->IsMissing == FALSE));
+
+ nextChild = commonExtension->ChildList;
+
+ count++;
+ };
+
+ relationsSize = (sizeof(DEVICE_RELATIONS) +
+ (count * sizeof(PDEVICE_OBJECT)));
+
+ deviceRelations = ExAllocatePoolWithTag(PagedPool, relationsSize, '5BcS');
+
+ if(deviceRelations == NULL) {
+
+ DebugPrint((1, "ClassRetrieveDeviceRelations: unable to allocate "
+ "%d bytes for device relations\n", relationsSize));
+
+ ClassReleaseChildLock(fdoExtension);
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(deviceRelations, relationsSize);
+
+ nextChild = fdoExtension->CommonExtension.ChildList;
+ i = count - 1;
+
+ while(nextChild != NULL) {
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+
+ commonExtension = &(nextChild->CommonExtension);
+
+ ASSERTMSG("ClassPnp internal error: missing child on active list\n",
+ (nextChild->IsMissing == FALSE));
+
+ deviceRelations->Objects[i--] = nextChild->DeviceObject;
+
+ status = ObReferenceObjectByPointer(
+ nextChild->DeviceObject,
+ 0,
+ NULL,
+ KernelMode);
+ ASSERT(NT_SUCCESS(status));
+
+ nextChild->IsEnumerated = TRUE;
+ nextChild = commonExtension->ChildList;
+ }
+
+ ASSERTMSG("Child list has changed: ", i == -1);
+
+ deviceRelations->Count = count;
+ *DeviceRelations = deviceRelations;
+ ClassReleaseChildLock(fdoExtension);
+ return STATUS_SUCCESS;
+} // end ClassRetrieveDeviceRelations()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetPdoId()
+
+Routine Description:
+
+ This routine will call into the driver to retrieve a copy of one of it's
+ id strings.
+
+Arguments:
+
+ Pdo - a pointer to the pdo being queried
+
+ IdType - which type of id string is being queried
+
+ IdString - an allocated unicode string structure which the driver
+ can fill in.
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassGetPdoId(
+ IN PDEVICE_OBJECT Pdo,
+ IN BUS_QUERY_ID_TYPE IdType,
+ IN PUNICODE_STRING IdString
+ )
+{
+ PCLASS_DRIVER_EXTENSION
+ driverExtension = IoGetDriverObjectExtension(Pdo->DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+
+ ASSERT_PDO(Pdo);
+ ASSERT(driverExtension->InitData.ClassQueryId);
+
+ PAGED_CODE();
+
+ return driverExtension->InitData.ClassQueryId( Pdo, IdType, IdString);
+} // end ClassGetPdoId()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassQueryPnpCapabilities()
+
+Routine Description:
+
+ This routine will call into the class driver to retrieve it's pnp
+ capabilities.
+
+Arguments:
+
+ PhysicalDeviceObject - The physical device object to retrieve properties
+ for.
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassQueryPnpCapabilities(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PDEVICE_CAPABILITIES Capabilities
+ )
+{
+ PCLASS_DRIVER_EXTENSION driverExtension =
+ ClassGetDriverExtension(DeviceObject->DriverObject);
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PCLASS_QUERY_PNP_CAPABILITIES queryRoutine = NULL;
+
+ PAGED_CODE();
+
+ ASSERT(DeviceObject);
+ ASSERT(Capabilities);
+
+ if(commonExtension->IsFdo) {
+ queryRoutine = driverExtension->InitData.FdoData.ClassQueryPnpCapabilities;
+ } else {
+ queryRoutine = driverExtension->InitData.PdoData.ClassQueryPnpCapabilities;
+ }
+
+ if(queryRoutine) {
+ return queryRoutine(DeviceObject,
+ Capabilities);
+ } else {
+ return STATUS_NOT_IMPLEMENTED;
+ }
+} // end ClassQueryPnpCapabilities()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInvalidateBusRelations()
+
+Routine Description:
+
+ This routine re-enumerates the devices on the "bus". It will call into
+ the driver's ClassEnumerate routine to update the device objects
+ immediately. It will then schedule a bus re-enumeration for pnp by calling
+ IoInvalidateDeviceRelations.
+
+Arguments:
+
+ Fdo - a pointer to the functional device object for this bus
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClassInvalidateBusRelations(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PCLASS_DRIVER_EXTENSION
+ driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ ASSERT_FDO(Fdo);
+ ASSERT(driverExtension->InitData.ClassEnumerateDevice != NULL);
+
+ if(InterlockedIncrement(&(fdoExtension->EnumerationInterlock)) == 1) {
+ status = driverExtension->InitData.ClassEnumerateDevice(Fdo);
+ }
+ InterlockedDecrement(&(fdoExtension->EnumerationInterlock));
+
+ if(!NT_SUCCESS(status)) {
+
+ DebugPrint((1, "ClassInvalidateBusRelations: EnumerateDevice routine "
+ "returned %lx\n", status));
+ }
+
+ IoInvalidateDeviceRelations(fdoExtension->LowerPdo, BusRelations);
+
+ return;
+} // end ClassInvalidateBusRelations()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassRemoveDevice() ISSUE-2000/02/18-henrygab - why public?!
+
+Routine Description:
+
+ This routine is called to handle the "removal" of a device. It will
+ forward the request downwards if necesssary, call into the driver
+ to release any necessary resources (memory, events, etc) and then
+ will delete the device object.
+
+Arguments:
+
+ DeviceObject - a pointer to the device object being removed
+
+ RemoveType - indicates what type of remove this is (regular or surprise).
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassRemoveDevice(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR RemoveType
+ )
+{
+ PCLASS_DRIVER_EXTENSION
+ driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
+ CLASS_DRIVER_EXTENSION_KEY);
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDEVICE_OBJECT lowerDeviceObject = commonExtension->LowerDeviceObject;
+ PCLASS_WMI_INFO classWmiInfo;
+ BOOLEAN proceedWithRemove = TRUE;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ commonExtension->IsRemoved = REMOVE_PENDING;
+
+ /*
+ * Deregister from WMI.
+ */
+ classWmiInfo = commonExtension->IsFdo ?
+ &driverExtension->InitData.FdoData.ClassWmiInfo :
+ &driverExtension->InitData.PdoData.ClassWmiInfo;
+ if (classWmiInfo->GuidRegInfo){
+ status = IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);
+ DBGTRACE(ClassDebugInfo, ("ClassRemoveDevice: IoWMIRegistrationControl(%p, WMI_ACTION_DEREGISTER) --> %lx", DeviceObject, status));
+ }
+
+ /*
+ * If we exposed a "shingle" (a named device interface openable by CreateFile)
+ * then delete it now.
+ */
+ if (commonExtension->MountedDeviceInterfaceName.Buffer){
+ IoSetDeviceInterfaceState(&commonExtension->MountedDeviceInterfaceName, FALSE);
+ RtlFreeUnicodeString(&commonExtension->MountedDeviceInterfaceName);
+ RtlInitUnicodeString(&commonExtension->MountedDeviceInterfaceName, NULL);
+ }
+
+ //
+ // If this is a surprise removal we leave the device around - which means
+ // we don't have to (or want to) drop the remove lock and wait for pending
+ // requests to complete.
+ //
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE){
+
+ //
+ // Release the lock we acquired when the device object was created.
+ //
+
+ ClassReleaseRemoveLock(DeviceObject, (PIRP) DeviceObject);
+
+ DebugPrint((1, "ClasspRemoveDevice - Reference count is now %d\n",
+ commonExtension->RemoveLock));
+
+ KeWaitForSingleObject(&commonExtension->RemoveEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ DebugPrint((1, "ClasspRemoveDevice - removing device %p\n", DeviceObject));
+
+ if(commonExtension->IsFdo) {
+
+ DebugPrint((1, "ClasspRemoveDevice - FDO %p has received a "
+ "remove request.\n", DeviceObject));
+
+ }
+ else {
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension = DeviceObject->DeviceExtension;
+
+ if (pdoExtension->IsMissing){
+ /*
+ * The child partition PDO is missing, so we are going to go ahead
+ * and delete it for the remove.
+ */
+ DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p is missing and will be removed", DeviceObject));
+ }
+ else {
+ /*
+ * We got a remove for a child partition PDO which is not actually missing.
+ * So we will NOT actually delete it.
+ */
+ DBGTRACE(ClassDebugWarning, ("ClasspRemoveDevice - PDO %p still exists and will be removed when it disappears", DeviceObject));
+
+ //
+ // Reacquire the remove lock for the next time this comes around.
+ //
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP) DeviceObject);
+
+ //
+ // the device wasn't missing so it's not really been removed.
+ //
+
+ commonExtension->IsRemoved = NO_REMOVE;
+
+ IoInvalidateDeviceRelations(
+ commonExtension->PartitionZeroExtension->LowerPdo,
+ BusRelations);
+
+ proceedWithRemove = FALSE;
+ }
+ }
+ }
+
+
+ if (proceedWithRemove){
+
+ /*
+ * Call the class driver's remove handler.
+ * All this is supposed to do is clean up its data and device interfaces.
+ */
+ ASSERT(commonExtension->DevInfo->ClassRemoveDevice);
+ status = commonExtension->DevInfo->ClassRemoveDevice(DeviceObject, RemoveType);
+ ASSERT(NT_SUCCESS(status));
+ status = STATUS_SUCCESS;
+
+ if (commonExtension->IsFdo){
+ PDEVICE_OBJECT pdo;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ ClasspDisableTimer(fdoExtension->DeviceObject);
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE){
+
+ PPHYSICAL_DEVICE_EXTENSION child;
+
+ //
+ // Cleanup the media detection resources now that the class driver
+ // has stopped it's timer (if any) and we can be sure they won't
+ // call us to do detection again.
+ //
+
+ ClassCleanupMediaChangeDetection(fdoExtension);
+
+ //
+ // Cleanup any Failure Prediction stuff
+ //
+ if (fdoExtension->FailurePredictionInfo) {
+ ExFreePool(fdoExtension->FailurePredictionInfo);
+ fdoExtension->FailurePredictionInfo = NULL;
+ }
+
+ /*
+ * Ordinarily all child PDOs will be removed by the time
+ * that the parent gets the REMOVE_DEVICE.
+ * However, if a child PDO has been created but has not
+ * been announced in a QueryDeviceRelations, then it is
+ * just a private data structure unknown to pnp, and we have
+ * to delete it ourselves.
+ */
+ ClassAcquireChildLock(fdoExtension);
+ while (child = ClassRemoveChild(fdoExtension, NULL, FALSE)){
+
+ //
+ // Yank the pdo. This routine will unlink the device from the
+ // pdo list so NextPdo will point to the next one when it's
+ // complete.
+ //
+ child->IsMissing = TRUE;
+ ClassRemoveDevice(child->DeviceObject, IRP_MN_REMOVE_DEVICE);
+ }
+ ClassReleaseChildLock(fdoExtension);
+ }
+ else if (RemoveType == IRP_MN_SURPRISE_REMOVAL){
+ /*
+ * This is a surprise-remove on the parent FDO.
+ * We will mark the child PDOs as missing so that they
+ * will actually get deleted when they get a REMOVE_DEVICE.
+ */
+ ClassMarkChildrenMissing(fdoExtension);
+ }
+
+ ClasspFreeReleaseRequest(DeviceObject);
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE){
+
+ //
+ // Free FDO-specific data structs
+ //
+ if (fdoExtension->PrivateFdoData){
+
+ DestroyAllTransferPackets(DeviceObject);
+
+ ExFreePool(fdoExtension->PrivateFdoData);
+ fdoExtension->PrivateFdoData = NULL;
+ }
+
+ if (commonExtension->DeviceName.Buffer) {
+ ExFreePool(commonExtension->DeviceName.Buffer);
+ RtlInitUnicodeString(&commonExtension->DeviceName, NULL);
+ }
+
+ if (fdoExtension->AdapterDescriptor) {
+ ExFreePool(fdoExtension->AdapterDescriptor);
+ fdoExtension->AdapterDescriptor = NULL;
+ }
+
+ if (fdoExtension->DeviceDescriptor) {
+ ExFreePool(fdoExtension->DeviceDescriptor);
+ fdoExtension->DeviceDescriptor = NULL;
+ }
+
+ //
+ // Detach our device object from the stack - there's no reason
+ // to hold off our cleanup any longer.
+ //
+
+ IoDetachDevice(lowerDeviceObject);
+ }
+ }
+ else {
+ /*
+ * This is a child partition PDO.
+ * We have already determined that it was previously marked
+ * as missing. So if this is a REMOVE_DEVICE, we will actually
+ * delete it.
+ */
+ if (RemoveType == IRP_MN_REMOVE_DEVICE){
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ commonExtension->PartitionZeroExtension;
+ PPHYSICAL_DEVICE_EXTENSION pdoExtension =
+ (PPHYSICAL_DEVICE_EXTENSION) commonExtension;
+
+ //
+ // See if this device is in the child list (if this was a suprise
+ // removal it might be) and remove it.
+ //
+
+ ClassRemoveChild(fdoExtension, pdoExtension, TRUE);
+ }
+ }
+
+ commonExtension->PartitionLength.QuadPart = 0;
+
+ if (RemoveType == IRP_MN_REMOVE_DEVICE){
+ IoDeleteDevice(DeviceObject);
+ }
+ }
+
+ return STATUS_SUCCESS;
+} // end ClassRemoveDevice()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetDriverExtension()
+
+Routine Description:
+
+ This routine will return the classpnp's driver extension.
+
+Arguments:
+
+ DriverObject - the driver object for which to get classpnp's extension
+
+Return Value:
+
+ Either NULL if none, or a pointer to the driver extension
+
+--*/
+PCLASS_DRIVER_EXTENSION
+ClassGetDriverExtension(
+ IN PDRIVER_OBJECT DriverObject
+ )
+{
+ return IoGetDriverObjectExtension(DriverObject, CLASS_DRIVER_EXTENSION_KEY);
+} // end ClassGetDriverExtension()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspStartIo()
+
+Routine Description:
+
+ This routine wraps the class driver's start io routine. If the device
+ is being removed it will complete any requests with
+ STATUS_DEVICE_DOES_NOT_EXIST and fire up the next packet.
+
+Arguments:
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClasspStartIo(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ //
+ // We're already holding the remove lock so just check the variable and
+ // see what's going on.
+ //
+
+ if(commonExtension->IsRemoved) {
+
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+
+ ClassAcquireRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT);
+ IoStartNextPacket(DeviceObject, FALSE);
+ ClassReleaseRemoveLock(DeviceObject, (PIRP) ClasspStartIo);
+ return;
+ }
+
+ commonExtension->DriverExtension->InitData.ClassStartIo(
+ DeviceObject,
+ Irp);
+
+ return;
+} // ClasspStartIo()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassUpdateInformationInRegistry()
+
+Routine Description:
+
+ This routine has knowledge about the layout of the device map information
+ in the registry. It will update this information to include a value
+ entry specifying the dos device name that is assumed to get assigned
+ to this NT device name. For more information on this assigning of the
+ dos device name look in the drive support routine in the hal that assigns
+ all dos names.
+
+ Since some versions of some device's firmware did not work and some
+ vendors did not bother to follow the specification, the entire inquiry
+ information must also be stored in the registry so than someone can
+ figure out the firmware version.
+
+Arguments:
+
+ DeviceObject - A pointer to the device object for the tape device.
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClassUpdateInformationInRegistry(
+ IN PDEVICE_OBJECT Fdo,
+ IN PCHAR DeviceName,
+ IN ULONG DeviceNumber,
+ IN PINQUIRYDATA InquiryData,
+ IN ULONG InquiryDataLength
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ NTSTATUS status;
+ SCSI_ADDRESS scsiAddress;
+ OBJECT_ATTRIBUTES objectAttributes;
+ PUCHAR buffer;
+ STRING string;
+ UNICODE_STRING unicodeName;
+ UNICODE_STRING unicodeRegistryPath;
+ UNICODE_STRING unicodeData;
+ HANDLE targetKey;
+ IO_STATUS_BLOCK ioStatus;
+
+
+ PAGED_CODE();
+
+ ASSERT(DeviceName);
+ fdoExtension = Fdo->DeviceExtension;
+ buffer = NULL;
+ targetKey = NULL;
+ RtlZeroMemory(&unicodeName, sizeof(UNICODE_STRING));
+ RtlZeroMemory(&unicodeData, sizeof(UNICODE_STRING));
+ RtlZeroMemory(&unicodeRegistryPath, sizeof(UNICODE_STRING));
+
+ TRY {
+
+ //
+ // Issue GET_ADDRESS Ioctl to determine path, target, and lun information.
+ //
+
+ ClassSendDeviceIoControlSynchronous(
+ IOCTL_SCSI_GET_ADDRESS,
+ Fdo,
+ &scsiAddress,
+ 0,
+ sizeof(SCSI_ADDRESS),
+ FALSE,
+ &ioStatus
+ );
+
+ if (!NT_SUCCESS(ioStatus.Status)) {
+
+ status = ioStatus.Status;
+ DebugPrint((1,
+ "UpdateInformationInRegistry: Get Address failed %lx\n",
+ status));
+ LEAVE;
+
+ } else {
+
+ DebugPrint((1,
+ "GetAddress: Port %x, Path %x, Target %x, Lun %x\n",
+ scsiAddress.PortNumber,
+ scsiAddress.PathId,
+ scsiAddress.TargetId,
+ scsiAddress.Lun));
+
+ }
+
+ //
+ // Allocate a buffer for the reg. spooge.
+ //
+
+ buffer = ExAllocatePoolWithTag(PagedPool, 1024, '6BcS');
+
+ if (buffer == NULL) {
+
+ //
+ // There is not return value for this. Since this is done at
+ // claim device time (currently only system initialization) getting
+ // the registry information correct will be the least of the worries.
+ //
+
+ LEAVE;
+ }
+
+ sprintf(buffer,
+ "\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d",
+ scsiAddress.PortNumber,
+ scsiAddress.PathId,
+ scsiAddress.TargetId,
+ scsiAddress.Lun);
+
+ RtlInitString(&string, buffer);
+
+ status = RtlAnsiStringToUnicodeString(&unicodeRegistryPath,
+ &string,
+ TRUE);
+
+ if (!NT_SUCCESS(status)) {
+ LEAVE;
+ }
+
+ //
+ // Open the registry key for the scsi information for this
+ // scsibus, target, lun.
+ //
+
+ InitializeObjectAttributes(&objectAttributes,
+ &unicodeRegistryPath,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&targetKey,
+ KEY_READ | KEY_WRITE,
+ &objectAttributes);
+
+ if (!NT_SUCCESS(status)) {
+ LEAVE;
+ }
+
+ //
+ // Now construct and attempt to create the registry value
+ // specifying the device name in the appropriate place in the
+ // device map.
+ //
+
+ RtlInitUnicodeString(&unicodeName, L"DeviceName");
+
+ sprintf(buffer, "%s%d", DeviceName, DeviceNumber);
+ RtlInitString(&string, buffer);
+ status = RtlAnsiStringToUnicodeString(&unicodeData,
+ &string,
+ TRUE);
+ if (NT_SUCCESS(status)) {
+ status = ZwSetValueKey(targetKey,
+ &unicodeName,
+ 0,
+ REG_SZ,
+ unicodeData.Buffer,
+ unicodeData.Length);
+ }
+
+ //
+ // if they sent in data, update the registry
+ //
+
+ if (InquiryDataLength) {
+
+ ASSERT(InquiryData);
+
+ RtlInitUnicodeString(&unicodeName, L"InquiryData");
+ status = ZwSetValueKey(targetKey,
+ &unicodeName,
+ 0,
+ REG_BINARY,
+ InquiryData,
+ InquiryDataLength);
+ }
+
+ // that's all, except to clean up.
+
+ } FINALLY {
+
+ if (unicodeData.Buffer) {
+ RtlFreeUnicodeString(&unicodeData);
+ }
+ if (unicodeRegistryPath.Buffer) {
+ RtlFreeUnicodeString(&unicodeRegistryPath);
+ }
+ if (targetKey) {
+ ZwClose(targetKey);
+ }
+ if (buffer) {
+ ExFreePool(buffer);
+ }
+
+ }
+
+} // end ClassUpdateInformationInRegistry()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspSendSynchronousCompletion()
+
+Routine Description:
+
+ This completion routine will set the user event in the irp after
+ freeing the irp and the associated MDL (if any).
+
+Arguments:
+
+ DeviceObject - the device object which requested the completion routine
+
+ Irp - the irp being completed
+
+ Context - unused
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED
+
+--*/
+NTSTATUS
+ClasspSendSynchronousCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ DebugPrint((3, "ClasspSendSynchronousCompletion: %p %p %p\n",
+ DeviceObject, Irp, Context));
+ //
+ // First set the status and information fields in the io status block
+ // provided by the caller.
+ //
+
+ *(Irp->UserIosb) = Irp->IoStatus;
+
+ //
+ // Unlock the pages for the data buffer.
+ //
+
+ if(Irp->MdlAddress) {
+ MmUnlockPages(Irp->MdlAddress);
+ IoFreeMdl(Irp->MdlAddress);
+ }
+
+ //
+ // Signal the caller's event.
+ //
+
+ KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
+
+ //
+ // Free the MDL and the IRP.
+ //
+
+ IoFreeIrp(Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClasspSendSynchronousCompletion()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspRegisterMountedDeviceInterface
+
+--*/
+VOID
+ClasspRegisterMountedDeviceInterface(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+{
+
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ BOOLEAN isFdo = commonExtension->IsFdo;
+
+ PDEVICE_OBJECT pdo;
+ UNICODE_STRING interfaceName;
+
+ NTSTATUS status;
+
+ if(isFdo) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION functionalExtension;
+
+ functionalExtension =
+ (PFUNCTIONAL_DEVICE_EXTENSION) commonExtension;
+ pdo = functionalExtension->LowerPdo;
+ } else {
+ pdo = DeviceObject;
+ }
+
+ status = IoRegisterDeviceInterface(
+ pdo,
+ &MOUNTDEV_MOUNTED_DEVICE_GUID,
+ NULL,
+ &interfaceName
+ );
+
+ if(NT_SUCCESS(status)) {
+
+ //
+ // Copy the interface name before setting the interface state - the
+ // name is needed by the components we notify.
+ //
+
+ commonExtension->MountedDeviceInterfaceName = interfaceName;
+ status = IoSetDeviceInterfaceState(&interfaceName, TRUE);
+
+ if(!NT_SUCCESS(status)) {
+ RtlFreeUnicodeString(&interfaceName);
+ }
+ }
+
+ if(!NT_SUCCESS(status)) {
+ RtlInitUnicodeString(&(commonExtension->MountedDeviceInterfaceName),
+ NULL);
+ }
+ return;
+} // end ClasspRegisterMountedDeviceInterface()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendDeviceIoControlSynchronous()
+
+Routine Description:
+
+ This routine is based upon IoBuildDeviceIoControlRequest(). It has been
+ modified to reduce code and memory by not double-buffering the io, using
+ the same buffer for both input and output, allocating and deallocating
+ the mdl on behalf of the caller, and waiting for the io to complete.
+
+ This routine also works around the rare cases in which APC's are disabled.
+ Since IoBuildDeviceIoControl() used APC's to signal completion, this had
+ led to a number of difficult-to-detect hangs, where the irp was completed,
+ but the event passed to IoBuild..() was still being waited upon by the
+ caller.
+
+Arguments:
+
+ IoControlCode - the IOCTL to send
+
+ TargetDeviceObject - the device object that should handle the ioctl
+
+ Buffer - the input and output buffer, or NULL if no input/output
+
+ InputBufferLength - the number of bytes prepared for the IOCTL in Buffer
+
+ OutputBufferLength - the number of bytes to be filled in upon success
+
+ InternalDeviceIoControl - if TRUE, uses IRP_MJ_INTERNAL_DEVICE_CONTROL
+
+ IoStatus - the status block that contains the results of the operation
+
+Return Value:
+
+--*/
+VOID
+ClassSendDeviceIoControlSynchronous(
+ IN ULONG IoControlCode,
+ IN PDEVICE_OBJECT TargetDeviceObject,
+ IN OUT PVOID Buffer OPTIONAL,
+ IN ULONG InputBufferLength,
+ IN ULONG OutputBufferLength,
+ IN BOOLEAN InternalDeviceIoControl,
+ OUT PIO_STATUS_BLOCK IoStatus
+ )
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG method;
+
+ PAGED_CODE();
+
+ irp = NULL;
+ method = IoControlCode & 3;
+
+
+ #if DBG // Begin Argument Checking (nop in fre version)
+
+ ASSERT(ARGUMENT_PRESENT(IoStatus));
+
+ if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
+ ASSERT(ARGUMENT_PRESENT(Buffer));
+ }
+ else {
+ ASSERT(!ARGUMENT_PRESENT(Buffer));
+ }
+ #endif
+
+ //
+ // Begin by allocating the IRP for this request. Do not charge quota to
+ // the current process for this IRP.
+ //
+
+ irp = IoAllocateIrp(TargetDeviceObject->StackSize, FALSE);
+ if (!irp) {
+ (*IoStatus).Information = 0;
+ (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ //
+ // Get a pointer to the stack location of the first driver which will be
+ // invoked. This is where the function codes and the parameters are set.
+ //
+
+ irpSp = IoGetNextIrpStackLocation(irp);
+
+ //
+ // Set the major function code based on the type of device I/O control
+ // function the caller has specified.
+ //
+
+ if (InternalDeviceIoControl) {
+ irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
+ } else {
+ irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
+ }
+
+ //
+ // Copy the caller's parameters to the service-specific portion of the
+ // IRP for those parameters that are the same for all four methods.
+ //
+
+ irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
+ irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
+ irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
+
+ //
+ // Get the method bits from the I/O control code to determine how the
+ // buffers are to be passed to the driver.
+ //
+
+ switch (method) {
+ // case 0
+ case METHOD_BUFFERED: {
+ if ((InputBufferLength != 0) || (OutputBufferLength != 0)) {
+
+ irp->AssociatedIrp.SystemBuffer =
+ ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
+ max(InputBufferLength, OutputBufferLength),
+ CLASS_TAG_DEVICE_CONTROL
+ );
+
+ if (irp->AssociatedIrp.SystemBuffer == NULL) {
+ IoFreeIrp(irp);
+ (*IoStatus).Information = 0;
+ (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ if (InputBufferLength != 0) {
+ RtlCopyMemory(irp->AssociatedIrp.SystemBuffer,
+ Buffer,
+ InputBufferLength);
+ }
+ } // end of buffering
+
+ irp->UserBuffer = Buffer;
+ break;
+ }
+
+ // case 1, case 2
+ case METHOD_IN_DIRECT:
+ case METHOD_OUT_DIRECT: {
+
+
+ if (InputBufferLength != 0) {
+ irp->AssociatedIrp.SystemBuffer = Buffer;
+ }
+
+ if (OutputBufferLength != 0) {
+
+ irp->MdlAddress = IoAllocateMdl(Buffer,
+ OutputBufferLength,
+ FALSE, FALSE,
+ (PIRP) NULL);
+
+ if (irp->MdlAddress == NULL) {
+ IoFreeIrp(irp);
+ (*IoStatus).Information = 0;
+ (*IoStatus).Status = STATUS_INSUFFICIENT_RESOURCES;
+ return;
+ }
+
+ if (method == METHOD_IN_DIRECT) {
+ MmProbeAndLockPages(irp->MdlAddress,
+ KernelMode,
+ IoReadAccess);
+ } else if (method == METHOD_OUT_DIRECT) {
+ MmProbeAndLockPages(irp->MdlAddress,
+ KernelMode,
+ IoWriteAccess);
+ } else {
+ ASSERT(!"If other methods reach here, code is out of date");
+ }
+ }
+ break;
+ }
+
+ // case 3
+ case METHOD_NEITHER: {
+
+ ASSERT(!"This routine does not support METHOD_NEITHER ioctls");
+ IoStatus->Information = 0;
+ IoStatus->Status = STATUS_NOT_SUPPORTED;
+ return;
+ break;
+ }
+ } // end of switch(method)
+
+ irp->Tail.Overlay.Thread = PsGetCurrentThread();
+
+ //
+ // send the irp synchronously
+ //
+
+ ClassSendIrpSynchronous(TargetDeviceObject, irp);
+
+ //
+ // copy the iostatus block for the caller
+ //
+
+ *IoStatus = irp->IoStatus;
+
+ //
+ // free any allocated resources
+ //
+
+ switch (method) {
+ case METHOD_BUFFERED: {
+
+ ASSERT(irp->UserBuffer == Buffer);
+
+ //
+ // first copy the buffered result, if any
+ // Note that there are no security implications in
+ // not checking for success since only drivers can
+ // call into this routine anyways...
+ //
+
+ if (OutputBufferLength != 0) {
+ RtlCopyMemory(Buffer, // irp->UserBuffer
+ irp->AssociatedIrp.SystemBuffer,
+ OutputBufferLength
+ );
+ }
+
+ //
+ // then free the memory allocated to buffer the io
+ //
+
+ if ((InputBufferLength !=0) || (OutputBufferLength != 0)) {
+ ExFreePool(irp->AssociatedIrp.SystemBuffer);
+ irp->AssociatedIrp.SystemBuffer = NULL;
+ }
+ break;
+ }
+
+ case METHOD_IN_DIRECT:
+ case METHOD_OUT_DIRECT: {
+
+ //
+ // we alloc a mdl if there is an output buffer specified
+ // free it here after unlocking the pages
+ //
+
+ if (OutputBufferLength != 0) {
+ ASSERT(irp->MdlAddress != NULL);
+ MmUnlockPages(irp->MdlAddress);
+ IoFreeMdl(irp->MdlAddress);
+ irp->MdlAddress = (PMDL) NULL;
+ }
+ break;
+ }
+
+ case METHOD_NEITHER: {
+ ASSERT(!"Code is out of date");
+ break;
+ }
+ }
+
+ //
+ // we always have allocated an irp. free it here.
+ //
+
+ IoFreeIrp(irp);
+ irp = (PIRP) NULL;
+
+ //
+ // return the io status block's status to the caller
+ //
+
+ return;
+} // end ClassSendDeviceIoControlSynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassForwardIrpSynchronous()
+
+Routine Description:
+
+ Forwards a given irp to the next lower device object.
+
+Arguments:
+
+ CommonExtension - the common class extension
+
+ Irp - the request to forward down the stack
+
+Return Value:
+
+--*/
+NTSTATUS
+ClassForwardIrpSynchronous(
+ IN PCOMMON_DEVICE_EXTENSION CommonExtension,
+ IN PIRP Irp
+ )
+{
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ return ClassSendIrpSynchronous(CommonExtension->LowerDeviceObject, Irp);
+} // end ClassForwardIrpSynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSendIrpSynchronous()
+
+Routine Description:
+
+ This routine sends the given irp to the given device object, and waits for
+ it to complete. On debug versions, will print out a debug message and
+ optionally assert for "lost" irps based upon classpnp's globals
+
+Arguments:
+
+ TargetDeviceObject - the device object to handle this irp
+
+ Irp - the request to be sent
+
+Return Value:
+
+--*/
+NTSTATUS
+ClassSendIrpSynchronous(
+ IN PDEVICE_OBJECT TargetDeviceObject,
+ IN PIRP Irp
+ )
+{
+ KEVENT event;
+ NTSTATUS status;
+
+ ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
+ ASSERT(TargetDeviceObject != NULL);
+ ASSERT(Irp != NULL);
+ ASSERT(Irp->StackCount >= TargetDeviceObject->StackSize);
+
+ //
+ // ISSUE-2000/02/20-henrygab What if APCs are disabled?
+ // May need to enter critical section before IoCallDriver()
+ // until the event is hit?
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ IoSetCompletionRoutine(Irp, ClassSignalCompletion, &event,
+ TRUE, TRUE, TRUE);
+
+ status = IoCallDriver(TargetDeviceObject, Irp);
+
+ if (status == STATUS_PENDING) {
+
+ #if DBG
+ LARGE_INTEGER timeout;
+
+ timeout.QuadPart = (LONGLONG)(-1 * 10 * 1000 * (LONGLONG)1000 *
+ ClasspnpGlobals.SecondsToWaitForIrps);
+
+ do {
+ status = KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ &timeout);
+
+
+ if (status == STATUS_TIMEOUT) {
+
+ //
+ // This DebugPrint should almost always be investigated by the
+ // party who sent the irp and/or the current owner of the irp.
+ // Synchronous Irps should not take this long (currently 30
+ // seconds) without good reason. This points to a potentially
+ // serious problem in the underlying device stack.
+ //
+
+ DebugPrint((0, "ClassSendIrpSynchronous: (%p) irp %p did not "
+ "complete within %x seconds\n",
+ TargetDeviceObject, Irp,
+ ClasspnpGlobals.SecondsToWaitForIrps
+ ));
+
+ if (ClasspnpGlobals.BreakOnLostIrps != 0) {
+ ASSERT(!" - Irp failed to complete within 30 seconds - ");
+ }
+ }
+
+
+ } while (status==STATUS_TIMEOUT);
+ #else
+ KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ #endif
+
+ status = Irp->IoStatus.Status;
+ }
+
+ return status;
+} // end ClassSendIrpSynchronous()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassGetVpb()
+
+Routine Description:
+
+ This routine returns the current VPB (Volume Parameter Block) for the
+ given device object.
+ The Vpb field is only visible in the ntddk.h (not the wdm.h) definition
+ of DEVICE_OBJECT; hence this exported function.
+
+Arguments:
+
+ DeviceObject - the device to get the VPB for
+
+Return Value:
+
+ the VPB, or NULL if none.
+
+--*/
+PVPB
+ClassGetVpb(
+ IN PDEVICE_OBJECT DeviceObject
+ )
+{
+ return DeviceObject->Vpb;
+} // end ClassGetVpb()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspAllocateReleaseRequest
+
+--*/
+NTSTATUS
+ClasspAllocateReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PIO_STACK_LOCATION irpStack;
+
+ KeInitializeSpinLock(&(fdoExtension->ReleaseQueueSpinLock));
+
+ fdoExtension->ReleaseQueueNeeded = FALSE;
+ fdoExtension->ReleaseQueueInProgress = FALSE;
+ fdoExtension->ReleaseQueueIrpFromPool = FALSE;
+
+ //
+ // The class driver is responsible for allocating a properly sized irp,
+ // or ClassReleaseQueue will attempt to do it on the first error.
+ //
+
+ fdoExtension->ReleaseQueueIrp = NULL;
+
+ //
+ // Write length to SRB.
+ //
+
+ fdoExtension->ReleaseQueueSrb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ return STATUS_SUCCESS;
+} // end ClasspAllocateReleaseRequest()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspFreeReleaseRequest
+
+--*/
+VOID
+ClasspFreeReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ //KIRQL oldIrql;
+
+ ASSERT(fdoExtension->CommonExtension.IsRemoved != NO_REMOVE);
+
+ //
+ // free anything the driver allocated
+ //
+
+ if (fdoExtension->ReleaseQueueIrp) {
+ if (fdoExtension->ReleaseQueueIrpFromPool) {
+ ExFreePool(fdoExtension->ReleaseQueueIrp);
+ } else {
+ IoFreeIrp(fdoExtension->ReleaseQueueIrp);
+ }
+ fdoExtension->ReleaseQueueIrp = NULL;
+ }
+
+ //
+ // free anything that we allocated
+ //
+
+ if ((fdoExtension->PrivateFdoData) &&
+ (fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated)) {
+
+ ExFreePool(fdoExtension->PrivateFdoData->ReleaseQueueIrp);
+ fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = FALSE;
+ fdoExtension->PrivateFdoData->ReleaseQueueIrp = NULL;
+ }
+
+ return;
+} // end ClasspFreeReleaseRequest()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseQueue()
+
+Routine Description:
+
+ This routine issues an internal device control command
+ to the port driver to release a frozen queue. The call
+ is issued asynchronously as ClassReleaseQueue will be invoked
+ from the IO completion DPC (and will have no context to
+ wait for a synchronous call to complete).
+
+ This routine must be called with the remove lock held.
+
+Arguments:
+
+ Fdo - The functional device object for the device with the frozen queue.
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassReleaseQueue(
+ IN PDEVICE_OBJECT Fdo
+ )
+{
+ ClasspReleaseQueue(Fdo, NULL);
+ return;
+} // end ClassReleaseQueue()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspAllocateReleaseQueueIrp()
+
+Routine Description:
+
+ This routine allocates the release queue irp held in classpnp's private
+ extension. This was added to allow no-memory conditions to be more
+ survivable.
+
+Return Value:
+
+ NT_SUCCESS value.
+
+Notes:
+
+ Does not grab the spinlock. Should only be called from StartDevice()
+ routine. May be called elsewhere for poorly-behaved drivers that cause
+ the queue to lockup before the device is started. This should *never*
+ occur, since it's illegal to send a request to a non-started PDO. This
+ condition is checked for in ClasspReleaseQueue().
+
+--*/
+NTSTATUS
+ClasspAllocateReleaseQueueIrp(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ KIRQL oldIrql;
+ UCHAR lowerStackSize;
+
+ //
+ // do an initial check w/o the spinlock
+ //
+
+ if (FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
+ return STATUS_SUCCESS;
+ }
+
+
+ lowerStackSize = FdoExtension->CommonExtension.LowerDeviceObject->StackSize;
+
+ //
+ // don't allocate one if one is in progress! this means whoever called
+ // this routine didn't check if one was in progress.
+ //
+
+ ASSERT(!(FdoExtension->ReleaseQueueInProgress));
+
+ FdoExtension->PrivateFdoData->ReleaseQueueIrp =
+ ExAllocatePoolWithTag(NonPagedPool,
+ IoSizeOfIrp(lowerStackSize),
+ CLASS_TAG_RELEASE_QUEUE
+ );
+
+ if (FdoExtension->PrivateFdoData->ReleaseQueueIrp == NULL) {
+ DebugPrint((0, "ClassPnpStartDevice: Cannot allocate for "
+ "release queue irp\n"));
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ IoInitializeIrp(FdoExtension->PrivateFdoData->ReleaseQueueIrp,
+ IoSizeOfIrp(lowerStackSize),
+ lowerStackSize);
+ FdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated = TRUE;
+
+ return STATUS_SUCCESS;
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspReleaseQueue()
+
+Routine Description:
+
+ This routine issues an internal device control command
+ to the port driver to release a frozen queue. The call
+ is issued asynchronously as ClassReleaseQueue will be invoked
+ from the IO completion DPC (and will have no context to
+ wait for a synchronous call to complete).
+
+ This routine must be called with the remove lock held.
+
+Arguments:
+
+ Fdo - The functional device object for the device with the frozen queue.
+
+ ReleaseQueueIrp - If this irp is supplied then the test to determine whether
+ a release queue request is in progress will be ignored.
+ The irp provided must be the IRP originally allocated
+ for release queue requests (so this parameter can only
+ really be provided by the release queue completion
+ routine.)
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClasspReleaseQueue(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP ReleaseQueueIrp OPTIONAL
+ )
+{
+ PIO_STACK_LOCATION irpStack;
+ PIRP irp;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+ PDEVICE_OBJECT lowerDevice;
+ PSCSI_REQUEST_BLOCK srb;
+ KIRQL currentIrql;
+
+ lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject;
+
+ //
+ // we raise irql seperately so we're not swapped out or suspended
+ // while holding the release queue irp in this routine. this lets
+ // us release the spin lock before lowering irql.
+ //
+
+ KeRaiseIrql(DISPATCH_LEVEL, ¤tIrql);
+
+ KeAcquireSpinLockAtDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
+
+ //
+ // make sure that if they passed us an irp, it matches our allocated irp.
+ //
+
+ ASSERT((ReleaseQueueIrp == NULL) ||
+ (ReleaseQueueIrp == fdoExtension->PrivateFdoData->ReleaseQueueIrp));
+
+ //
+ // ASSERT that we've already allocated this. (should not occur)
+ // try to allocate it anyways, then finally bugcheck if
+ // there's still no memory...
+ //
+
+ ASSERT(fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated);
+ if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
+ ClasspAllocateReleaseQueueIrp(fdoExtension);
+ }
+ if (!fdoExtension->PrivateFdoData->ReleaseQueueIrpAllocated) {
+ KeBugCheckEx(SCSI_DISK_DRIVER_INTERNAL, 0x12, (ULONG_PTR)Fdo, 0x0, 0x0);
+ }
+
+ if ((fdoExtension->ReleaseQueueInProgress) && (ReleaseQueueIrp == NULL)) {
+
+ //
+ // Someone is already using the irp - just set the flag to indicate that
+ // we need to release the queue again.
+ //
+
+ fdoExtension->ReleaseQueueNeeded = TRUE;
+ KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
+ KeLowerIrql(currentIrql);
+ return;
+
+ }
+
+ //
+ // Mark that there is a release queue in progress and drop the spinlock.
+ //
+
+ fdoExtension->ReleaseQueueInProgress = TRUE;
+ if (ReleaseQueueIrp) {
+ irp = ReleaseQueueIrp;
+ } else {
+ irp = fdoExtension->PrivateFdoData->ReleaseQueueIrp;
+ }
+ srb = &(fdoExtension->ReleaseQueueSrb);
+
+ KeReleaseSpinLockFromDpcLevel(&(fdoExtension->ReleaseQueueSpinLock));
+
+ ASSERT(irp != NULL);
+
+ irpStack = IoGetNextIrpStackLocation(irp);
+
+ irpStack->MajorFunction = IRP_MJ_SCSI;
+
+ srb->OriginalRequest = irp;
+
+ //
+ // Store the SRB address in next stack for port driver.
+ //
+
+ irpStack->Parameters.Scsi.Srb = srb;
+
+ //
+ // If this device is removable then flush the queue. This will also
+ // release it.
+ //
+
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)){
+ srb->Function = SRB_FUNCTION_FLUSH_QUEUE;
+ }
+ else {
+ srb->Function = SRB_FUNCTION_RELEASE_QUEUE;
+ }
+
+ ClassAcquireRemoveLock(Fdo, irp);
+
+ IoSetCompletionRoutine(irp,
+ ClassReleaseQueueCompletion,
+ Fdo,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoCallDriver(lowerDevice, irp);
+
+ KeLowerIrql(currentIrql);
+
+ return;
+
+} // end ClassReleaseQueue()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseQueueCompletion()
+
+Routine Description:
+
+ This routine is called when an asynchronous I/O request
+ which was issused by the class driver completes. Examples of such requests
+ are release queue or START UNIT. This routine releases the queue if
+ necessary. It then frees the context and the IRP.
+
+Arguments:
+
+ DeviceObject - The device object for the logical unit; however since this
+ is the top stack location the value is NULL.
+
+ Irp - Supplies a pointer to the Irp to be processed.
+
+ Context - Supplies the context to be used to process this request.
+
+Return Value:
+
+ None.
+
+--*/
+NTSTATUS
+ClassReleaseQueueCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ KIRQL oldIrql;
+
+ BOOLEAN releaseQueueNeeded;
+
+ DeviceObject = Context;
+
+ fdoExtension = DeviceObject->DeviceExtension;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ //
+ // Grab the spinlock and clear the release queue in progress flag so others
+ // can run. Save (and clear) the state of the release queue needed flag
+ // so that we can issue a new release queue outside the spinlock.
+ //
+
+ KeAcquireSpinLock(&(fdoExtension->ReleaseQueueSpinLock), &oldIrql);
+
+ releaseQueueNeeded = fdoExtension->ReleaseQueueNeeded;
+
+ fdoExtension->ReleaseQueueNeeded = FALSE;
+ fdoExtension->ReleaseQueueInProgress = FALSE;
+
+ KeReleaseSpinLock(&(fdoExtension->ReleaseQueueSpinLock), oldIrql);
+
+ //
+ // If we need a release queue then issue one now. Another processor may
+ // have already started one in which case we'll try to issue this one after
+ // it is done - but we should never recurse more than one deep.
+ //
+
+ if(releaseQueueNeeded) {
+ ClasspReleaseQueue(DeviceObject, Irp);
+ }
+
+ //
+ // Indicate the I/O system should stop processing the Irp completion.
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // ClassAsynchronousCompletion()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAcquireChildLock()
+
+Routine Description:
+
+ This routine acquires the lock protecting children PDOs. It may be
+ acquired recursively by the same thread, but must be release by the
+ thread once for each acquisition.
+
+Arguments:
+
+ FdoExtension - the device whose child list is protected.
+
+Return Value:
+
+ None
+
+--*/
+VOID
+ClassAcquireChildLock(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PAGED_CODE();
+
+ if(FdoExtension->ChildLockOwner != KeGetCurrentThread()) {
+ KeWaitForSingleObject(&FdoExtension->ChildLock,
+ Executive, KernelMode,
+ FALSE, NULL);
+
+ ASSERT(FdoExtension->ChildLockOwner == NULL);
+ ASSERT(FdoExtension->ChildLockAcquisitionCount == 0);
+
+ FdoExtension->ChildLockOwner = KeGetCurrentThread();
+ } else {
+ ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
+ }
+
+ FdoExtension->ChildLockAcquisitionCount++;
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseChildLock() ISSUE-2000/02/18-henrygab - not documented
+
+Routine Description:
+
+ This routine releases the lock protecting children PDOs. It must be
+ called once for each time ClassAcquireChildLock was called.
+
+Arguments:
+
+ FdoExtension - the device whose child list is protected
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassReleaseChildLock(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ ASSERT(FdoExtension->ChildLockOwner == KeGetCurrentThread());
+ ASSERT(FdoExtension->ChildLockAcquisitionCount != 0);
+
+ FdoExtension->ChildLockAcquisitionCount -= 1;
+
+ if(FdoExtension->ChildLockAcquisitionCount == 0) {
+ FdoExtension->ChildLockOwner = NULL;
+ KeSetEvent(&FdoExtension->ChildLock, IO_NO_INCREMENT, FALSE);
+ }
+
+ return;
+} // end ClassReleaseChildLock(
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAddChild()
+
+Routine Description:
+
+ This routine will insert a new child into the head of the child list.
+
+Arguments:
+
+ Parent - the child's parent (contains the head of the list)
+ Child - the child to be inserted.
+ AcquireLock - whether the child lock should be acquired (TRUE) or whether
+ it's already been acquired by or on behalf of the caller
+ (FALSE).
+
+Return Value:
+
+ None.
+
+--*/
+VOID
+ClassAddChild(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ IN PPHYSICAL_DEVICE_EXTENSION Child,
+ IN BOOLEAN AcquireLock
+ )
+{
+ if(AcquireLock) {
+ ClassAcquireChildLock(Parent);
+ }
+
+ #if DBG
+ //
+ // Make sure this child's not already in the list.
+ //
+ {
+ PPHYSICAL_DEVICE_EXTENSION testChild;
+
+ for (testChild = Parent->CommonExtension.ChildList;
+ testChild != NULL;
+ testChild = testChild->CommonExtension.ChildList) {
+
+ ASSERT(testChild != Child);
+ }
+ }
+ #endif
+
+ Child->CommonExtension.ChildList = Parent->CommonExtension.ChildList;
+ Parent->CommonExtension.ChildList = Child;
+
+ if(AcquireLock) {
+ ClassReleaseChildLock(Parent);
+ }
+ return;
+} // end ClassAddChild()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassRemoveChild()
+
+Routine Description:
+
+ This routine will remove a child from the child list.
+
+Arguments:
+
+ Parent - the parent to be removed from.
+
+ Child - the child to be removed or NULL if the first child should be
+ removed.
+
+ AcquireLock - whether the child lock should be acquired (TRUE) or whether
+ it's already been acquired by or on behalf of the caller
+ (FALSE).
+
+Return Value:
+
+ A pointer to the child which was removed or NULL if no such child could
+ be found in the list (or if Child was NULL but the list is empty).
+
+--*/
+PPHYSICAL_DEVICE_EXTENSION
+ClassRemoveChild(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ IN PPHYSICAL_DEVICE_EXTENSION Child,
+ IN BOOLEAN AcquireLock
+ )
+{
+ if(AcquireLock) {
+ ClassAcquireChildLock(Parent);
+ }
+
+ TRY {
+ PCOMMON_DEVICE_EXTENSION previousChild = &Parent->CommonExtension;
+
+ //
+ // If the list is empty then bail out now.
+ //
+
+ if(Parent->CommonExtension.ChildList == NULL) {
+ Child = NULL;
+ LEAVE;
+ }
+
+ //
+ // If the caller specified a child then find the child object before
+ // it. If none was specified then the FDO is the child object before
+ // the one we want to remove.
+ //
+
+ if(Child != NULL) {
+
+ //
+ // Scan through the child list to find the entry which points to
+ // this one.
+ //
+
+ do {
+ ASSERT(previousChild != &Child->CommonExtension);
+
+ if(previousChild->ChildList == Child) {
+ break;
+ }
+
+ previousChild = &previousChild->ChildList->CommonExtension;
+ } while(previousChild != NULL);
+
+ if(previousChild == NULL) {
+ Child = NULL;
+ LEAVE;
+ }
+ }
+
+ //
+ // Save the next child away then unlink it from the list.
+ //
+
+ Child = previousChild->ChildList;
+ previousChild->ChildList = Child->CommonExtension.ChildList;
+ Child->CommonExtension.ChildList = NULL;
+
+ } FINALLY {
+ if(AcquireLock) {
+ ClassReleaseChildLock(Parent);
+ }
+ }
+ return Child;
+} // end ClassRemoveChild()
+
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspRetryRequestDpc
+
+--*/
+VOID
+ClasspRetryRequestDpc(
+ IN PKDPC Dpc,
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PVOID Arg1,
+ IN PVOID Arg2
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCLASS_RETRY_INFO retryList;
+ KIRQL irql;
+
+
+ commonExtension = DeviceObject->DeviceExtension;
+ ASSERT(commonExtension->IsFdo);
+ fdoExtension = DeviceObject->DeviceExtension;
+ fdoData = fdoExtension->PrivateFdoData;
+
+
+ KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
+ {
+ LARGE_INTEGER now;
+ KeQueryTickCount(&now);
+
+ //
+ // if CurrentTick is less than now
+ // fire another DPC
+ // else
+ // retry entire list
+ // endif
+ //
+
+ if (now.QuadPart < fdoData->Retry.Tick.QuadPart) {
+
+ ClasspRetryDpcTimer(fdoData);
+ retryList = NULL;
+
+ } else {
+
+ retryList = fdoData->Retry.ListHead;
+ fdoData->Retry.ListHead = NULL;
+ fdoData->Retry.Delta.QuadPart = (LONGLONG)0;
+ fdoData->Retry.Tick.QuadPart = (LONGLONG)0;
+
+ }
+ }
+ KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
+
+ while (retryList != NULL) {
+
+ PIRP irp;
+
+ irp = CONTAINING_RECORD(retryList, IRP, Tail.Overlay.DriverContext[0]);
+ DebugPrint((ClassDebugDelayedRetry, "ClassRetry: -- %p\n", irp));
+ retryList = retryList->Next;
+ #if DBG
+ irp->Tail.Overlay.DriverContext[0] = ULongToPtr(0xdddddddd); // invalidate data
+ irp->Tail.Overlay.DriverContext[1] = ULongToPtr(0xdddddddd); // invalidate data
+ irp->Tail.Overlay.DriverContext[2] = ULongToPtr(0xdddddddd); // invalidate data
+ irp->Tail.Overlay.DriverContext[3] = ULongToPtr(0xdddddddd); // invalidate data
+ #endif
+
+ IoCallDriver(commonExtension->LowerDeviceObject, irp);
+
+ }
+ return;
+
+} // end ClasspRetryRequestDpc()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClassRetryRequest
+
+--*/
+VOID
+ClassRetryRequest(
+ IN PDEVICE_OBJECT SelfDeviceObject,
+ IN PIRP Irp,
+ IN LARGE_INTEGER TimeDelta100ns // in 100ns units
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData;
+ PCLASS_RETRY_INFO retryInfo;
+ PCLASS_RETRY_INFO *previousNext;
+ LARGE_INTEGER delta;
+ KIRQL irql;
+
+ //
+ // this checks we aren't destroying irps
+ //
+ ASSERT(sizeof(CLASS_RETRY_INFO) <= (4*sizeof(PVOID)));
+
+ fdoExtension = SelfDeviceObject->DeviceExtension;
+ fdoData = fdoExtension->PrivateFdoData;
+
+ if (!fdoExtension->CommonExtension.IsFdo) {
+
+ //
+ // this debug print/assertion should ALWAYS be investigated.
+ // ClassRetryRequest can currently only be used by FDO's
+ //
+
+ DebugPrint((ClassDebugError, "ClassRetryRequestEx: LOST IRP %p\n", Irp));
+ ASSERT(!"ClassRetryRequestEx Called From PDO? LOST IRP");
+ return;
+
+ }
+
+ if (TimeDelta100ns.QuadPart < 0) {
+ ASSERT(!"ClassRetryRequest - must use positive delay");
+ TimeDelta100ns.QuadPart *= -1;
+ }
+
+ //
+ // prepare what we can out of the loop
+ //
+
+ retryInfo = (PCLASS_RETRY_INFO)(&Irp->Tail.Overlay.DriverContext[0]);
+ RtlZeroMemory(retryInfo, sizeof(CLASS_RETRY_INFO));
+
+ delta.QuadPart = (TimeDelta100ns.QuadPart / fdoData->Retry.Granularity);
+ if (TimeDelta100ns.QuadPart % fdoData->Retry.Granularity) {
+ delta.QuadPart ++; // round up to next tick
+ }
+ if (delta.QuadPart == (LONGLONG)0) {
+ delta.QuadPart = MINIMUM_RETRY_UNITS;
+ }
+
+ //
+ // now determine if we should fire another DPC or not
+ //
+
+ KeAcquireSpinLock(&fdoData->Retry.Lock, &irql);
+
+ //
+ // always add request to the list
+ //
+
+ retryInfo->Next = fdoData->Retry.ListHead;
+ fdoData->Retry.ListHead = retryInfo;
+
+ if (fdoData->Retry.Delta.QuadPart == (LONGLONG)0) {
+
+ DebugPrint((ClassDebugDelayedRetry, "ClassRetry: +++ %p\n", Irp));
+
+ //
+ // must be exactly one item on list
+ //
+
+ ASSERT(fdoData->Retry.ListHead != NULL);
+ ASSERT(fdoData->Retry.ListHead->Next == NULL);
+
+ //
+ // if currentDelta is zero, always fire a DPC
+ //
+
+ KeQueryTickCount(&fdoData->Retry.Tick);
+ fdoData->Retry.Tick.QuadPart += delta.QuadPart;
+ fdoData->Retry.Delta.QuadPart = delta.QuadPart;
+ ClasspRetryDpcTimer(fdoData);
+
+ } else if (delta.QuadPart > fdoData->Retry.Delta.QuadPart) {
+
+ //
+ // if delta is greater than the list's current delta,
+ // increase the DPC handling time by difference
+ // and update the delta to new larger value
+ // allow the DPC to re-fire itself if needed
+ //
+
+ DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
+
+ //
+ // must be at least two items on list
+ //
+
+ ASSERT(fdoData->Retry.ListHead != NULL);
+ ASSERT(fdoData->Retry.ListHead->Next != NULL);
+
+ fdoData->Retry.Tick.QuadPart -= fdoData->Retry.Delta.QuadPart;
+ fdoData->Retry.Tick.QuadPart += delta.QuadPart;
+
+ fdoData->Retry.Delta.QuadPart = delta.QuadPart;
+
+ } else {
+
+ //
+ // just inserting it on the list was enough
+ //
+
+ DebugPrint((ClassDebugDelayedRetry, "ClassRetry: ++ %p\n", Irp));
+
+ }
+
+
+ KeReleaseSpinLock(&fdoData->Retry.Lock, irql);
+
+
+} // end ClassRetryRequest()
+
+/*++
+
+ ISSUE-2000/02/20-henrygab Not documented ClasspRetryDpcTimer
+
+--*/
+VOID
+ClasspRetryDpcTimer(
+ IN PCLASS_PRIVATE_FDO_DATA FdoData
+ )
+{
+ LARGE_INTEGER fire;
+
+ ASSERT(FdoData->Retry.Tick.QuadPart != (LONGLONG)0);
+ ASSERT(FdoData->Retry.ListHead != NULL); // never fire an empty list
+
+ //
+ // fire == (CurrentTick - now) * (100ns per tick)
+ //
+ // NOTE: Overflow is nearly impossible and is ignored here
+ //
+
+ KeQueryTickCount(&fire);
+ fire.QuadPart = FdoData->Retry.Tick.QuadPart - fire.QuadPart;
+ fire.QuadPart *= FdoData->Retry.Granularity;
+
+ //
+ // fire is now multiples of 100ns until should fire the timer.
+ // if timer should already have expired, or would fire too quickly,
+ // fire it in some arbitrary number of ticks to prevent infinitely
+ // recursing.
+ //
+
+ if (fire.QuadPart < MINIMUM_RETRY_UNITS) {
+ fire.QuadPart = MINIMUM_RETRY_UNITS;
+ }
+
+ DebugPrint((ClassDebugDelayedRetry,
+ "ClassRetry: ======= %I64x ticks\n",
+ fire.QuadPart));
+
+ //
+ // must use negative to specify relative time to fire
+ //
+
+ fire.QuadPart = fire.QuadPart * ((LONGLONG)-1);
+
+ //
+ // set the timer, since this is the first addition
+ //
+
+ KeSetTimerEx(&FdoData->Retry.Timer, fire, 0, &FdoData->Retry.Dpc);
+
+ return;
+} // end ClasspRetryDpcTimer()
+
+NTSTATUS
+ClasspInitializeHotplugInfo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ DEVICE_REMOVAL_POLICY deviceRemovalPolicy;
+ NTSTATUS status;
+ ULONG resultLength = 0;
+ ULONG writeCacheOverride;
+
+ PAGED_CODE();
+
+ //
+ // start with some default settings
+ //
+ RtlZeroMemory(&(fdoData->HotplugInfo), sizeof(STORAGE_HOTPLUG_INFO));
+
+ //
+ // set the size (aka version)
+ //
+
+ fdoData->HotplugInfo.Size = sizeof(STORAGE_HOTPLUG_INFO);
+
+ //
+ // set if the device has removable media
+ //
+
+ if (FdoExtension->DeviceDescriptor->RemovableMedia) {
+ fdoData->HotplugInfo.MediaRemovable = TRUE;
+ } else {
+ fdoData->HotplugInfo.MediaRemovable = FALSE;
+ }
+
+ //
+ // this refers to devices which, for reasons not yet understood,
+ // do not fail PREVENT_MEDIA_REMOVAL requests even though they
+ // have no way to lock the media into the drive. this allows
+ // the filesystems to turn off delayed-write caching for these
+ // devices as well.
+ //
+
+ if (TEST_FLAG(FdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_CANNOT_LOCK_MEDIA)) {
+ fdoData->HotplugInfo.MediaHotplug = TRUE;
+ } else {
+ fdoData->HotplugInfo.MediaHotplug = FALSE;
+ }
+
+
+ //
+ // Look into the registry to see if the user has chosen
+ // to override the default setting for the removal policy
+ //
+
+ RtlZeroMemory(&deviceRemovalPolicy, sizeof(DEVICE_REMOVAL_POLICY));
+
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_REMOVAL_POLICY_VALUE_NAME,
+ (PULONG)&deviceRemovalPolicy);
+
+ if (deviceRemovalPolicy == 0)
+ {
+ //
+ // Query the default removal policy from the kernel
+ //
+
+ status = IoGetDeviceProperty(FdoExtension->LowerPdo,
+ DevicePropertyRemovalPolicy,
+ sizeof(DEVICE_REMOVAL_POLICY),
+ (PVOID)&deviceRemovalPolicy,
+ &resultLength);
+ if (!NT_SUCCESS(status))
+ {
+ return status;
+ }
+
+ if (resultLength != sizeof(DEVICE_REMOVAL_POLICY))
+ {
+ return STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ //
+ // use this info to set the DeviceHotplug setting
+ // don't rely on DeviceCapabilities, since it can't properly
+ // determine device relations, etc. let the kernel figure this
+ // stuff out instead.
+ //
+
+ if (deviceRemovalPolicy == RemovalPolicyExpectSurpriseRemoval) {
+ fdoData->HotplugInfo.DeviceHotplug = TRUE;
+ } else {
+ fdoData->HotplugInfo.DeviceHotplug = FALSE;
+ }
+
+ //
+ // this refers to the *filesystem* caching, but has to be included
+ // here since it's a per-device setting. this may change to be
+ // stored by the system in the future.
+ //
+
+ writeCacheOverride = FALSE;
+ ClassGetDeviceParameter(FdoExtension,
+ CLASSP_REG_SUBKEY_NAME,
+ CLASSP_REG_WRITE_CACHE_VALUE_NAME,
+ &writeCacheOverride);
+
+ if (writeCacheOverride) {
+ fdoData->HotplugInfo.WriteCacheEnableOverride = TRUE;
+ } else {
+ fdoData->HotplugInfo.WriteCacheEnableOverride = FALSE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+ClasspScanForClassHacks(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG_PTR Data
+ )
+{
+ PAGED_CODE();
+
+ //
+ // remove invalid flags and save
+ //
+
+ CLEAR_FLAG(Data, FDO_HACK_INVALID_FLAGS);
+ SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, Data);
+ return;
+}
+
+VOID
+ClasspScanForSpecialInRegistry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ HANDLE deviceParameterHandle; // device instance key
+ HANDLE classParameterHandle; // classpnp subkey
+ OBJECT_ATTRIBUTES objectAttributes;
+ UNICODE_STRING subkeyName;
+ NTSTATUS status;
+
+ //
+ // seeded in the ENUM tree by ClassInstaller
+ //
+ ULONG deviceHacks;
+ RTL_QUERY_REGISTRY_TABLE queryTable[2]; // null terminated array
+
+ PAGED_CODE();
+
+ deviceParameterHandle = NULL;
+ classParameterHandle = NULL;
+ deviceHacks = 0;
+
+ status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_WRITE,
+ &deviceParameterHandle
+ );
+
+ if (!NT_SUCCESS(status)) {
+ goto cleanupScanForSpecial;
+ }
+
+ RtlInitUnicodeString(&subkeyName, CLASSP_REG_SUBKEY_NAME);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ NULL
+ );
+
+ status = ZwOpenKey( &classParameterHandle,
+ KEY_READ,
+ &objectAttributes
+ );
+
+ if (!NT_SUCCESS(status)) {
+ goto cleanupScanForSpecial;
+ }
+
+ //
+ // Zero out the memory
+ //
+
+ RtlZeroMemory(&queryTable[0], 2*sizeof(RTL_QUERY_REGISTRY_TABLE));
+
+ //
+ // Setup the structure to read
+ //
+
+ queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
+ queryTable[0].Name = CLASSP_REG_HACK_VALUE_NAME;
+ queryTable[0].EntryContext = &deviceHacks;
+ queryTable[0].DefaultType = REG_DWORD;
+ queryTable[0].DefaultData = &deviceHacks;
+ queryTable[0].DefaultLength = 0;
+
+ //
+ // read values
+ //
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PWSTR)classParameterHandle,
+ &queryTable[0],
+ NULL,
+ NULL
+ );
+ if (!NT_SUCCESS(status)) {
+ goto cleanupScanForSpecial;
+ }
+
+ //
+ // remove unknown values and save...
+ //
+
+ KdPrintEx((DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
+ "Classpnp => ScanForSpecial: HackFlags %#08x\n",
+ deviceHacks));
+
+ CLEAR_FLAG(deviceHacks, FDO_HACK_INVALID_FLAGS);
+ SET_FLAG(FdoExtension->PrivateFdoData->HackFlags, deviceHacks);
+
+
+cleanupScanForSpecial:
+
+ if (deviceParameterHandle) {
+ ZwClose(deviceParameterHandle);
+ }
+
+ if (classParameterHandle) {
+ ZwClose(classParameterHandle);
+ }
+
+ //
+ // we should modify the system hive to include another key for us to grab
+ // settings from. in this case: Classpnp\HackFlags
+ //
+ // the use of a DWORD value for the HackFlags allows 32 hacks w/o
+ // significant use of the registry, and also reduces OEM exposure.
+ //
+ // definition of bit flags:
+ // 0x00000001 -- Device succeeds PREVENT_MEDIUM_REMOVAL, but
+ // cannot actually prevent removal.
+ // 0x00000002 -- Device hard-hangs or times out for GESN requests.
+ // 0xfffffffc -- Currently reserved, may be used later.
+ //
+
+ return;
+}
+
+
+
+
diff --git a/reactos/drivers/storage/classpnp/class.def b/reactos/drivers/storage/classpnp/class.def
new file mode 100644
index 00000000000..f6a2311b642
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/class.def
@@ -0,0 +1,61 @@
+NAME CLASSPNP.SYS
+
+EXPORTS
+ ClassInitialize@12
+ ClassInitializeEx@12
+ ClassGetDescriptor@12
+ ClassReadDriveCapacity@4
+ ClassReleaseQueue@4
+ ClassAsynchronousCompletion@12
+ ClassSplitRequest@12
+ ClassDeviceControl@8
+ ClassIoComplete@12
+ ClassIoCompleteAssociated@12
+ ClassInterpretSenseInfo@28
+ ClassSendDeviceIoControlSynchronous@28
+ ClassSendIrpSynchronous@8
+ ClassForwardIrpSynchronous@8
+ ClassSendSrbSynchronous@20
+ ClassSendSrbAsynchronous@24
+ ClassBuildRequest@8
+ ClassModeSense@16
+ ClassFindModePage@16
+ ClassClaimDevice@8
+ ClassInternalIoControl@8
+ ClassCreateDeviceObject@20
+ ClassRemoveDevice@8
+ ClassInitializeSrbLookasideList@8
+ ClassDeleteSrbLookasideList@4
+ ClassQueryTimeOutRegistryValue@4
+ ClassInvalidateBusRelations@4
+ ClassMarkChildrenMissing@4
+ ClassMarkChildMissing@8
+ ClassDebugPrint
+ ClassGetDriverExtension@4
+ ClassCompleteRequest@12
+ ClassReleaseRemoveLock@8
+ ClassAcquireRemoveLockEx@16
+ ClassUpdateInformationInRegistry@20
+ ClassWmiCompleteRequest@20
+ ClassWmiFireEvent@20
+ ClassGetVpb@4
+ ClassSetFailurePredictionPoll@12
+ ClassNotifyFailurePredicted@32
+ ClassInitializeTestUnitPolling@8
+ ClassSignalCompletion@12
+ ClassSendStartUnit@4
+ ClassSetMediaChangeState@12
+ ClassResetMediaChangeTimer@4
+ ClassCheckMediaState@4
+ ClassInitializeMediaChangeDetection@8
+ ClassCleanupMediaChangeDetection@4
+ ClassEnableMediaChangeDetection@4
+ ClassDisableMediaChangeDetection@4
+ ClassSpinDownPowerHandler@8
+ ClassStopUnitPowerHandler@8
+ ClassAcquireChildLock@4
+ ClassReleaseChildLock@4
+ ClassScanForSpecial@12
+ ClassSetDeviceParameter@16
+ ClassGetDeviceParameter@16
+
diff --git a/reactos/drivers/storage/classpnp/class.rc b/reactos/drivers/storage/classpnp/class.rc
new file mode 100644
index 00000000000..4ee7e3d9eef
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/class.rc
@@ -0,0 +1,23 @@
+//+-------------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (C) Microsoft Corporation, 1996 - 1999
+//
+// File: class.rc
+//
+//--------------------------------------------------------------------------
+
+#include
+
+#include
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "SCSI Class System Dll"
+#define VER_INTERNALNAME_STR "Classpnp.sys"
+#define VER_ORIGINALFILENAME_STR "Classpnp.sys"
+#define VER_LANGNEUTRAL
+
+#include "common.ver"
+
diff --git a/reactos/drivers/storage/classpnp/classp.h b/reactos/drivers/storage/classpnp/classp.h
new file mode 100644
index 00000000000..43dc218a309
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/classp.h
@@ -0,0 +1,907 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ classp.h
+
+Abstract:
+
+ Private header file for classpnp.sys modules. This contains private
+ structure and function declarations as well as constant values which do
+ not need to be exported.
+
+Author:
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#if CLASS_INIT_GUID
+#include
+#endif
+
+#include
+#include
+#include
+
+extern CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[];
+
+extern GUID ClassGuidQueryRegInfoEx;
+
+
+#define CLASSP_REG_SUBKEY_NAME (L"Classpnp")
+
+#define CLASSP_REG_HACK_VALUE_NAME (L"HackMask")
+#define CLASSP_REG_MMC_DETECTION_VALUE_NAME (L"MMCDetectionState")
+#define CLASSP_REG_WRITE_CACHE_VALUE_NAME (L"WriteCacheEnableOverride")
+#define CLASSP_REG_PERF_RESTORE_VALUE_NAME (L"RestorePerfAtCount")
+#define CLASSP_REG_REMOVAL_POLICY_VALUE_NAME (L"UserRemovalPolicy")
+
+#define CLASS_PERF_RESTORE_MINIMUM (0x10)
+#define CLASS_ERROR_LEVEL_1 (0x4)
+#define CLASS_ERROR_LEVEL_2 (0x8)
+
+#define FDO_HACK_CANNOT_LOCK_MEDIA (0x00000001)
+#define FDO_HACK_GESN_IS_BAD (0x00000002)
+#define FDO_HACK_NO_SYNC_CACHE (0x00000004)
+
+#define FDO_HACK_VALID_FLAGS (0x00000007)
+#define FDO_HACK_INVALID_FLAGS (~FDO_HACK_VALID_FLAGS)
+
+/*
+ * Lots of retries of synchronized SCSI commands that devices may not
+ * even support really slows down the system (especially while booting).
+ * (Even GetDriveCapacity may be failed on purpose if an external disk is powered off).
+ * If a disk cannot return a small initialization buffer at startup
+ * in two attempts (with delay interval) then we cannot expect it to return
+ * data consistently with four retries.
+ * So don't set the retry counts as high here as for data SRBs.
+ *
+ * If we find that these requests are failing consecutively,
+ * despite the retry interval, on otherwise reliable media,
+ * then we should either increase the retry interval for
+ * that failure or (by all means) increase these retry counts as appropriate.
+ */
+#define NUM_LOCKMEDIAREMOVAL_RETRIES 1
+#define NUM_MODESENSE_RETRIES 1
+#define NUM_DRIVECAPACITY_RETRIES 1
+
+
+#define CLASS_FILE_OBJECT_EXTENSION_KEY 'eteP'
+#define CLASSP_VOLUME_VERIFY_CHECKED 0x34
+
+#define CLASS_TAG_PRIVATE_DATA 'CPcS'
+#define CLASS_TAG_PRIVATE_DATA_FDO 'FPcS'
+#define CLASS_TAG_PRIVATE_DATA_PDO 'PPcS'
+
+struct _MEDIA_CHANGE_DETECTION_INFO {
+
+ //
+ // Mutex to synchronize enable/disable requests and media state changes
+ //
+
+ KMUTEX MediaChangeMutex;
+
+ //
+ // The current state of the media (present, not present, unknown)
+ // protected by MediaChangeSynchronizationEvent
+ //
+
+ MEDIA_CHANGE_DETECTION_STATE MediaChangeDetectionState;
+
+ //
+ // This is a count of how many time MCD has been disabled. if it is
+ // set to zero, then we'll poll the device for MCN events with the
+ // then-current method (ie. TEST UNIT READY or GESN). this is
+ // protected by MediaChangeMutex
+ //
+
+ LONG MediaChangeDetectionDisableCount;
+
+
+ //
+ // The timer value to support media change events. This is a countdown
+ // value used to determine when to poll the device for a media change.
+ // The max value for the timer is 255 seconds. This is not protected
+ // by an event -- simply InterlockedExchanged() as needed.
+ //
+
+ LONG MediaChangeCountDown;
+
+ //
+ // recent changes allowed instant retries of the MCN irp. Since this
+ // could cause an infinite loop, keep a count of how many times we've
+ // retried immediately so that we can catch if the count exceeds an
+ // arbitrary limit.
+ //
+
+ LONG MediaChangeRetryCount;
+
+ //
+ // use GESN if it's available
+ //
+
+ struct {
+ BOOLEAN Supported;
+ BOOLEAN HackEventMask;
+ UCHAR EventMask;
+ UCHAR NoChangeEventMask;
+ PUCHAR Buffer;
+ PMDL Mdl;
+ ULONG BufferSize;
+ } Gesn;
+
+ //
+ // If this value is one, then the irp is currently in use.
+ // If this value is zero, then the irp is available.
+ // Use InterlockedCompareExchange() to set from "available" to "in use".
+ // ASSERT that InterlockedCompareExchange() showed previous value of
+ // "in use" when changing back to "available" state.
+ // This also implicitly protects the MediaChangeSrb and SenseBuffer
+ //
+
+ LONG MediaChangeIrpInUse;
+
+ //
+ // Pointer to the irp to be used for media change detection.
+ // protected by Interlocked MediaChangeIrpInUse
+ //
+
+ PIRP MediaChangeIrp;
+
+ //
+ // The srb for the media change detection.
+ // protected by Interlocked MediaChangeIrpInUse
+ //
+
+ SCSI_REQUEST_BLOCK MediaChangeSrb;
+ PUCHAR SenseBuffer;
+ ULONG SrbFlags;
+
+ //
+ // Second timer to keep track of how long the media change IRP has been
+ // in use. If this value exceeds the timeout (#defined) then we should
+ // print out a message to the user and set the MediaChangeIrpLost flag
+ // protected by using Interlocked() operations in ClasspSendMediaStateIrp,
+ // the only routine which should modify this value.
+ //
+
+ LONG MediaChangeIrpTimeInUse;
+
+ //
+ // Set by CdRomTickHandler when we determine that the media change irp has
+ // been lost
+ //
+
+ BOOLEAN MediaChangeIrpLost;
+
+};
+
+typedef enum {
+ SimpleMediaLock,
+ SecureMediaLock,
+ InternalMediaLock
+} MEDIA_LOCK_TYPE, *PMEDIA_LOCK_TYPE;
+
+typedef struct _FAILURE_PREDICTION_INFO {
+ FAILURE_PREDICTION_METHOD Method;
+ ULONG CountDown; // Countdown timer
+ ULONG Period; // Countdown period
+
+ PIO_WORKITEM WorkQueueItem;
+
+ KEVENT Event;
+} FAILURE_PREDICTION_INFO, *PFAILURE_PREDICTION_INFO;
+
+
+
+//
+// This struct must always fit within four PVOIDs of info,
+// as it uses the irp's "PVOID DriverContext[4]" to store
+// this info
+//
+typedef struct _CLASS_RETRY_INFO {
+ struct _CLASS_RETRY_INFO *Next;
+} CLASS_RETRY_INFO, *PCLASS_RETRY_INFO;
+
+
+
+typedef struct _CSCAN_LIST {
+
+ //
+ // The current block which has an outstanding request.
+ //
+
+ ULONGLONG BlockNumber;
+
+ //
+ // The list of blocks past the CurrentBlock to which we're going to do
+ // i/o. This list is maintained in sorted order.
+ //
+
+ LIST_ENTRY CurrentSweep;
+
+ //
+ // The list of blocks behind the current block for which we'll have to
+ // wait until the next scan across the disk. This is kept as a stack,
+ // the cost of sorting it is taken when it's moved over to be the
+ // running list.
+ //
+
+ LIST_ENTRY NextSweep;
+
+} CSCAN_LIST, *PCSCAN_LIST;
+
+//
+// add to the front of this structure to help prevent illegal
+// snooping by other utilities.
+//
+
+
+
+typedef enum _CLASS_DETECTION_STATE {
+ ClassDetectionUnknown = 0,
+ ClassDetectionUnsupported = 1,
+ ClassDetectionSupported = 2
+} CLASS_DETECTION_STATE, *PCLASS_DETECTION_STATE;
+
+
+typedef struct _CLASS_ERROR_LOG_DATA {
+ LARGE_INTEGER TickCount; // Offset 0x00
+ ULONG PortNumber; // Offset 0x08
+
+ UCHAR ErrorPaging : 1; // Offset 0x0c
+ UCHAR ErrorRetried : 1;
+ UCHAR ErrorUnhandled : 1;
+ UCHAR ErrorReserved : 5;
+
+ UCHAR Reserved[3];
+
+ SCSI_REQUEST_BLOCK Srb; // Offset 0x10
+
+ /*
+ * We define the SenseData as the default length.
+ * Since the sense data returned by the port driver may be longer,
+ * SenseData must be at the end of this structure.
+ * For our internal error log, we only log the default length.
+ */
+ SENSE_DATA SenseData; // Offset 0x50 for x86 (or 0x68 for ia64) (ULONG32 Alignment required!)
+} CLASS_ERROR_LOG_DATA, *PCLASS_ERROR_LOG_DATA;
+
+#define NUM_ERROR_LOG_ENTRIES 16
+
+
+
+typedef struct _TRANSFER_PACKET {
+
+ LIST_ENTRY AllPktsListEntry; // entry in fdoData's static AllTransferPacketsList
+ SINGLE_LIST_ENTRY SlistEntry; // for when in free list (use fast slist)
+
+ PIRP Irp;
+ PDEVICE_OBJECT Fdo;
+
+ /*
+ * This is the client IRP that this TRANSFER_PACKET is currently
+ * servicing.
+ */
+ PIRP OriginalIrp;
+ BOOLEAN CompleteOriginalIrpWhenLastPacketCompletes;
+
+ /*
+ * Stuff for retrying the transfer.
+ */
+ ULONG NumRetries;
+ KTIMER RetryTimer;
+ KDPC RetryTimerDPC;
+ ULONG RetryIntervalSec;
+
+ /*
+ * Event for synchronizing the transfer (optional).
+ * (Note that we can't have the event in the packet itself because
+ * by the time a thread waits on an event the packet may have
+ * been completed and re-issued.
+ */
+ PKEVENT SyncEventPtr;
+
+ /*
+ * Stuff for retrying during extreme low-memory stress
+ * (when we retry 1 page at a time).
+ */
+ BOOLEAN InLowMemRetry;
+ PUCHAR LowMemRetry_remainingBufPtr;
+ ULONG LowMemRetry_remainingBufLen;
+ LARGE_INTEGER LowMemRetry_nextChunkTargetLocation;
+
+ /*
+ * Fields used for cancelling the packet.
+ */
+ // BOOLEAN Cancelled;
+ // KEVENT CancelledEvent;
+
+ /*
+ * We keep the buffer and length values here as well
+ * as in the SRB because some miniports return
+ * the transferred length in SRB.DataTransferLength,
+ * and if the SRB failed we need that value again for the retry.
+ * We don't trust the lower stack to preserve any of these values in the SRB.
+ */
+ PUCHAR BufPtrCopy;
+ ULONG BufLenCopy;
+ LARGE_INTEGER TargetLocationCopy;
+
+ /*
+ * This is a standard SCSI structure that receives a detailed
+ * report about a SCSI error on the hardware.
+ */
+ SENSE_DATA SrbErrorSenseData;
+
+ /*
+ * This is the SRB block for this TRANSFER_PACKET.
+ * For IOCTLs, the SRB block includes two DWORDs for
+ * device object and ioctl code; so these must
+ * immediately follow the SRB block.
+ */
+ SCSI_REQUEST_BLOCK Srb;
+ // ULONG SrbIoctlDevObj; // not handling ioctls yet
+ // ULONG SrbIoctlCode;
+
+} TRANSFER_PACKET, *PTRANSFER_PACKET;
+
+/*
+ * MIN_INITIAL_TRANSFER_PACKETS is the minimum number of packets that
+ * we preallocate at startup for each device (we need at least one packet
+ * to guarantee forward progress during memory stress).
+ * MIN_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs
+ * we allow to build up and remain for each device;
+ * we _lazily_ work down to this number when they're not needed.
+ * MAX_WORKINGSET_TRANSFER_PACKETS is the number of TRANSFER_PACKETs
+ * that we _immediately_ reduce to when they are not needed.
+ *
+ * The absolute maximum number of packets that we will allocate is
+ * whatever is required by the current activity, up to the memory limit;
+ * as soon as stress ends, we snap down to MAX_WORKINGSET_TRANSFER_PACKETS;
+ * we then lazily work down to MIN_WORKINGSET_TRANSFER_PACKETS.
+ */
+#define MIN_INITIAL_TRANSFER_PACKETS 1
+#define MIN_WORKINGSET_TRANSFER_PACKETS_Consumer 4
+#define MAX_WORKINGSET_TRANSFER_PACKETS_Consumer 64
+#define MIN_WORKINGSET_TRANSFER_PACKETS_Server 64
+#define MAX_WORKINGSET_TRANSFER_PACKETS_Server 1024
+#define MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise 256
+#define MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise 2048
+
+
+//
+// add to the front of this structure to help prevent illegal
+// snooping by other utilities.
+//
+struct _CLASS_PRIVATE_FDO_DATA {
+
+ //
+ // this private structure allows us to
+ // dynamically re-enable the perf benefits
+ // lost due to transient error conditions.
+ // in w2k, a reboot was required. :(
+ //
+ struct {
+ ULONG OriginalSrbFlags;
+ ULONG SuccessfulIO;
+ ULONG ReEnableThreshhold; // 0 means never
+ } Perf;
+
+ ULONG_PTR HackFlags;
+
+ STORAGE_HOTPLUG_INFO HotplugInfo;
+
+ // Legacy. Still used by obsolete legacy code.
+ struct {
+ LARGE_INTEGER Delta; // in ticks
+ LARGE_INTEGER Tick; // when it should fire
+ PCLASS_RETRY_INFO ListHead; // singly-linked list
+ ULONG Granularity; // static
+ KSPIN_LOCK Lock; // protective spin lock
+ KDPC Dpc; // DPC routine object
+ KTIMER Timer; // timer to fire DPC
+ } Retry;
+
+ BOOLEAN TimerStarted;
+ BOOLEAN LoggedTURFailureSinceLastIO;
+
+ //
+ // privately allocated release queue irp
+ // protected by fdoExtension->ReleaseQueueSpinLock
+ //
+ BOOLEAN ReleaseQueueIrpAllocated;
+ PIRP ReleaseQueueIrp;
+
+ /*
+ * Queues for TRANSFER_PACKETs that contextualize the IRPs and SRBs
+ * that we send down to the port driver.
+ * (The free list is an slist so that we can use fast
+ * interlocked operations on it; but the relatively-static
+ * AllTransferPacketsList list has to be
+ * a doubly-linked list since we have to dequeue from the middle).
+ */
+ LIST_ENTRY AllTransferPacketsList;
+ SLIST_HEADER FreeTransferPacketsList;
+ ULONG NumFreeTransferPackets;
+ ULONG NumTotalTransferPackets;
+ ULONG DbgPeakNumTransferPackets;
+
+ /*
+ * Queue for deferred client irps
+ */
+ LIST_ENTRY DeferredClientIrpList;
+
+ /*
+ * Precomputed maximum transfer length for the hardware.
+ */
+ ULONG HwMaxXferLen;
+
+ /*
+ * SCSI_REQUEST_BLOCK template preconfigured with the constant values.
+ * This is slapped into the SRB in the TRANSFER_PACKET for each transfer.
+ */
+ SCSI_REQUEST_BLOCK SrbTemplate;
+
+ KSPIN_LOCK SpinLock;
+
+ /*
+ * Circular array of timestamped logs of errors that occurred on this device.
+ */
+ ULONG ErrorLogNextIndex;
+ CLASS_ERROR_LOG_DATA ErrorLogs[NUM_ERROR_LOG_ENTRIES];
+
+};
+
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+
+#define NOT_READY_RETRY_INTERVAL 10
+#define MINIMUM_RETRY_UNITS ((LONGLONG)32)
+
+
+/*
+ * Simple singly-linked-list queuing macros, with no synchronization.
+ */
+static inline VOID SimpleInitSlistHdr(SINGLE_LIST_ENTRY *SListHdr)
+{
+ SListHdr->Next = NULL;
+}
+static inline VOID SimplePushSlist(SINGLE_LIST_ENTRY *SListHdr, SINGLE_LIST_ENTRY *SListEntry)
+{
+ SListEntry->Next = SListHdr->Next;
+ SListHdr->Next = SListEntry;
+}
+static inline SINGLE_LIST_ENTRY *SimplePopSlist(SINGLE_LIST_ENTRY *SListHdr)
+{
+ SINGLE_LIST_ENTRY *sListEntry = SListHdr->Next;
+ if (sListEntry){
+ SListHdr->Next = sListEntry->Next;
+ sListEntry->Next = NULL;
+ }
+ return sListEntry;
+}
+static inline BOOLEAN SimpleIsSlistEmpty(SINGLE_LIST_ENTRY *SListHdr)
+{
+ return (SListHdr->Next == NULL);
+}
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+VOID
+ClassUnload(
+ IN PDRIVER_OBJECT DriverObject
+ );
+
+NTSTATUS
+ClassCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClasspCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+ClasspCleanupProtectedLocks(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ );
+
+NTSTATUS
+ClasspEjectionControl(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN MEDIA_LOCK_TYPE LockType,
+ IN BOOLEAN Lock
+ );
+
+NTSTATUS
+ClassReadWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClassDeviceControlDispatch(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+NTSTATUS
+ClassDeviceControl(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+NTSTATUS
+ClassDispatchPnp(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+NTSTATUS
+ClassPnpStartDevice(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+ClassInternalIoControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClassShutdownFlush(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClassSystemControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+//
+// Class internal routines
+//
+
+NTSTATUS
+ClassAddDevice(
+ IN PDRIVER_OBJECT DriverObject,
+ IN OUT PDEVICE_OBJECT PhysicalDeviceObject
+ );
+
+NTSTATUS
+ClasspSendSynchronousCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+VOID
+RetryRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PSCSI_REQUEST_BLOCK Srb,
+ BOOLEAN Associated,
+ ULONG RetryInterval
+ );
+
+NTSTATUS
+ClassIoCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+ClassPnpQueryFdoRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClassRetrieveDeviceRelations(
+ IN PDEVICE_OBJECT Fdo,
+ IN DEVICE_RELATION_TYPE RelationType,
+ OUT PDEVICE_RELATIONS *DeviceRelations
+ );
+
+NTSTATUS
+ClassGetPdoId(
+ IN PDEVICE_OBJECT Pdo,
+ IN BUS_QUERY_ID_TYPE IdType,
+ IN PUNICODE_STRING IdString
+ );
+
+NTSTATUS
+ClassQueryPnpCapabilities(
+ IN PDEVICE_OBJECT PhysicalDeviceObject,
+ IN PDEVICE_CAPABILITIES Capabilities
+ );
+
+VOID
+ClasspStartIo(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClasspPagingNotificationCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PDEVICE_OBJECT RealDeviceObject
+ );
+
+NTSTATUS
+ClasspMediaChangeCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ );
+
+PFILE_OBJECT_EXTENSION
+ClasspGetFsContext(
+ IN PCOMMON_DEVICE_EXTENSION CommonExtension,
+ IN PFILE_OBJECT FileObject
+ );
+
+NTSTATUS
+ClasspMcnControl(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PIRP Irp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+ClasspRegisterMountedDeviceInterface(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+ClasspDisableTimer(
+ PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+ClasspEnableTimer(
+ PDEVICE_OBJECT DeviceObject
+ );
+
+//
+// routines for dictionary list support
+//
+
+VOID
+InitializeDictionary(
+ IN PDICTIONARY Dictionary
+ );
+
+BOOLEAN
+TestDictionarySignature(
+ IN PDICTIONARY Dictionary
+ );
+
+NTSTATUS
+AllocateDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key,
+ IN ULONG Size,
+ IN ULONG Tag,
+ OUT PVOID *Entry
+ );
+
+PVOID
+GetDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key
+ );
+
+VOID
+FreeDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN PVOID Entry
+ );
+
+
+NTSTATUS
+ClasspAllocateReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+VOID
+ClasspFreeReleaseRequest(
+ IN PDEVICE_OBJECT Fdo
+ );
+
+NTSTATUS
+ClassReleaseQueueCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+VOID
+ClasspReleaseQueue(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP ReleaseQueueIrp
+ );
+
+VOID
+ClasspDisablePowerNotification(
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+);
+
+//
+// class power routines
+//
+
+NTSTATUS
+ClassDispatchPower(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ClassMinimalPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+//
+// Child list routines
+//
+
+VOID
+ClassAddChild(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ IN PPHYSICAL_DEVICE_EXTENSION Child,
+ IN BOOLEAN AcquireLock
+ );
+
+PPHYSICAL_DEVICE_EXTENSION
+ClassRemoveChild(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Parent,
+ IN PPHYSICAL_DEVICE_EXTENSION Child,
+ IN BOOLEAN AcquireLock
+ );
+
+VOID
+ClasspRetryDpcTimer(
+ IN PCLASS_PRIVATE_FDO_DATA FdoData
+ );
+
+VOID
+ClasspRetryRequestDpc(
+ IN PKDPC Dpc,
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PVOID Arg1,
+ IN PVOID Arg2
+ );
+
+VOID
+ClassFreeOrReuseSrb(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+ClassRetryRequest(
+ IN PDEVICE_OBJECT SelfDeviceObject,
+ IN PIRP Irp,
+ IN LARGE_INTEGER TimeDelta100ns // in 100ns units
+ );
+
+VOID
+ClasspBuildRequestEx(
+ IN PFUNCTIONAL_DEVICE_EXTENSION Fdo,
+ IN PIRP Irp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+NTSTATUS
+ClasspAllocateReleaseQueueIrp(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+NTSTATUS
+ClasspInitializeGesn(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PMEDIA_CHANGE_DETECTION_INFO Info
+ );
+
+VOID
+ClasspSendNotification(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN const GUID * Guid,
+ IN ULONG ExtraDataSize,
+ IN PVOID ExtraData
+ );
+
+VOID
+ClassSendEjectionNotification(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+ClasspScanForSpecialInRegistry(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+ClasspScanForClassHacks(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN ULONG_PTR Data
+ );
+
+NTSTATUS
+ClasspInitializeHotplugInfo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+VOID
+ClasspPerfIncrementErrorCount(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+VOID
+ClasspPerfIncrementSuccessfulIo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ );
+
+
+PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo);
+VOID DestroyTransferPacket(PTRANSFER_PACKET Pkt);
+VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, PTRANSFER_PACKET Pkt);
+PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded);
+VOID SetupReadWriteTransferPacket(PTRANSFER_PACKET pkt, PVOID Buf, ULONG Len, LARGE_INTEGER DiskLocation, PIRP OriginalIrp);
+VOID SubmitTransferPacket(PTRANSFER_PACKET Pkt);
+NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context);
+VOID ServiceTransferRequest(PDEVICE_OBJECT Fdo, PIRP Irp);
+VOID TransferPacketRetryTimerDpc(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2);
+BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt);
+BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt);
+VOID EnqueueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData, PIRP Irp);
+PIRP DequeueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData);
+VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation);
+BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt);
+VOID SetupEjectionTransferPacket(TRANSFER_PACKET *Pkt, BOOLEAN PreventMediaRemoval, PKEVENT SyncEventPtr, PIRP OriginalIrp);
+VOID SetupModeSenseTransferPacket(TRANSFER_PACKET *Pkt, PKEVENT SyncEventPtr, PVOID ModeSenseBuffer, UCHAR ModeSenseBufferLen, UCHAR PageMode, PIRP OriginalIrp);
+VOID SetupDriveCapacityTransferPacket(TRANSFER_PACKET *Pkt, PVOID ReadCapacityBuffer, ULONG ReadCapacityBufferLen, PKEVENT SyncEventPtr, PIRP OriginalIrp);
+PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen);
+VOID FreeDeviceInputMdl(PMDL Mdl);
+NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo);
+VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo);
+
+
+
+
+
diff --git a/reactos/drivers/storage/classpnp/classpnp.rbuild b/reactos/drivers/storage/classpnp/classpnp.rbuild
new file mode 100644
index 00000000000..668d79b79c9
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/classpnp.rbuild
@@ -0,0 +1,37 @@
+
+
+
+
+
+ ntoskrnl
+ hal
+ pseh
+ libcntpr
+ ../inc
+ 0
+ 100
+ 1
+ 0
+ 512
+ 512
+
+ -mrtd
+ -fno-builtin
+ -w
+
+ autorun.c
+ class.c
+ classwmi.c
+ create.c
+ data.c
+ dictlib.c
+ lock.c
+ power.c
+ xferpkt.c
+ clntirp.c
+ retry.c
+ utils.c
+ obsolete.c
+ debug.c
+ class.rc
+
diff --git a/reactos/drivers/storage/classpnp/classwmi.c b/reactos/drivers/storage/classpnp/classwmi.c
new file mode 100644
index 00000000000..bbd43a6999d
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/classwmi.c
@@ -0,0 +1,778 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ classwmi.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "ntddk.h"
+#include "scsi.h"
+
+#include "classpnp.h"
+
+#include "mountdev.h"
+
+#include
+
+#include "wmistr.h"
+
+NTSTATUS
+ClassSystemControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+BOOLEAN
+ClassFindGuid(
+ PGUIDREGINFO GuidList,
+ ULONG GuidCount,
+ LPGUID Guid,
+ PULONG GuidIndex
+ );
+
+//
+// This is the name for the MOF resource that must be part of all drivers that
+// register via this interface.
+#define MOFRESOURCENAME L"MofResourceName"
+
+//
+// What can be paged ???
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, ClassSystemControl)
+#pragma alloc_text(PAGE, ClassFindGuid)
+#endif
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassFindGuid()
+
+Routine Description:
+
+ This routine will search the list of guids registered and return
+ the index for the one that was registered.
+
+Arguments:
+
+ GuidList is the list of guids to search
+
+ GuidCount is the count of guids in the list
+
+ Guid is the guid being searched for
+
+ *GuidIndex returns the index to the guid
+
+Return Value:
+
+ TRUE if guid is found else FALSE
+
+--*/
+BOOLEAN
+ClassFindGuid(
+ PGUIDREGINFO GuidList,
+ ULONG GuidCount,
+ LPGUID Guid,
+ PULONG GuidIndex
+ )
+{
+ ULONG i;
+
+ PAGED_CODE();
+
+ for (i = 0; i < GuidCount; i++)
+ {
+ if (IsEqualGUID(Guid, &GuidList[i].Guid))
+ {
+ *GuidIndex = i;
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+} // end ClassFindGuid()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSystemControl()
+
+Routine Description:
+
+ Dispatch routine for IRP_MJ_SYSTEM_CONTROL. This routine will process
+ all wmi requests received, forwarding them if they are not for this
+ driver or determining if the guid is valid and if so passing it to
+ the driver specific function for handing wmi requests.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassSystemControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PCLASS_DRIVER_EXTENSION driverExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ ULONG isRemoved;
+ ULONG bufferSize;
+ PUCHAR buffer;
+ NTSTATUS status;
+ UCHAR minorFunction;
+ ULONG guidIndex;
+ PCLASS_WMI_INFO classWmiInfo;
+
+ PAGED_CODE();
+
+ //
+ // Make sure device has not been removed
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+ if(isRemoved)
+ {
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ //
+ // If the irp is not a WMI irp or it is not targetted at this device
+ // or this device has not regstered with WMI then just forward it on.
+ minorFunction = irpStack->MinorFunction;
+ if ((minorFunction > IRP_MN_EXECUTE_METHOD) ||
+ (irpStack->Parameters.WMI.ProviderId != (ULONG_PTR)DeviceObject) ||
+ ((minorFunction != IRP_MN_REGINFO) &&
+ (commonExtension->GuidRegInfo == NULL)))
+ {
+ //
+ // CONSIDER: Do I need to hang onto lock until IoCallDriver returns ?
+ IoSkipCurrentIrpStackLocation(Irp);
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ return(IoCallDriver(commonExtension->LowerDeviceObject, Irp));
+ }
+
+ buffer = (PUCHAR)irpStack->Parameters.WMI.Buffer;
+ bufferSize = irpStack->Parameters.WMI.BufferSize;
+
+ if (minorFunction != IRP_MN_REGINFO)
+ {
+ //
+ // For all requests other than query registration info we are passed
+ // a guid. Determine if the guid is one that is supported by the
+ // device.
+ if (ClassFindGuid(commonExtension->GuidRegInfo,
+ commonExtension->GuidCount,
+ (LPGUID)irpStack->Parameters.WMI.DataPath,
+ &guidIndex))
+ {
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_WMI_GUID_NOT_FOUND;
+ }
+
+ if (NT_SUCCESS(status) &&
+ ((minorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) ||
+ (minorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE) ||
+ (minorFunction == IRP_MN_CHANGE_SINGLE_ITEM) ||
+ (minorFunction == IRP_MN_EXECUTE_METHOD)))
+ {
+ if ( (((PWNODE_HEADER)buffer)->Flags) &
+ WNODE_FLAG_STATIC_INSTANCE_NAMES)
+ {
+ if ( ((PWNODE_SINGLE_INSTANCE)buffer)->InstanceIndex != 0 )
+ {
+ status = STATUS_WMI_INSTANCE_NOT_FOUND;
+ }
+ } else {
+ status = STATUS_WMI_INSTANCE_NOT_FOUND;
+ }
+ }
+
+ if (! NT_SUCCESS(status))
+ {
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return(status);
+ }
+ }
+
+ driverExtension = commonExtension->DriverExtension;
+
+ classWmiInfo = commonExtension->IsFdo ?
+ &driverExtension->InitData.FdoData.ClassWmiInfo :
+ &driverExtension->InitData.PdoData.ClassWmiInfo;
+ switch(minorFunction)
+ {
+ case IRP_MN_REGINFO:
+ {
+ ULONG guidCount;
+ PGUIDREGINFO guidList;
+ PWMIREGINFOW wmiRegInfo;
+ PWMIREGGUIDW wmiRegGuid;
+ PDEVICE_OBJECT pdo;
+ PUNICODE_STRING regPath;
+ PWCHAR stringPtr;
+ ULONG retSize;
+ ULONG registryPathOffset;
+ ULONG mofResourceOffset;
+ ULONG bufferNeeded;
+ ULONG i;
+ ULONG_PTR nameInfo;
+ ULONG nameSize, nameOffset, nameFlags;
+ UNICODE_STRING name, mofName;
+ PCLASS_QUERY_WMI_REGINFO_EX ClassQueryWmiRegInfoEx;
+
+ name.Buffer = NULL;
+ name.Length = 0;
+ name.MaximumLength = 0;
+ nameFlags = 0;
+
+ ClassQueryWmiRegInfoEx = commonExtension->IsFdo ?
+ driverExtension->ClassFdoQueryWmiRegInfoEx :
+ driverExtension->ClassPdoQueryWmiRegInfoEx;
+
+ if (ClassQueryWmiRegInfoEx == NULL)
+ {
+ status = classWmiInfo->ClassQueryWmiRegInfo(
+ DeviceObject,
+ &nameFlags,
+ &name);
+
+ RtlInitUnicodeString(&mofName, MOFRESOURCENAME);
+ } else {
+ RtlInitUnicodeString(&mofName, L"");
+ status = (*ClassQueryWmiRegInfoEx)(
+ DeviceObject,
+ &nameFlags,
+ &name,
+ &mofName);
+ }
+
+ if (NT_SUCCESS(status) &&
+ (! (nameFlags & WMIREG_FLAG_INSTANCE_PDO) &&
+ (name.Buffer == NULL)))
+ {
+ //
+ // if PDO flag not specified then an instance name must be
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ if (NT_SUCCESS(status))
+ {
+ guidList = classWmiInfo->GuidRegInfo;
+ guidCount = classWmiInfo->GuidCount;
+
+ nameOffset = sizeof(WMIREGINFO) +
+ guidCount * sizeof(WMIREGGUIDW);
+
+ if (nameFlags & WMIREG_FLAG_INSTANCE_PDO)
+ {
+ nameSize = 0;
+ nameInfo = commonExtension->IsFdo ?
+ (ULONG_PTR)((PFUNCTIONAL_DEVICE_EXTENSION)commonExtension)->LowerPdo :
+ (ULONG_PTR)DeviceObject;
+ } else {
+ nameFlags |= WMIREG_FLAG_INSTANCE_LIST;
+ nameSize = name.Length + sizeof(USHORT);
+ nameInfo = nameOffset;
+ }
+
+ mofResourceOffset = nameOffset + nameSize;
+
+ registryPathOffset = mofResourceOffset +
+ mofName.Length + sizeof(USHORT);
+
+ regPath = &driverExtension->RegistryPath;
+ bufferNeeded = registryPathOffset +
+ regPath->Length + sizeof(USHORT);
+
+ if (bufferNeeded <= bufferSize)
+ {
+ retSize = bufferNeeded;
+
+ commonExtension->GuidCount = guidCount;
+ commonExtension->GuidRegInfo = guidList;
+
+ wmiRegInfo = (PWMIREGINFO)buffer;
+ wmiRegInfo->BufferSize = bufferNeeded;
+ wmiRegInfo->NextWmiRegInfo = 0;
+ wmiRegInfo->MofResourceName = mofResourceOffset;
+ wmiRegInfo->RegistryPath = registryPathOffset;
+ wmiRegInfo->GuidCount = guidCount;
+
+ for (i = 0; i < guidCount; i++)
+ {
+ wmiRegGuid = &wmiRegInfo->WmiRegGuid[i];
+ wmiRegGuid->Guid = guidList[i].Guid;
+ wmiRegGuid->Flags = guidList[i].Flags | nameFlags;
+ wmiRegGuid->InstanceInfo = nameInfo;
+ wmiRegGuid->InstanceCount = 1;
+ }
+
+ if ( nameFlags & WMIREG_FLAG_INSTANCE_LIST)
+ {
+ stringPtr = (PWCHAR)((PUCHAR)buffer + nameOffset);
+ *stringPtr++ = name.Length;
+ RtlCopyMemory(stringPtr,
+ name.Buffer,
+ name.Length);
+ }
+
+ stringPtr = (PWCHAR)((PUCHAR)buffer + mofResourceOffset);
+ *stringPtr++ = mofName.Length;
+ RtlCopyMemory(stringPtr,
+ mofName.Buffer,
+ mofName.Length);
+
+ stringPtr = (PWCHAR)((PUCHAR)buffer + registryPathOffset);
+ *stringPtr++ = regPath->Length;
+ RtlCopyMemory(stringPtr,
+ regPath->Buffer,
+ regPath->Length);
+ } else {
+ *((PULONG)buffer) = bufferNeeded;
+ retSize = sizeof(ULONG);
+ }
+ } else {
+ retSize = 0;
+ }
+
+ if (name.Buffer != NULL)
+ {
+ ExFreePool(name.Buffer);
+ }
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = retSize;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return(status);
+ }
+
+ case IRP_MN_QUERY_ALL_DATA:
+ {
+ PWNODE_ALL_DATA wnode;
+ ULONG bufferAvail;
+
+ wnode = (PWNODE_ALL_DATA)buffer;
+
+ if (bufferSize < sizeof(WNODE_ALL_DATA))
+ {
+ bufferAvail = 0;
+ } else {
+ bufferAvail = bufferSize - sizeof(WNODE_ALL_DATA);
+ }
+
+ wnode->DataBlockOffset = sizeof(WNODE_ALL_DATA);
+
+ status = classWmiInfo->ClassQueryWmiDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ bufferAvail,
+ buffer + sizeof(WNODE_ALL_DATA));
+
+ break;
+ }
+
+ case IRP_MN_QUERY_SINGLE_INSTANCE:
+ {
+ PWNODE_SINGLE_INSTANCE wnode;
+ ULONG dataBlockOffset;
+
+ wnode = (PWNODE_SINGLE_INSTANCE)buffer;
+
+ dataBlockOffset = wnode->DataBlockOffset;
+
+ status = classWmiInfo->ClassQueryWmiDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ bufferSize - dataBlockOffset,
+ (PUCHAR)wnode + dataBlockOffset);
+
+ break;
+ }
+
+ case IRP_MN_CHANGE_SINGLE_INSTANCE:
+ {
+ PWNODE_SINGLE_INSTANCE wnode;
+
+ wnode = (PWNODE_SINGLE_INSTANCE)buffer;
+
+ status = classWmiInfo->ClassSetWmiDataBlock(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ wnode->SizeDataBlock,
+ (PUCHAR)wnode + wnode->DataBlockOffset);
+
+ break;
+ }
+
+ case IRP_MN_CHANGE_SINGLE_ITEM:
+ {
+ PWNODE_SINGLE_ITEM wnode;
+
+ wnode = (PWNODE_SINGLE_ITEM)buffer;
+
+ status = classWmiInfo->ClassSetWmiDataItem(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ wnode->ItemId,
+ wnode->SizeDataItem,
+ (PUCHAR)wnode + wnode->DataBlockOffset);
+
+ break;
+ }
+
+ case IRP_MN_EXECUTE_METHOD:
+ {
+ PWNODE_METHOD_ITEM wnode;
+
+ wnode = (PWNODE_METHOD_ITEM)buffer;
+
+ status = classWmiInfo->ClassExecuteWmiMethod(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ wnode->MethodId,
+ wnode->SizeDataBlock,
+ bufferSize - wnode->DataBlockOffset,
+ buffer + wnode->DataBlockOffset);
+
+
+ break;
+ }
+
+ case IRP_MN_ENABLE_EVENTS:
+ {
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ EventGeneration,
+ TRUE);
+ break;
+ }
+
+ case IRP_MN_DISABLE_EVENTS:
+ {
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ EventGeneration,
+ FALSE);
+ break;
+ }
+
+ case IRP_MN_ENABLE_COLLECTION:
+ {
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ DataBlockCollection,
+ TRUE);
+ break;
+ }
+
+ case IRP_MN_DISABLE_COLLECTION:
+ {
+ status = classWmiInfo->ClassWmiFunctionControl(
+ DeviceObject,
+ Irp,
+ guidIndex,
+ DataBlockCollection,
+ FALSE);
+ break;
+ }
+
+ default:
+ {
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ }
+
+ return(status);
+} // end ClassSystemControl()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassWmiCompleteRequest()
+
+Routine Description:
+
+
+ This routine will do the work of completing a WMI irp. Depending upon the
+ the WMI request this routine will fixup the returned WNODE appropriately.
+
+ NOTE: This routine assumes that the ClassRemoveLock is held and it will
+ release it.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this request.
+
+ Irp - Supplies the Irp making the request.
+
+ Status - Status to complete the irp with. STATUS_BUFFER_TOO_SMALL is used
+ to indicate that more buffer is required for the data requested.
+
+ BufferUsed - number of bytes of actual data to return (not including WMI
+ specific structures)
+
+ PriorityBoost - priority boost to pass to ClassCompleteRequest
+
+Return Value:
+
+ status
+
+--*/
+SCSIPORTAPI
+NTSTATUS
+ClassWmiCompleteRequest(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN NTSTATUS Status,
+ IN ULONG BufferUsed,
+ IN CCHAR PriorityBoost
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ UCHAR MinorFunction;
+ PUCHAR buffer;
+ ULONG retSize;
+ UCHAR minorFunction;
+ ULONG bufferSize;
+
+ minorFunction = irpStack->MinorFunction;
+ buffer = (PUCHAR)irpStack->Parameters.WMI.Buffer;
+ bufferSize = irpStack->Parameters.WMI.BufferSize;
+
+ switch(minorFunction)
+ {
+ case IRP_MN_QUERY_ALL_DATA:
+ {
+ PWNODE_ALL_DATA wnode;
+ PWNODE_TOO_SMALL wnodeTooSmall;
+ ULONG bufferNeeded;
+
+ wnode = (PWNODE_ALL_DATA)buffer;
+
+ bufferNeeded = sizeof(WNODE_ALL_DATA) + BufferUsed;
+
+ if (NT_SUCCESS(Status))
+ {
+ retSize = bufferNeeded;
+ wnode->WnodeHeader.BufferSize = bufferNeeded;
+ KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp);
+ wnode->WnodeHeader.Flags |= WNODE_FLAG_FIXED_INSTANCE_SIZE;
+ wnode->FixedInstanceSize = BufferUsed;
+ wnode->InstanceCount = 1;
+
+ } else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
+
+ wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
+ wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
+ wnodeTooSmall->SizeNeeded = sizeof(WNODE_ALL_DATA) + BufferUsed;
+ retSize = sizeof(WNODE_TOO_SMALL);
+ Status = STATUS_SUCCESS;
+ } else {
+ retSize = 0;
+ }
+ break;
+ }
+
+ case IRP_MN_QUERY_SINGLE_INSTANCE:
+ {
+ PWNODE_SINGLE_INSTANCE wnode;
+ PWNODE_TOO_SMALL wnodeTooSmall;
+ ULONG bufferNeeded;
+
+ wnode = (PWNODE_SINGLE_INSTANCE)buffer;
+
+ bufferNeeded = wnode->DataBlockOffset + BufferUsed;
+
+ if (NT_SUCCESS(Status))
+ {
+ retSize = bufferNeeded;
+ wnode->WnodeHeader.BufferSize = bufferNeeded;
+ KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp);
+ wnode->SizeDataBlock = BufferUsed;
+
+ } else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
+
+ wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
+ wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
+ wnodeTooSmall->SizeNeeded = bufferNeeded;
+ retSize = sizeof(WNODE_TOO_SMALL);
+ Status = STATUS_SUCCESS;
+ } else {
+ retSize = 0;
+ }
+ break;
+ }
+
+ case IRP_MN_EXECUTE_METHOD:
+ {
+ PWNODE_METHOD_ITEM wnode;
+ PWNODE_TOO_SMALL wnodeTooSmall;
+ ULONG bufferNeeded;
+
+ wnode = (PWNODE_METHOD_ITEM)buffer;
+
+ bufferNeeded = wnode->DataBlockOffset + BufferUsed;
+
+ if (NT_SUCCESS(Status))
+ {
+ retSize = bufferNeeded;
+ wnode->WnodeHeader.BufferSize = bufferNeeded;
+ KeQuerySystemTime(&wnode->WnodeHeader.TimeStamp);
+ wnode->SizeDataBlock = BufferUsed;
+
+ } else if (Status == STATUS_BUFFER_TOO_SMALL) {
+ wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
+
+ wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
+ wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
+ wnodeTooSmall->SizeNeeded = bufferNeeded;
+ retSize = sizeof(WNODE_TOO_SMALL);
+ Status = STATUS_SUCCESS;
+ } else {
+ retSize = 0;
+ }
+ break;
+ }
+
+ default:
+ {
+ //
+ // All other requests don't return any data
+ retSize = 0;
+ break;
+ }
+
+ }
+
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = retSize;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, PriorityBoost);
+ return(Status);
+} // end ClassWmiCompleteRequest()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassWmiFireEvent()
+
+Routine Description:
+
+ This routine will fire a WMI event using the data buffer passed. This
+ routine may be called at or below DPC level
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object for this event
+
+ Guid is pointer to the GUID that represents the event
+
+ InstanceIndex is the index of the instance of the event
+
+ EventDataSize is the number of bytes of data that is being fired with
+ with the event
+
+ EventData is the data that is fired with the events. This may be NULL
+ if there is no data associated with the event
+
+
+Return Value:
+
+ status
+
+--*/
+NTSTATUS
+ClassWmiFireEvent(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN LPGUID Guid,
+ IN ULONG InstanceIndex,
+ IN ULONG EventDataSize,
+ IN PVOID EventData
+ )
+{
+
+ ULONG sizeNeeded;
+ PWNODE_SINGLE_INSTANCE event;
+ NTSTATUS status;
+
+ if (EventData == NULL)
+ {
+ EventDataSize = 0;
+ }
+
+ sizeNeeded = sizeof(WNODE_SINGLE_INSTANCE) + EventDataSize;
+
+ event = ExAllocatePoolWithTag(NonPagedPool, sizeNeeded, CLASS_TAG_WMI);
+ if (event != NULL)
+ {
+ event->WnodeHeader.Guid = *Guid;
+ event->WnodeHeader.ProviderId = IoWMIDeviceObjectToProviderId(DeviceObject);
+ event->WnodeHeader.BufferSize = sizeNeeded;
+ event->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
+ WNODE_FLAG_EVENT_ITEM |
+ WNODE_FLAG_STATIC_INSTANCE_NAMES;
+ KeQuerySystemTime(&event->WnodeHeader.TimeStamp);
+
+ event->InstanceIndex = InstanceIndex;
+ event->SizeDataBlock = EventDataSize;
+ event->DataBlockOffset = sizeof(WNODE_SINGLE_INSTANCE);
+ if (EventData != NULL)
+ {
+ RtlCopyMemory( &event->VariableData, EventData, EventDataSize);
+ }
+
+ status = IoWMIWriteEvent(event);
+ if (! NT_SUCCESS(status))
+ {
+ ExFreePool(event);
+ }
+ } else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ return(status);
+} // end ClassWmiFireEvent()
+
diff --git a/reactos/drivers/storage/classpnp/clntirp.c b/reactos/drivers/storage/classpnp/clntirp.c
new file mode 100644
index 00000000000..934e389bf19
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/clntirp.c
@@ -0,0 +1,74 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ clntirp.c
+
+Abstract:
+
+ Client IRP queuing routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+
+/*
+ * EnqueueDeferredClientIrp
+ *
+ * Note: we currently do not support Cancel for storage irps.
+ */
+VOID EnqueueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData, PIRP Irp)
+{
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&FdoData->SpinLock, &oldIrql);
+ InsertTailList(&FdoData->DeferredClientIrpList, &Irp->Tail.Overlay.ListEntry);
+ KeReleaseSpinLock(&FdoData->SpinLock, oldIrql);
+}
+
+
+/*
+ * DequeueDeferredClientIrp
+ *
+ */
+PIRP DequeueDeferredClientIrp(PCLASS_PRIVATE_FDO_DATA FdoData)
+{
+ KIRQL oldIrql;
+ PLIST_ENTRY listEntry;
+ PIRP irp;
+
+ KeAcquireSpinLock(&FdoData->SpinLock, &oldIrql);
+ if (IsListEmpty(&FdoData->DeferredClientIrpList)){
+ listEntry = NULL;
+ }
+ else {
+ listEntry = RemoveHeadList(&FdoData->DeferredClientIrpList);
+ }
+ KeReleaseSpinLock(&FdoData->SpinLock, oldIrql);
+
+ if (listEntry == NULL) {
+ irp = NULL;
+ } else {
+ irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
+ ASSERT(irp->Type == IO_TYPE_IRP);
+ InitializeListHead(&irp->Tail.Overlay.ListEntry);
+ }
+
+ return irp;
+}
+
+
+
diff --git a/reactos/drivers/storage/classpnp/create.c b/reactos/drivers/storage/classpnp/create.c
new file mode 100644
index 00000000000..12fc002452f
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/create.c
@@ -0,0 +1,977 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ class.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#define CLASS_INIT_GUID 0
+#include "classp.h"
+#include "debug.h"
+
+ULONG BreakOnClose = 0;
+
+PUCHAR LockTypeStrings[] = {
+ "Simple",
+ "Secure",
+ "Internal"
+};
+
+
+PFILE_OBJECT_EXTENSION
+ClasspGetFsContext(
+ IN PCOMMON_DEVICE_EXTENSION CommonExtension,
+ IN PFILE_OBJECT FileObject
+ );
+
+VOID
+ClasspCleanupDisableMcn(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text(PAGE, ClassCreateClose)
+#pragma alloc_text(PAGE, ClasspCreateClose)
+#pragma alloc_text(PAGE, ClasspCleanupProtectedLocks)
+#pragma alloc_text(PAGE, ClasspEjectionControl)
+#pragma alloc_text(PAGE, ClasspCleanupDisableMcn)
+#pragma alloc_text(PAGE, ClasspGetFsContext)
+#endif
+
+NTSTATUS
+ClassCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ SCSI class driver create and close routine. This is called by the I/O system
+ when the device is opened or closed.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by system.
+
+ Irp - IRP involved.
+
+Return Value:
+
+ Device-specific drivers return value or STATUS_SUCCESS.
+
+--*/
+
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ ULONG removeState;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // If we're getting a close request then we know the device object hasn't
+ // been completely destroyed. Let the driver cleanup if necessary.
+ //
+
+ removeState = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ //
+ // Invoke the device-specific routine, if one exists. Otherwise complete
+ // with SUCCESS
+ //
+
+ if((removeState == NO_REMOVE) ||
+ IS_CLEANUP_REQUEST(IoGetCurrentIrpStackLocation(Irp)->MajorFunction)) {
+
+ status = ClasspCreateClose(DeviceObject, Irp);
+
+ if((NT_SUCCESS(status)) &&
+ (commonExtension->DevInfo->ClassCreateClose)) {
+
+ return commonExtension->DevInfo->ClassCreateClose(DeviceObject, Irp);
+ }
+
+ } else {
+ status = STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ Irp->IoStatus.Status = status;
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return status;
+}
+
+
+NTSTATUS
+ClasspCreateClose(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine will handle create/close operations for a given classpnp
+ device if the class driver doesn't supply it's own handler. If there
+ is a file object supplied for our driver (if it's a FO_DIRECT_DEVICE_OPEN
+ file object) then it will initialize a file extension on create or destroy
+ the extension on a close.
+
+Arguments:
+
+ DeviceObject - the device object being opened or closed.
+
+ Irp - the create/close irp
+
+Return Value:
+
+ status
+
+--*/
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+
+ PFILE_OBJECT fileObject = irpStack->FileObject;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+
+ //
+ // ISSUE-2000/3/28-henrygab - if lower stack fails create/close, we end up
+ // in an inconsistent state. re-write to verify all args and allocate all
+ // required resources, then pass the irp down, then complete the
+ // transaction. this is because we also cannot forward the irp, then fail
+ // it after it has succeeded a lower-level driver.
+ //
+
+ if(irpStack->MajorFunction == IRP_MJ_CREATE) {
+
+ PIO_SECURITY_CONTEXT securityContext =
+ irpStack->Parameters.Create.SecurityContext;
+ DebugPrint((2,
+ "ClasspCREATEClose: create received for device %p\n",
+ DeviceObject));
+ DebugPrint((2,
+ "ClasspCREATEClose: desired access %lx\n",
+ securityContext->DesiredAccess));
+ DebugPrint((2,
+ "ClasspCREATEClose: file object %lx\n",
+ irpStack->FileObject));
+
+ ASSERT(BreakOnClose == FALSE);
+
+ if(irpStack->FileObject != NULL) {
+
+ PFILE_OBJECT_EXTENSION fsContext;
+
+ //
+ // Allocate our own file object extension for this device object.
+ //
+
+ status = AllocateDictionaryEntry(
+ &commonExtension->FileObjectDictionary,
+ (ULONGLONG) irpStack->FileObject,
+ sizeof(FILE_OBJECT_EXTENSION),
+ CLASS_TAG_FILE_OBJECT_EXTENSION,
+ &fsContext);
+
+ if(NT_SUCCESS(status)) {
+
+ RtlZeroMemory(fsContext,
+ sizeof(FILE_OBJECT_EXTENSION));
+
+ fsContext->FileObject = irpStack->FileObject;
+ fsContext->DeviceObject = DeviceObject;
+ } else if (status == STATUS_OBJECT_NAME_COLLISION) {
+ status = STATUS_SUCCESS;
+ }
+ }
+
+ } else {
+
+ DebugPrint((2,
+ "ClasspCreateCLOSE: close received for device %p\n",
+ DeviceObject));
+ DebugPrint((2,
+ "ClasspCreateCLOSE: file object %p\n",
+ fileObject));
+
+ if(irpStack->FileObject != NULL) {
+
+ PFILE_OBJECT_EXTENSION fsContext =
+ ClasspGetFsContext(commonExtension, irpStack->FileObject);
+
+ DebugPrint((2,
+ "ClasspCreateCLOSE: file extension %p\n",
+ fsContext));
+
+ if(fsContext != NULL) {
+
+ DebugPrint((2,
+ "ClasspCreateCLOSE: extension is ours - "
+ "freeing\n"));
+ ASSERT(BreakOnClose == FALSE);
+
+ ClasspCleanupProtectedLocks(fsContext);
+
+ ClasspCleanupDisableMcn(fsContext);
+
+ FreeDictionaryEntry(&(commonExtension->FileObjectDictionary),
+ fsContext);
+ }
+ }
+ }
+
+ //
+ // Notify the lower levels about the create or close operation - give them
+ // a chance to cleanup too.
+ //
+
+ DebugPrint((2,
+ "ClasspCreateClose: %s for devobj %p\n",
+ (NT_SUCCESS(status) ? "Success" : "FAILED"),
+ DeviceObject));
+
+
+ if(NT_SUCCESS(status)) {
+
+ KEVENT event;
+
+ //
+ // Set up the event to wait on
+ //
+
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ IoSetCompletionRoutine( Irp, ClassSignalCompletion, &event,
+ TRUE, TRUE, TRUE);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ if(status == STATUS_PENDING) {
+ KeWaitForSingleObject(&event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = Irp->IoStatus.Status;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ DebugPrint((ClassDebugError,
+ "ClasspCreateClose: Lower driver failed, but we "
+ "succeeded. This is a problem, lock counts will be "
+ "out of sync between levels.\n"));
+ }
+
+ }
+
+
+ return status;
+}
+
+
+VOID
+ClasspCleanupProtectedLocks(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ FsContext->DeviceObject->DeviceExtension;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ commonExtension->PartitionZeroExtension;
+
+ ULONG newDeviceLockCount = 1;
+
+ PAGED_CODE();
+
+ DebugPrint((2,
+ "ClasspCleanupProtectedLocks called for %p\n",
+ FsContext->DeviceObject));
+ DebugPrint((2,
+ "ClasspCleanupProtectedLocks - FsContext %p is locked "
+ "%d times\n", FsContext, FsContext->LockCount));
+
+ ASSERT(BreakOnClose == FALSE);
+
+ //
+ // Synchronize with ejection and ejection control requests.
+ //
+
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
+ UserRequest,
+ UserMode,
+ FALSE,
+ NULL);
+
+ //
+ // For each secure lock on this handle decrement the secured lock count
+ // for the FDO. Keep track of the new value.
+ //
+
+ if(FsContext->LockCount != 0) {
+
+ do {
+
+ InterlockedDecrement(&FsContext->LockCount);
+
+ newDeviceLockCount =
+ InterlockedDecrement(&fdoExtension->ProtectedLockCount);
+
+ } while(FsContext->LockCount != 0);
+
+ //
+ // If the new lock count has been dropped to zero then issue a lock
+ // command to the device.
+ //
+
+ DebugPrint((2,
+ "ClasspCleanupProtectedLocks: FDO secured lock count = %d "
+ "lock count = %d\n",
+ fdoExtension->ProtectedLockCount,
+ fdoExtension->LockCount));
+
+ if((newDeviceLockCount == 0) && (fdoExtension->LockCount == 0)) {
+
+ SCSI_REQUEST_BLOCK srb;
+ PCDB cdb;
+ NTSTATUS status;
+
+ DebugPrint((2,
+ "ClasspCleanupProtectedLocks: FDO lock count dropped "
+ "to zero\n"));
+
+ RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
+ cdb = (PCDB) &(srb.Cdb);
+
+ srb.CdbLength = 6;
+
+ cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+
+ //
+ // TRUE - prevent media removal.
+ // FALSE - allow media removal.
+ //
+
+ cdb->MEDIA_REMOVAL.Prevent = FALSE;
+
+ //
+ // Set timeout value.
+ //
+
+ srb.TimeOutValue = fdoExtension->TimeOutValue;
+ status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
+ &srb,
+ NULL,
+ 0,
+ FALSE);
+
+ DebugPrint((2,
+ "ClasspCleanupProtectedLocks: unlock request to drive "
+ "returned status %lx\n", status));
+ }
+ }
+
+ KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+ KeLeaveCriticalRegion();
+ return;
+}
+
+
+VOID
+ClasspCleanupDisableMcn(
+ IN PFILE_OBJECT_EXTENSION FsContext
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ FsContext->DeviceObject->DeviceExtension;
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ commonExtension->PartitionZeroExtension;
+
+ ULONG newCount = 1;
+
+ PAGED_CODE();
+
+ DebugPrint((ClassDebugTrace,
+ "ClasspCleanupDisableMcn called for %p\n",
+ FsContext->DeviceObject));
+ DebugPrint((ClassDebugTrace,
+ "ClasspCleanupDisableMcn - FsContext %p is disabled "
+ "%d times\n", FsContext, FsContext->McnDisableCount));
+
+ //
+ // For each secure lock on this handle decrement the secured lock count
+ // for the FDO. Keep track of the new value.
+ //
+
+ while(FsContext->McnDisableCount != 0) {
+ FsContext->McnDisableCount--;
+ ClassEnableMediaChangeDetection(fdoExtension);
+ }
+
+ return;
+}
+
+
+#if 1
+/*
+ * BUGBUG REMOVE this old function implementation as soon as the
+ * boottime pagefile problems with the new one (below)
+ * are resolved.
+ */
+NTSTATUS
+ClasspEjectionControl(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN MEDIA_LOCK_TYPE LockType,
+ IN BOOLEAN Lock
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = Fdo->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension =
+ (PCOMMON_DEVICE_EXTENSION) FdoExtension;
+
+ PFILE_OBJECT_EXTENSION fsContext = NULL;
+ NTSTATUS status;
+ PSCSI_REQUEST_BLOCK srb = NULL;
+ BOOLEAN countChanged = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // Interlock with ejection and secure lock cleanup code. This is a
+ // user request so we can allow the stack to get swapped out while we
+ // wait for synchronization.
+ //
+
+ status = KeWaitForSingleObject(
+ &(FdoExtension->EjectSynchronizationEvent),
+ UserRequest,
+ UserMode,
+ FALSE,
+ NULL);
+
+ ASSERT(status == STATUS_SUCCESS);
+
+ DebugPrint((2,
+ "ClasspEjectionControl: "
+ "Received request for %s lock type\n",
+ LockTypeStrings[LockType]
+ ));
+
+ _SEH2_TRY {
+ PCDB cdb;
+
+ srb = ClasspAllocateSrb(FdoExtension);
+
+ if(srb == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ _SEH2_LEAVE;
+ }
+
+ RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ cdb = (PCDB) srb->Cdb;
+
+ //
+ // Determine if this is a "secured" request.
+ //
+
+ if(LockType == SecureMediaLock) {
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_OBJECT fileObject = irpStack->FileObject;
+
+ //
+ // Make sure that the file object we are supplied has a
+ // proper FsContext before we try doing a secured lock.
+ //
+
+ if(fileObject != NULL) {
+ fsContext = ClasspGetFsContext(commonExtension, fileObject);
+ }
+
+ if (fsContext == NULL) {
+
+ //
+ // This handle isn't setup correctly. We can't let the
+ // operation go.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ _SEH2_LEAVE;
+ }
+ }
+
+ if(Lock) {
+
+ //
+ // This is a lock command. Reissue the command in case bus or
+ // device was reset and the lock was cleared.
+ // note: may need to decrement count if actual lock operation
+ // failed....
+ //
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount++;
+ countChanged = TRUE;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount++;
+ FdoExtension->ProtectedLockCount++;
+ countChanged = TRUE;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount++;
+ countChanged = TRUE;
+ break;
+ }
+ }
+
+ } else {
+
+ //
+ // This is an unlock command. If it's a secured one then make sure
+ // the caller has a lock outstanding or return an error.
+ // note: may need to re-increment the count if actual unlock
+ // operation fails....
+ //
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ if(FdoExtension->LockCount != 0) {
+ FdoExtension->LockCount--;
+ countChanged = TRUE;
+ }
+ break;
+ }
+
+ case SecureMediaLock: {
+ if(fsContext->LockCount == 0) {
+ status = STATUS_INVALID_DEVICE_STATE;
+ _SEH2_LEAVE;
+ }
+ fsContext->LockCount--;
+ FdoExtension->ProtectedLockCount--;
+ countChanged = TRUE;
+ break;
+ }
+
+ case InternalMediaLock: {
+ ASSERT(FdoExtension->InternalLockCount != 0);
+ FdoExtension->InternalLockCount--;
+ countChanged = TRUE;
+ break;
+ }
+ }
+
+ //
+ // We only send an unlock command to the drive if both the
+ // secured and unsecured lock counts have dropped to zero.
+ //
+
+ if((FdoExtension->ProtectedLockCount != 0) ||
+ (FdoExtension->InternalLockCount != 0) ||
+ (FdoExtension->LockCount != 0)) {
+
+ status = STATUS_SUCCESS;
+ _SEH2_LEAVE;
+ }
+ }
+
+ status = STATUS_SUCCESS;
+ if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+
+ srb->CdbLength = 6;
+ cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+
+ //
+ // TRUE - prevent media removal.
+ // FALSE - allow media removal.
+ //
+
+ cdb->MEDIA_REMOVAL.Prevent = Lock;
+
+ //
+ // Set timeout value.
+ //
+
+ srb->TimeOutValue = FdoExtension->TimeOutValue;
+
+ //
+ // The actual lock operation on the device isn't so important
+ // as the internal lock counts. Ignore failures.
+ //
+
+ status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
+ srb,
+ NULL,
+ 0,
+ FALSE);
+ }
+
+ } _SEH2_FINALLY {
+
+ if (!NT_SUCCESS(status)) {
+ DebugPrint((2,
+ "ClasspEjectionControl: FAILED status %x -- "
+ "reverting lock counts\n", status));
+
+ if (countChanged) {
+
+ //
+ // have to revert to previous counts if the
+ // lock/unlock operation actually failed.
+ //
+
+ if(Lock) {
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount--;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount--;
+ FdoExtension->ProtectedLockCount--;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount--;
+ break;
+ }
+ }
+
+ } else {
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount++;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount++;
+ FdoExtension->ProtectedLockCount++;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount++;
+ break;
+ }
+ }
+ }
+
+ }
+
+ } else {
+
+ DebugPrint((2,
+ "ClasspEjectionControl: Succeeded\n"));
+
+ }
+
+ DebugPrint((2,
+ "ClasspEjectionControl: "
+ "Current Counts: Internal: %x Secure: %x Simple: %x\n",
+ FdoExtension->InternalLockCount,
+ FdoExtension->ProtectedLockCount,
+ FdoExtension->LockCount
+ ));
+
+ KeSetEvent(&(FdoExtension->EjectSynchronizationEvent),
+ IO_NO_INCREMENT,
+ FALSE);
+ if (srb) {
+ ClassFreeOrReuseSrb(FdoExtension, srb);
+ }
+
+ } _SEH2_END;
+ return status;
+}
+
+#else
+
+/*
+ * BUGBUG RESTORE
+ * This is a new implementation of the function that doesn't thrash memory
+ * or depend on the srbLookasideList.
+ * HOWEVER, it seems to cause pagefile initialization to fail during boot
+ * for some reason. Need to resolve this before switching to this function.
+ */
+NTSTATUS
+ClasspEjectionControl(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN MEDIA_LOCK_TYPE LockType,
+ IN BOOLEAN Lock
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PFILE_OBJECT_EXTENSION fsContext;
+ BOOLEAN fileHandleOk = TRUE;
+ BOOLEAN countChanged = FALSE;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ status = KeWaitForSingleObject(
+ &fdoExt->EjectSynchronizationEvent,
+ UserRequest,
+ UserMode,
+ FALSE,
+ NULL);
+ ASSERT(status == STATUS_SUCCESS);
+
+ /*
+ * If this is a "secured" request, we have to make sure
+ * that the file handle is valid.
+ */
+ if (LockType == SecureMediaLock){
+ PIO_STACK_LOCATION thisSp = IoGetCurrentIrpStackLocation(Irp);
+
+ /*
+ * Make sure that the file object we are supplied has a
+ * proper FsContext before we try doing a secured lock.
+ */
+ if (thisSp->FileObject){
+ PCOMMON_DEVICE_EXTENSION commonExt = (PCOMMON_DEVICE_EXTENSION)fdoExt;
+ fsContext = ClasspGetFsContext(commonExt, thisSp->FileObject);
+ }
+ else {
+ fsContext = NULL;
+ }
+
+ if (!fsContext){
+ ASSERT(fsContext);
+ fileHandleOk = FALSE;
+ }
+ }
+
+ if (fileHandleOk){
+
+ /*
+ * Adjust the lock counts and make sure they make sense.
+ */
+ status = STATUS_SUCCESS;
+ if (Lock){
+ switch(LockType) {
+ case SimpleMediaLock:
+ fdoExt->LockCount++;
+ countChanged = TRUE;
+ break;
+ case SecureMediaLock:
+ fsContext->LockCount++;
+ fdoExt->ProtectedLockCount++;
+ countChanged = TRUE;
+ break;
+ case InternalMediaLock:
+ fdoExt->InternalLockCount++;
+ countChanged = TRUE;
+ break;
+ }
+ }
+ else {
+ /*
+ * This is an unlock command. If it's a secured one then make sure
+ * the caller has a lock outstanding or return an error.
+ */
+ switch (LockType){
+ case SimpleMediaLock:
+ if (fdoExt->LockCount > 0){
+ fdoExt->LockCount--;
+ countChanged = TRUE;
+ }
+ else {
+ ASSERT(fdoExt->LockCount > 0);
+ status = STATUS_INTERNAL_ERROR;
+ }
+ break;
+ case SecureMediaLock:
+ if (fsContext->LockCount > 0){
+ ASSERT(fdoExt->ProtectedLockCount > 0);
+ fsContext->LockCount--;
+ fdoExt->ProtectedLockCount--;
+ countChanged = TRUE;
+ }
+ else {
+ ASSERT(fsContext->LockCount > 0);
+ status = STATUS_INVALID_DEVICE_STATE;
+ }
+ break;
+ case InternalMediaLock:
+ ASSERT(fdoExt->InternalLockCount > 0);
+ fdoExt->InternalLockCount--;
+ countChanged = TRUE;
+ break;
+ }
+ }
+
+ if (NT_SUCCESS(status)){
+ /*
+ * We only send an unlock command to the drive if
+ * all the lock counts have dropped to zero.
+ */
+ if (!Lock &&
+ (fdoExt->ProtectedLockCount ||
+ fdoExt->InternalLockCount ||
+ fdoExt->LockCount)){
+
+ /*
+ * The lock count is still positive, so don't unlock yet.
+ */
+ status = STATUS_SUCCESS;
+ }
+ else if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
+ /*
+ * The device isn't removable media. don't send a cmd.
+ */
+ status = STATUS_SUCCESS;
+ }
+ else {
+ TRANSFER_PACKET *pkt;
+
+ pkt = DequeueFreeTransferPacket(Fdo, TRUE);
+ if (pkt){
+ KEVENT event;
+
+ /*
+ * Store the number of packets servicing the irp (one)
+ * inside the original IRP. It will be used to counted down
+ * to zero when the packet completes.
+ * Initialize the original IRP's status to success.
+ * If the packet fails, we will set it to the error status.
+ */
+ Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ /*
+ * Set this up as a SYNCHRONOUS transfer, submit it,
+ * and wait for the packet to complete. The result
+ * status will be written to the original irp.
+ */
+ KeInitializeEvent(&event, SynchronizationEvent, FALSE);
+ SetupEjectionTransferPacket(pkt, Lock, &event, Irp);
+ SubmitTransferPacket(pkt);
+ KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
+ status = Irp->IoStatus.Status;
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
+ }
+ }
+ else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ if (!NT_SUCCESS(status) && countChanged) {
+
+ //
+ // have to revert to previous counts if the
+ // lock/unlock operation actually failed.
+ //
+
+ if(Lock) {
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount--;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount--;
+ FdoExtension->ProtectedLockCount--;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount--;
+ break;
+ }
+ }
+
+ } else {
+
+ switch(LockType) {
+
+ case SimpleMediaLock: {
+ FdoExtension->LockCount++;
+ break;
+ }
+
+ case SecureMediaLock: {
+ fsContext->LockCount++;
+ FdoExtension->ProtectedLockCount++;
+ break;
+ }
+
+ case InternalMediaLock: {
+ FdoExtension->InternalLockCount++;
+ break;
+ }
+ }
+ }
+ }
+
+
+
+ KeSetEvent(&fdoExt->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
+
+ return status;
+}
+#endif
+
+PFILE_OBJECT_EXTENSION
+ClasspGetFsContext(
+ IN PCOMMON_DEVICE_EXTENSION CommonExtension,
+ IN PFILE_OBJECT FileObject
+ )
+{
+ PAGED_CODE();
+ return GetDictionaryEntry(&(CommonExtension->FileObjectDictionary),
+ (ULONGLONG) FileObject);
+}
+
diff --git a/reactos/drivers/storage/classpnp/data.c b/reactos/drivers/storage/classpnp/data.c
new file mode 100644
index 00000000000..9bd638ba13c
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/data.c
@@ -0,0 +1,48 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ disk.c
+
+Abstract:
+
+ SCSI disk class driver
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg("PAGE")
+#endif
+
+/*
+#define FDO_HACK_CANNOT_LOCK_MEDIA (0x00000001)
+#define FDO_HACK_GESN_IS_BAD (0x00000002)
+*/
+
+CLASSPNP_SCAN_FOR_SPECIAL_INFO ClassBadItems[] = {
+ { "" , "MITSUMI CD-ROM FX240" , NULL, 0x02 },
+ { "" , "MITSUMI CD-ROM FX320" , NULL, 0x02 },
+ { "" , "MITSUMI CD-ROM FX322" , NULL, 0x02 },
+ { "" , "COMPAQ CRD-8481B" , NULL, 0x04 },
+ { NULL , NULL , NULL, 0x0 }
+};
+
+
+GUID ClassGuidQueryRegInfoEx = GUID_CLASSPNP_QUERY_REGINFOEX;
+
+#ifdef ALLOC_DATA_PRAGMA
+#pragma data_seg()
+#endif
+
diff --git a/reactos/drivers/storage/classpnp/debug.c b/reactos/drivers/storage/classpnp/debug.c
new file mode 100644
index 00000000000..bea6bfb872f
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/debug.c
@@ -0,0 +1,693 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ CLASSPNP debug code and data
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+#include "classp.h"
+#include "debug.h"
+
+#if DBG
+
+ //
+ // default to not breaking in for lost irps, five minutes before we even
+ // bother checking for lost irps, using standard debug print macros, and
+ // using a 64k debug print buffer
+ //
+
+ #ifndef CLASS_GLOBAL_BREAK_ON_LOST_IRPS
+ #error "CLASS_GLOBAL_BREAK_ON_LOST_IRPS undefined"
+ #define CLASS_GLOBAL_BREAK_ON_LOST_IRPS 0
+ #endif // CLASS_GLOBAL_BREAK_ON_LOST_IRPS
+
+ #ifndef CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB
+ #error "CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB undefined"
+ #define CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB 300
+ #endif // CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB
+
+ #ifndef CLASS_GLOBAL_USE_DELAYED_RETRY
+ #error "CLASS_GLOBAL_USE_DELAYED_RETRY undefined"
+ #define CLASS_GLOBAL_USE_DELAYED_RETRY 1
+ #endif // CLASS_GLOBAL_USE_DELAYED_RETRY
+
+ #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT
+ #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT undefined"
+ #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT 0
+ #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT
+
+ #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE
+ #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE undefined"
+ #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE 512
+ #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE
+
+ #ifndef CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS
+ #error "CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS undefined"
+ #define CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS 512
+ #endif // CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS
+
+ #pragma data_seg("NONPAGE")
+
+
+
+ CLASSPNP_GLOBALS ClasspnpGlobals;
+
+ //
+ // the low sixteen bits are used to see if the debug level is high enough
+ // the high sixteen bits are used to singly enable debug levels 1-16
+ //
+ LONG ClassDebug = 0xFFFFFFFF;
+
+ BOOLEAN DebugTrapOnWarn = FALSE;
+
+ VOID ClasspInitializeDebugGlobals()
+ {
+ KIRQL irql;
+
+ if (InterlockedCompareExchange(&ClasspnpGlobals.Initializing, 1, 0) == 0) {
+
+ KeInitializeSpinLock(&ClasspnpGlobals.SpinLock);
+
+ KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);
+
+ DebugPrint((1, "CLASSPNP.SYS => Initializing ClasspnpGlobals...\n"));
+
+ ClasspnpGlobals.Buffer = NULL;
+ ClasspnpGlobals.Index = -1;
+ ClasspnpGlobals.BreakOnLostIrps = CLASS_GLOBAL_BREAK_ON_LOST_IRPS;
+ ClasspnpGlobals.EachBufferSize = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE;
+ ClasspnpGlobals.NumberOfBuffers = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS;
+ ClasspnpGlobals.SecondsToWaitForIrps = CLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB;
+
+ //
+ // this should be the last item set
+ //
+
+ ClasspnpGlobals.UseBufferedDebugPrint = CLASS_GLOBAL_BUFFERED_DEBUG_PRINT;
+
+ KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);
+
+ InterlockedExchange(&ClasspnpGlobals.Initialized, 1);
+
+ }
+ }
+
+
+
+ /*++////////////////////////////////////////////////////////////////////////////
+
+ ClassDebugPrint()
+
+ Routine Description:
+
+ Debug print for all class drivers, NOOP on FRE versions.
+ Allows printing to a debug buffer (with auto fallback to kdprint) by
+ properly setting the Globals in classpnp on CHK versions.
+
+ Arguments:
+
+ Debug print level, or from 0 to 3 for legacy drivers.
+
+ Return Value:
+
+ None
+
+ --*/
+ VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...)
+ {
+ va_list ap;
+ va_start(ap, DebugMessage);
+
+ if ((DebugPrintLevel <= (ClassDebug & 0x0000ffff)) ||
+ ((1 << (DebugPrintLevel + 15)) & ClassDebug)) {
+
+ if (ClasspnpGlobals.UseBufferedDebugPrint &&
+ ClasspnpGlobals.Buffer == NULL) {
+
+ //
+ // this double-check prevents always taking
+ // a spinlock just to ensure we have a buffer
+ //
+
+ KIRQL irql;
+
+ KeAcquireSpinLock(&ClasspnpGlobals.SpinLock, &irql);
+ if (ClasspnpGlobals.Buffer == NULL) {
+
+ SIZE_T bufferSize;
+ bufferSize = ClasspnpGlobals.NumberOfBuffers *
+ ClasspnpGlobals.EachBufferSize;
+ DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
+ "ClassDebugPrint: Allocating %x bytes for "
+ "classdebugprint buffer\n", bufferSize);
+ ClasspnpGlobals.Index = -1;
+ ClasspnpGlobals.Buffer =
+ ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'bDcS');
+ DbgPrintEx(DPFLTR_CLASSPNP_ID, DPFLTR_ERROR_LEVEL,
+ "ClassDebugPrint: Allocated buffer at %p\n",
+ ClasspnpGlobals.Buffer);
+
+ }
+ KeReleaseSpinLock(&ClasspnpGlobals.SpinLock, irql);
+
+ }
+
+ if (ClasspnpGlobals.UseBufferedDebugPrint &&
+ ClasspnpGlobals.Buffer != NULL) {
+
+ //
+ // we never free the buffer, so once it exists,
+ // we can just print to it with immunity
+ //
+
+ ULONG index;
+ PUCHAR buffer;
+ index = InterlockedIncrement(&ClasspnpGlobals.Index);
+ index %= ClasspnpGlobals.NumberOfBuffers;
+ index *= (ULONG)ClasspnpGlobals.EachBufferSize;
+
+ buffer = ClasspnpGlobals.Buffer;
+ buffer += index;
+
+ _vsnprintf(buffer, ClasspnpGlobals.EachBufferSize, DebugMessage, ap);
+
+ } else {
+
+ //
+ // either we could not allocate a buffer for debug prints
+ // or buffered debug prints are disabled
+ //
+ vDbgPrintEx(-1, DPFLTR_ERROR_LEVEL, DebugMessage, ap);
+
+ }
+
+ }
+
+ va_end(ap);
+
+ }
+
+
+ char *DbgGetIoctlStr(ULONG ioctl)
+ {
+ char *ioctlStr = "?";
+
+ switch (ioctl){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(ioctlCode) case ioctlCode: ioctlStr = #ioctlCode; break;
+
+ MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY)
+ MAKE_CASE(IOCTL_STORAGE_CHECK_VERIFY2)
+ MAKE_CASE(IOCTL_STORAGE_MEDIA_REMOVAL)
+ MAKE_CASE(IOCTL_STORAGE_EJECT_MEDIA)
+ MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA)
+ MAKE_CASE(IOCTL_STORAGE_LOAD_MEDIA2)
+ MAKE_CASE(IOCTL_STORAGE_RESERVE)
+ MAKE_CASE(IOCTL_STORAGE_RELEASE)
+ MAKE_CASE(IOCTL_STORAGE_FIND_NEW_DEVICES)
+ MAKE_CASE(IOCTL_STORAGE_EJECTION_CONTROL)
+ MAKE_CASE(IOCTL_STORAGE_MCN_CONTROL)
+ MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES)
+ MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_TYPES_EX)
+ MAKE_CASE(IOCTL_STORAGE_GET_MEDIA_SERIAL_NUMBER)
+ MAKE_CASE(IOCTL_STORAGE_GET_HOTPLUG_INFO)
+ MAKE_CASE(IOCTL_STORAGE_RESET_BUS)
+ MAKE_CASE(IOCTL_STORAGE_RESET_DEVICE)
+ MAKE_CASE(IOCTL_STORAGE_GET_DEVICE_NUMBER)
+ MAKE_CASE(IOCTL_STORAGE_PREDICT_FAILURE)
+ MAKE_CASE(IOCTL_STORAGE_QUERY_PROPERTY)
+ MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_BUS)
+ MAKE_CASE(OBSOLETE_IOCTL_STORAGE_RESET_DEVICE)
+ }
+
+ return ioctlStr;
+ }
+
+ char *DbgGetScsiOpStr(PSCSI_REQUEST_BLOCK Srb)
+ {
+ PCDB pCdb = (PCDB)Srb->Cdb;
+ UCHAR scsiOp = pCdb->CDB6GENERIC.OperationCode;
+ char *scsiOpStr = "?";
+
+ switch (scsiOp){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(scsiOpCode) case scsiOpCode: scsiOpStr = #scsiOpCode; break;
+
+ MAKE_CASE(SCSIOP_TEST_UNIT_READY)
+ MAKE_CASE(SCSIOP_REWIND) // aka SCSIOP_REZERO_UNIT
+ MAKE_CASE(SCSIOP_REQUEST_BLOCK_ADDR)
+ MAKE_CASE(SCSIOP_REQUEST_SENSE)
+ MAKE_CASE(SCSIOP_FORMAT_UNIT)
+ MAKE_CASE(SCSIOP_READ_BLOCK_LIMITS)
+ MAKE_CASE(SCSIOP_INIT_ELEMENT_STATUS) // aka SCSIOP_REASSIGN_BLOCKS
+ MAKE_CASE(SCSIOP_RECEIVE) // aka SCSIOP_READ6
+ MAKE_CASE(SCSIOP_SEND) // aka SCSIOP_WRITE6, SCSIOP_PRINT
+ MAKE_CASE(SCSIOP_SLEW_PRINT) // aka SCSIOP_SEEK6, SCSIOP_TRACK_SELECT
+ MAKE_CASE(SCSIOP_SEEK_BLOCK)
+ MAKE_CASE(SCSIOP_PARTITION)
+ MAKE_CASE(SCSIOP_READ_REVERSE)
+ MAKE_CASE(SCSIOP_FLUSH_BUFFER) // aka SCSIOP_WRITE_FILEMARKS
+ MAKE_CASE(SCSIOP_SPACE)
+ MAKE_CASE(SCSIOP_INQUIRY)
+ MAKE_CASE(SCSIOP_VERIFY6)
+ MAKE_CASE(SCSIOP_RECOVER_BUF_DATA)
+ MAKE_CASE(SCSIOP_MODE_SELECT)
+ MAKE_CASE(SCSIOP_RESERVE_UNIT)
+ MAKE_CASE(SCSIOP_RELEASE_UNIT)
+ MAKE_CASE(SCSIOP_COPY)
+ MAKE_CASE(SCSIOP_ERASE)
+ MAKE_CASE(SCSIOP_MODE_SENSE)
+ MAKE_CASE(SCSIOP_START_STOP_UNIT) // aka SCSIOP_STOP_PRINT, SCSIOP_LOAD_UNLOAD
+ MAKE_CASE(SCSIOP_RECEIVE_DIAGNOSTIC)
+ MAKE_CASE(SCSIOP_SEND_DIAGNOSTIC)
+ MAKE_CASE(SCSIOP_MEDIUM_REMOVAL)
+ MAKE_CASE(SCSIOP_READ_FORMATTED_CAPACITY)
+ MAKE_CASE(SCSIOP_READ_CAPACITY)
+ MAKE_CASE(SCSIOP_READ)
+ MAKE_CASE(SCSIOP_WRITE)
+ MAKE_CASE(SCSIOP_SEEK) // aka SCSIOP_LOCATE, SCSIOP_POSITION_TO_ELEMENT
+ MAKE_CASE(SCSIOP_WRITE_VERIFY)
+ MAKE_CASE(SCSIOP_VERIFY)
+ MAKE_CASE(SCSIOP_SEARCH_DATA_HIGH)
+ MAKE_CASE(SCSIOP_SEARCH_DATA_EQUAL)
+ MAKE_CASE(SCSIOP_SEARCH_DATA_LOW)
+ MAKE_CASE(SCSIOP_SET_LIMITS)
+ MAKE_CASE(SCSIOP_READ_POSITION)
+ MAKE_CASE(SCSIOP_SYNCHRONIZE_CACHE)
+ MAKE_CASE(SCSIOP_COMPARE)
+ MAKE_CASE(SCSIOP_COPY_COMPARE)
+ MAKE_CASE(SCSIOP_WRITE_DATA_BUFF)
+ MAKE_CASE(SCSIOP_READ_DATA_BUFF)
+ MAKE_CASE(SCSIOP_CHANGE_DEFINITION)
+ MAKE_CASE(SCSIOP_READ_SUB_CHANNEL)
+ MAKE_CASE(SCSIOP_READ_TOC)
+ MAKE_CASE(SCSIOP_READ_HEADER)
+ MAKE_CASE(SCSIOP_PLAY_AUDIO)
+ MAKE_CASE(SCSIOP_GET_CONFIGURATION)
+ MAKE_CASE(SCSIOP_PLAY_AUDIO_MSF)
+ MAKE_CASE(SCSIOP_PLAY_TRACK_INDEX)
+ MAKE_CASE(SCSIOP_PLAY_TRACK_RELATIVE)
+ MAKE_CASE(SCSIOP_GET_EVENT_STATUS)
+ MAKE_CASE(SCSIOP_PAUSE_RESUME)
+ MAKE_CASE(SCSIOP_LOG_SELECT)
+ MAKE_CASE(SCSIOP_LOG_SENSE)
+ MAKE_CASE(SCSIOP_STOP_PLAY_SCAN)
+ MAKE_CASE(SCSIOP_READ_DISK_INFORMATION)
+ MAKE_CASE(SCSIOP_READ_TRACK_INFORMATION)
+ MAKE_CASE(SCSIOP_RESERVE_TRACK_RZONE)
+ MAKE_CASE(SCSIOP_SEND_OPC_INFORMATION)
+ MAKE_CASE(SCSIOP_MODE_SELECT10)
+ MAKE_CASE(SCSIOP_MODE_SENSE10)
+ MAKE_CASE(SCSIOP_CLOSE_TRACK_SESSION)
+ MAKE_CASE(SCSIOP_READ_BUFFER_CAPACITY)
+ MAKE_CASE(SCSIOP_SEND_CUE_SHEET)
+ MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_IN)
+ MAKE_CASE(SCSIOP_PERSISTENT_RESERVE_OUT)
+ MAKE_CASE(SCSIOP_REPORT_LUNS)
+ MAKE_CASE(SCSIOP_BLANK)
+ MAKE_CASE(SCSIOP_SEND_KEY)
+ MAKE_CASE(SCSIOP_REPORT_KEY)
+ MAKE_CASE(SCSIOP_MOVE_MEDIUM)
+ MAKE_CASE(SCSIOP_LOAD_UNLOAD_SLOT) // aka SCSIOP_EXCHANGE_MEDIUM
+ MAKE_CASE(SCSIOP_SET_READ_AHEAD)
+ MAKE_CASE(SCSIOP_READ_DVD_STRUCTURE)
+ MAKE_CASE(SCSIOP_REQUEST_VOL_ELEMENT)
+ MAKE_CASE(SCSIOP_SEND_VOLUME_TAG)
+ MAKE_CASE(SCSIOP_READ_ELEMENT_STATUS)
+ MAKE_CASE(SCSIOP_READ_CD_MSF)
+ MAKE_CASE(SCSIOP_SCAN_CD)
+ MAKE_CASE(SCSIOP_SET_CD_SPEED)
+ MAKE_CASE(SCSIOP_PLAY_CD)
+ MAKE_CASE(SCSIOP_MECHANISM_STATUS)
+ MAKE_CASE(SCSIOP_READ_CD)
+ MAKE_CASE(SCSIOP_SEND_DVD_STRUCTURE)
+ MAKE_CASE(SCSIOP_INIT_ELEMENT_RANGE)
+ }
+
+ return scsiOpStr;
+ }
+
+
+ char *DbgGetSrbStatusStr(PSCSI_REQUEST_BLOCK Srb)
+ {
+ char *srbStatStr = "?";
+
+ switch (Srb->SrbStatus){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(srbStat) \
+ case srbStat: \
+ srbStatStr = #srbStat; \
+ break; \
+ case srbStat|SRB_STATUS_QUEUE_FROZEN: \
+ srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN"; \
+ break; \
+ case srbStat|SRB_STATUS_AUTOSENSE_VALID: \
+ srbStatStr = #srbStat "|SRB_STATUS_AUTOSENSE_VALID"; \
+ break; \
+ case srbStat|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID: \
+ srbStatStr = #srbStat "|SRB_STATUS_QUEUE_FROZEN|SRB_STATUS_AUTOSENSE_VALID"; \
+ break;
+
+ MAKE_CASE(SRB_STATUS_PENDING)
+ MAKE_CASE(SRB_STATUS_SUCCESS)
+ MAKE_CASE(SRB_STATUS_ABORTED)
+ MAKE_CASE(SRB_STATUS_ABORT_FAILED)
+ MAKE_CASE(SRB_STATUS_ERROR)
+ MAKE_CASE(SRB_STATUS_BUSY)
+ MAKE_CASE(SRB_STATUS_INVALID_REQUEST)
+ MAKE_CASE(SRB_STATUS_INVALID_PATH_ID)
+ MAKE_CASE(SRB_STATUS_NO_DEVICE)
+ MAKE_CASE(SRB_STATUS_TIMEOUT)
+ MAKE_CASE(SRB_STATUS_SELECTION_TIMEOUT)
+ MAKE_CASE(SRB_STATUS_COMMAND_TIMEOUT)
+ MAKE_CASE(SRB_STATUS_MESSAGE_REJECTED)
+ MAKE_CASE(SRB_STATUS_BUS_RESET)
+ MAKE_CASE(SRB_STATUS_PARITY_ERROR)
+ MAKE_CASE(SRB_STATUS_REQUEST_SENSE_FAILED)
+ MAKE_CASE(SRB_STATUS_NO_HBA)
+ MAKE_CASE(SRB_STATUS_DATA_OVERRUN)
+ MAKE_CASE(SRB_STATUS_UNEXPECTED_BUS_FREE)
+ MAKE_CASE(SRB_STATUS_PHASE_SEQUENCE_FAILURE)
+ MAKE_CASE(SRB_STATUS_BAD_SRB_BLOCK_LENGTH)
+ MAKE_CASE(SRB_STATUS_REQUEST_FLUSHED)
+ MAKE_CASE(SRB_STATUS_INVALID_LUN)
+ MAKE_CASE(SRB_STATUS_INVALID_TARGET_ID)
+ MAKE_CASE(SRB_STATUS_BAD_FUNCTION)
+ MAKE_CASE(SRB_STATUS_ERROR_RECOVERY)
+ MAKE_CASE(SRB_STATUS_NOT_POWERED)
+ MAKE_CASE(SRB_STATUS_INTERNAL_ERROR)
+ }
+
+ return srbStatStr;
+ }
+
+
+ char *DbgGetSenseCodeStr(PSCSI_REQUEST_BLOCK Srb)
+ {
+ char *senseCodeStr = "?";
+
+ if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
+ PSENSE_DATA senseData;
+ UCHAR senseCode;
+
+ ASSERT(Srb->SenseInfoBuffer);
+ senseData = Srb->SenseInfoBuffer;
+ senseCode = senseData->SenseKey & 0xf;
+
+ switch (senseCode){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(snsCod) case snsCod: senseCodeStr = #snsCod; break;
+
+ MAKE_CASE(SCSI_SENSE_NO_SENSE)
+ MAKE_CASE(SCSI_SENSE_RECOVERED_ERROR)
+ MAKE_CASE(SCSI_SENSE_NOT_READY)
+ MAKE_CASE(SCSI_SENSE_MEDIUM_ERROR)
+ MAKE_CASE(SCSI_SENSE_HARDWARE_ERROR)
+ MAKE_CASE(SCSI_SENSE_ILLEGAL_REQUEST)
+ MAKE_CASE(SCSI_SENSE_UNIT_ATTENTION)
+ MAKE_CASE(SCSI_SENSE_DATA_PROTECT)
+ MAKE_CASE(SCSI_SENSE_BLANK_CHECK)
+ MAKE_CASE(SCSI_SENSE_UNIQUE)
+ MAKE_CASE(SCSI_SENSE_COPY_ABORTED)
+ MAKE_CASE(SCSI_SENSE_ABORTED_COMMAND)
+ MAKE_CASE(SCSI_SENSE_EQUAL)
+ MAKE_CASE(SCSI_SENSE_VOL_OVERFLOW)
+ MAKE_CASE(SCSI_SENSE_MISCOMPARE)
+ MAKE_CASE(SCSI_SENSE_RESERVED)
+ }
+ }
+
+ return senseCodeStr;
+ }
+
+
+ char *DbgGetAdditionalSenseCodeStr(PSCSI_REQUEST_BLOCK Srb)
+ {
+ char *adSenseCodeStr = "?";
+
+ if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
+ PSENSE_DATA senseData;
+ UCHAR adSenseCode;
+
+ ASSERT(Srb->SenseInfoBuffer);
+ senseData = Srb->SenseInfoBuffer;
+ adSenseCode = senseData->AdditionalSenseCode;
+
+ switch (adSenseCode){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(adSnsCod) case adSnsCod: adSenseCodeStr = #adSnsCod; break;
+
+ MAKE_CASE(SCSI_ADSENSE_NO_SENSE)
+ MAKE_CASE(SCSI_ADSENSE_LUN_NOT_READY)
+ MAKE_CASE(SCSI_ADSENSE_TRACK_ERROR)
+ MAKE_CASE(SCSI_ADSENSE_SEEK_ERROR)
+ MAKE_CASE(SCSI_ADSENSE_REC_DATA_NOECC)
+ MAKE_CASE(SCSI_ADSENSE_REC_DATA_ECC)
+ MAKE_CASE(SCSI_ADSENSE_ILLEGAL_COMMAND)
+ MAKE_CASE(SCSI_ADSENSE_ILLEGAL_BLOCK)
+ MAKE_CASE(SCSI_ADSENSE_INVALID_CDB)
+ MAKE_CASE(SCSI_ADSENSE_INVALID_LUN)
+ MAKE_CASE(SCSI_ADSENSE_WRITE_PROTECT) // aka SCSI_ADWRITE_PROTECT
+ MAKE_CASE(SCSI_ADSENSE_MEDIUM_CHANGED)
+ MAKE_CASE(SCSI_ADSENSE_BUS_RESET)
+ MAKE_CASE(SCSI_ADSENSE_INVALID_MEDIA)
+ MAKE_CASE(SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)
+ MAKE_CASE(SCSI_ADSENSE_POSITION_ERROR)
+ MAKE_CASE(SCSI_ADSENSE_OPERATOR_REQUEST)
+ MAKE_CASE(SCSI_ADSENSE_FAILURE_PREDICTION_THRESHOLD_EXCEEDED)
+ MAKE_CASE(SCSI_ADSENSE_COPY_PROTECTION_FAILURE)
+ MAKE_CASE(SCSI_ADSENSE_VENDOR_UNIQUE)
+ MAKE_CASE(SCSI_ADSENSE_MUSIC_AREA)
+ MAKE_CASE(SCSI_ADSENSE_DATA_AREA)
+ MAKE_CASE(SCSI_ADSENSE_VOLUME_OVERFLOW)
+ }
+ }
+
+ return adSenseCodeStr;
+ }
+
+
+ char *DbgGetAdditionalSenseCodeQualifierStr(PSCSI_REQUEST_BLOCK Srb)
+ {
+ char *adSenseCodeQualStr = "?";
+
+ if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID){
+ PSENSE_DATA senseData;
+ UCHAR adSenseCode;
+ UCHAR adSenseCodeQual;
+
+ ASSERT(Srb->SenseInfoBuffer);
+ senseData = Srb->SenseInfoBuffer;
+ adSenseCode = senseData->AdditionalSenseCode;
+ adSenseCodeQual = senseData->AdditionalSenseCodeQualifier;
+
+ switch (adSenseCode){
+
+ #undef MAKE_CASE
+ #define MAKE_CASE(adSnsCodQual) case adSnsCodQual: adSenseCodeQualStr = #adSnsCodQual; break;
+
+ case SCSI_ADSENSE_LUN_NOT_READY:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_CAUSE_NOT_REPORTABLE)
+ MAKE_CASE(SCSI_SENSEQ_BECOMING_READY)
+ MAKE_CASE(SCSI_SENSEQ_INIT_COMMAND_REQUIRED)
+ MAKE_CASE(SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED)
+ MAKE_CASE(SCSI_SENSEQ_FORMAT_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_REBUILD_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_RECALCULATION_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_OPERATION_IN_PROGRESS)
+ MAKE_CASE(SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS)
+ }
+ break;
+ case SCSI_ADSENSE_NO_SENSE:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_FILEMARK_DETECTED)
+ MAKE_CASE(SCSI_SENSEQ_END_OF_MEDIA_DETECTED)
+ MAKE_CASE(SCSI_SENSEQ_SETMARK_DETECTED)
+ MAKE_CASE(SCSI_SENSEQ_BEGINNING_OF_MEDIA_DETECTED)
+ }
+ break;
+ case SCSI_ADSENSE_ILLEGAL_BLOCK:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_ILLEGAL_ELEMENT_ADDR)
+ }
+ break;
+ case SCSI_ADSENSE_POSITION_ERROR:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_DESTINATION_FULL)
+ MAKE_CASE(SCSI_SENSEQ_SOURCE_EMPTY)
+ }
+ break;
+ case SCSI_ADSENSE_INVALID_MEDIA:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_MEDIA_INSTALLED)
+ MAKE_CASE(SCSI_SENSEQ_UNKNOWN_FORMAT)
+ MAKE_CASE(SCSI_SENSEQ_INCOMPATIBLE_FORMAT)
+ MAKE_CASE(SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED)
+ }
+ break;
+ case SCSI_ADSENSE_OPERATOR_REQUEST:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_STATE_CHANGE_INPUT)
+ MAKE_CASE(SCSI_SENSEQ_MEDIUM_REMOVAL)
+ MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_ENABLE)
+ MAKE_CASE(SCSI_SENSEQ_WRITE_PROTECT_DISABLE)
+ }
+ break;
+ case SCSI_ADSENSE_COPY_PROTECTION_FAILURE:
+ switch (adSenseCodeQual){
+ MAKE_CASE(SCSI_SENSEQ_AUTHENTICATION_FAILURE)
+ MAKE_CASE(SCSI_SENSEQ_KEY_NOT_PRESENT)
+ MAKE_CASE(SCSI_SENSEQ_KEY_NOT_ESTABLISHED)
+ MAKE_CASE(SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION)
+ MAKE_CASE(SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT)
+ MAKE_CASE(SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR)
+ }
+ break;
+ }
+ }
+
+ return adSenseCodeQualStr;
+ }
+
+
+ /*
+ * DbgCheckReturnedPkt
+ *
+ * Check a completed TRANSFER_PACKET for all sorts of error conditions
+ * and warn/trap appropriately.
+ */
+ VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt)
+ {
+ PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
+
+ ASSERT(Pkt->Srb.OriginalRequest == Pkt->Irp);
+ ASSERT(Pkt->Srb.DataBuffer == Pkt->BufPtrCopy);
+ ASSERT(Pkt->Srb.DataTransferLength <= Pkt->BufLenCopy);
+ ASSERT(!Pkt->Irp->CancelRoutine);
+
+ if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_PENDING){
+ DBGERR(("SRB completed with status PENDING in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
+ Pkt,
+ DBGGETSCSIOPSTR(&Pkt->Srb),
+ DBGGETSRBSTATUSSTR(&Pkt->Srb),
+ (ULONG)Pkt->Srb.SrbStatus,
+ Pkt->Irp->IoStatus.Status));
+ }
+ else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){
+ /*
+ * Make sure SRB and IRP status match.
+ */
+ if (!NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
+ DBGWARN(("SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
+ Pkt,
+ DBGGETSCSIOPSTR(&Pkt->Srb),
+ DBGGETSRBSTATUSSTR(&Pkt->Srb),
+ (ULONG)Pkt->Srb.SrbStatus,
+ Pkt->Irp->IoStatus.Status));
+ }
+
+ if (Pkt->Irp->IoStatus.Information != Pkt->Srb.DataTransferLength){
+ DBGERR(("SRB and IRP result transfer lengths don't match in succeeded packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%xh).",
+ Pkt,
+ DBGGETSCSIOPSTR(&Pkt->Srb),
+ DBGGETSRBSTATUSSTR(&Pkt->Srb),
+ Pkt->Srb.DataTransferLength,
+ Pkt->Irp->IoStatus.Information));
+ }
+ }
+ else {
+ if (NT_SUCCESS(Pkt->Irp->IoStatus.Status)){
+ DBGWARN(("SRB and IRP status don't match in packet %ph: (op=%s srbstat=%s(%xh), irpstat=%xh)",
+ Pkt,
+ DBGGETSCSIOPSTR(&Pkt->Srb),
+ DBGGETSRBSTATUSSTR(&Pkt->Srb),
+ (ULONG)Pkt->Srb.SrbStatus,
+ Pkt->Irp->IoStatus.Status));
+ }
+ DBGTRACE(ClassDebugWarning, ("Packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)",
+ Pkt,
+ DBGGETSCSIOPSTR(&Pkt->Srb),
+ DBGGETSRBSTATUSSTR(&Pkt->Srb),
+ (ULONG)Pkt->Srb.SrbStatus,
+ Pkt->Irp->IoStatus.Status,
+ DBGGETSENSECODESTR(&Pkt->Srb),
+ DBGGETADSENSECODESTR(&Pkt->Srb),
+ DBGGETADSENSEQUALIFIERSTR(&Pkt->Srb)));
+
+ /*
+ * If the SRB failed with underrun or overrun, then the actual
+ * transferred length should be returned in both SRB and IRP.
+ * (SRB's only have an error status for overrun, so it's overloaded).
+ */
+ if ((SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) &&
+ (Pkt->Irp->IoStatus.Information != Pkt->Srb.DataTransferLength)){
+ DBGERR(("SRB and IRP result transfer lengths don't match in failed packet %ph: (op=%s, SrbStatus=%s, Srb.DataTransferLength=%xh, Irp->IoStatus.Information=%xh).",
+ Pkt,
+ DBGGETSCSIOPSTR(&Pkt->Srb),
+ DBGGETSRBSTATUSSTR(&Pkt->Srb),
+ Pkt->Srb.DataTransferLength,
+ Pkt->Irp->IoStatus.Information));
+ }
+ }
+
+
+ /*
+ * Some miniport drivers have been caught changing the SCSI operation
+ * code in the SRB. This is absolutely disallowed as it breaks our error handling.
+ */
+ switch (pCdb->CDB10.OperationCode){
+ case SCSIOP_MEDIUM_REMOVAL:
+ case SCSIOP_MODE_SENSE:
+ case SCSIOP_READ_CAPACITY:
+ case SCSIOP_READ:
+ case SCSIOP_WRITE:
+ case SCSIOP_START_STOP_UNIT:
+ break;
+ default:
+ DBGERR(("Miniport illegally changed Srb.Cdb.OperationCode in packet %ph failed (op=%s srbstat=%s(%xh), irpstat=%xh, sense=%s/%s/%s)",
+ Pkt,
+ DBGGETSCSIOPSTR(&Pkt->Srb),
+ DBGGETSRBSTATUSSTR(&Pkt->Srb),
+ (ULONG)Pkt->Srb.SrbStatus,
+ Pkt->Irp->IoStatus.Status,
+ DBGGETSENSECODESTR(&Pkt->Srb),
+ DBGGETADSENSECODESTR(&Pkt->Srb),
+ DBGGETADSENSEQUALIFIERSTR(&Pkt->Srb)));
+ break;
+ }
+
+ }
+
+#else
+
+ // We have to keep this in the retail build for legacy.
+ VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...)
+ {
+ DbgPrint("retail build\n");
+ }
+
+#endif
+
diff --git a/reactos/drivers/storage/classpnp/debug.h b/reactos/drivers/storage/classpnp/debug.h
new file mode 100644
index 00000000000..2fa64c41fe1
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/debug.h
@@ -0,0 +1,148 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ debug.h
+
+Abstract:
+
+
+Author:
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+
+VOID ClassDebugPrint(CLASS_DEBUG_LEVEL DebugPrintLevel, PCCHAR DebugMessage, ...);
+
+#if DBG
+
+ typedef struct _CLASSPNP_GLOBALS {
+
+ //
+ // whether or not to ASSERT for lost irps
+ //
+
+ ULONG BreakOnLostIrps;
+ ULONG SecondsToWaitForIrps;
+
+ //
+ // use a buffered debug print to help
+ // catch timing issues that do not
+ // reproduce with std debugprints enabled
+ //
+
+ ULONG UseBufferedDebugPrint;
+ ULONG UseDelayedRetry;
+
+ //
+ // the next four are the buffered printing support
+ // (currently unimplemented) and require the spinlock
+ // to use
+ //
+
+ ULONG Index; // index into buffer
+ KSPIN_LOCK SpinLock;
+ PUCHAR Buffer; // requires spinlock to access
+ ULONG NumberOfBuffers; // number of buffers available
+ SIZE_T EachBufferSize; // size of each buffer
+
+ //
+ // interlocked variables to initialize
+ // this data only once
+ //
+
+ LONG Initializing;
+ LONG Initialized;
+
+ } CLASSPNP_GLOBALS, *PCLASSPNP_GLOBALS;
+
+ #define DBGTRACE(dbgTraceLevel, args_in_parens) \
+ if (ClassDebug & (1 << (dbgTraceLevel+15))){ \
+ DbgPrint("CLASSPNP> *** TRACE *** (file %s, line %d)\n", __FILE__, __LINE__ ); \
+ DbgPrint(" > "); \
+ DbgPrint args_in_parens; \
+ DbgPrint("\n"); \
+ if (DebugTrapOnWarn && (dbgTraceLevel == ClassDebugWarning)){ \
+ DbgBreakPoint(); \
+ } \
+ }
+ #define DBGWARN(args_in_parens) \
+ { \
+ DbgPrint("CLASSPNP> *** WARNING *** (file %s, line %d)\n", __FILE__, __LINE__ ); \
+ DbgPrint(" > "); \
+ DbgPrint args_in_parens; \
+ DbgPrint("\n"); \
+ if (DebugTrapOnWarn){ \
+ DbgBreakPoint(); \
+ } \
+ }
+ #define DBGERR(args_in_parens) \
+ { \
+ DbgPrint("CLASSPNP> *** ERROR *** (file %s, line %d)\n", __FILE__, __LINE__ ); \
+ DbgPrint(" > "); \
+ DbgPrint args_in_parens; \
+ DbgPrint("\n"); \
+ DbgBreakPoint(); \
+ }
+ #define DBGTRAP(args_in_parens) \
+ { \
+ DbgPrint("CLASSPNP> *** COVERAGE TRAP *** (file %s, line %d)\n", __FILE__, __LINE__ ); \
+ DbgPrint(" > "); \
+ DbgPrint args_in_parens; \
+ DbgPrint("\n"); \
+ DbgBreakPoint(); \
+ }
+
+
+ #define DBGGETIOCTLSTR(_ioctl) DbgGetIoctlStr(_ioctl)
+ #define DBGGETSCSIOPSTR(_pSrb) DbgGetScsiOpStr(_pSrb)
+ #define DBGGETSENSECODESTR(_pSrb) DbgGetSenseCodeStr(_pSrb)
+ #define DBGGETADSENSECODESTR(_pSrb) DbgGetAdditionalSenseCodeStr(_pSrb)
+ #define DBGGETADSENSEQUALIFIERSTR(_pSrb) DbgGetAdditionalSenseCodeQualifierStr(_pSrb)
+ #define DBGCHECKRETURNEDPKT(_pkt) DbgCheckReturnedPkt(_pkt)
+ #define DBGGETSRBSTATUSSTR(_pSrb) DbgGetSrbStatusStr(_pSrb)
+
+ VOID ClasspInitializeDebugGlobals();
+ char *DbgGetIoctlStr(ULONG ioctl);
+ char *DbgGetScsiOpStr(PSCSI_REQUEST_BLOCK Srb);
+ char *DbgGetSenseCodeStr(PSCSI_REQUEST_BLOCK Srb);
+ char *DbgGetAdditionalSenseCodeStr(PSCSI_REQUEST_BLOCK Srb);
+ char *DbgGetAdditionalSenseCodeQualifierStr(PSCSI_REQUEST_BLOCK Srb);
+ VOID DbgCheckReturnedPkt(TRANSFER_PACKET *Pkt);
+ char *DbgGetSrbStatusStr(PSCSI_REQUEST_BLOCK Srb);
+
+
+ extern CLASSPNP_GLOBALS ClasspnpGlobals;
+ extern LONG ClassDebug;
+ extern BOOLEAN DebugTrapOnWarn;
+
+#else
+
+ #define ClasspInitializeDebugGlobals()
+ #define DBGWARN(args_in_parens)
+ #define DBGERR(args_in_parens)
+ #define DBGTRACE(dbgTraceLevel, args_in_parens)
+ #define DBGTRAP(args_in_parens)
+
+ #define DBGGETIOCTLSTR(_ioctl)
+ #define DBGGETSCSIOPSTR(_pSrb)
+ #define DBGGETSENSECODESTR(_pSrb)
+ #define DBGGETADSENSECODESTR(_pSrb)
+ #define DBGGETADSENSEQUALIFIERSTR(_pSrb)
+ #define DBGCHECKRETURNEDPKT(_pkt)
+ #define DBGGETSRBSTATUSSTR(_pSrb)
+
+#endif
+
+
diff --git a/reactos/drivers/storage/classpnp/dictlib.c b/reactos/drivers/storage/classpnp/dictlib.c
new file mode 100644
index 00000000000..ba479fe9b52
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/dictlib.c
@@ -0,0 +1,216 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1990 - 1999
+
+Module Name:
+
+ dictlib.c
+
+Abstract:
+
+ Support library for maintaining a dictionary list (list of objects
+ referenced by a key value).
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+ This module generates a static library
+
+Revision History:
+
+--*/
+
+#include
+#include
+
+#define DICTIONARY_SIGNATURE (((ULONG)'dict' << 32) + 'sig ')
+
+struct _DICTIONARY_HEADER {
+ struct _DICTIONARY_HEADER* Next;
+ ULONGLONG Key;
+ UCHAR Data[0];
+};
+
+struct _DICTIONARY_HEADER;
+typedef struct _DICTIONARY_HEADER DICTIONARY_HEADER, *PDICTIONARY_HEADER;
+
+
+VOID
+InitializeDictionary(
+ IN PDICTIONARY Dictionary
+ )
+{
+ RtlZeroMemory(Dictionary, sizeof(Dictionary));
+ Dictionary->Signature = DICTIONARY_SIGNATURE;
+ KeInitializeSpinLock(&Dictionary->SpinLock);
+ return;
+}
+
+
+BOOLEAN
+TestDictionarySignature(
+ IN PDICTIONARY Dictionary
+ )
+{
+ return Dictionary->Signature == DICTIONARY_SIGNATURE;
+}
+
+NTSTATUS
+AllocateDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key,
+ IN ULONG Size,
+ IN ULONG Tag,
+ OUT PVOID *Entry
+ )
+{
+ PDICTIONARY_HEADER header;
+ KIRQL oldIrql;
+ PDICTIONARY_HEADER *entry;
+
+ NTSTATUS status = STATUS_SUCCESS;
+
+ *Entry = NULL;
+
+ header = ExAllocatePoolWithTag(NonPagedPool,
+ Size + sizeof(DICTIONARY_HEADER),
+ Tag);
+
+ if(header == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory(header, sizeof(DICTIONARY_HEADER) + Size);
+ header->Key = Key;
+
+ //
+ // Find the correct location for this entry in the dictionary.
+ //
+
+ KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
+
+ TRY {
+
+ entry = &(Dictionary->List);
+
+ while(*entry != NULL) {
+ if((*entry)->Key == Key) {
+
+ //
+ // Dictionary must have unique keys.
+ //
+
+ status = STATUS_OBJECT_NAME_COLLISION;
+ LEAVE;
+
+ } else if ((*entry)->Key < Key) {
+
+ //
+ // We will go ahead and insert the key in here.
+ //
+ break;
+ } else {
+ entry = &((*entry)->Next);
+ }
+ }
+
+ //
+ // If we make it here then we will go ahead and do the insertion.
+ //
+
+ header->Next = *entry;
+ *entry = header;
+
+ } FINALLY {
+ KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
+
+ if(!NT_SUCCESS(status)) {
+ ExFreePool(header);
+ } else {
+ *Entry = (PVOID) header->Data;
+ }
+ }
+ return status;
+}
+
+
+PVOID
+GetDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN ULONGLONG Key
+ )
+{
+ PDICTIONARY_HEADER entry;
+ PVOID data;
+ KIRQL oldIrql;
+
+
+ data = NULL;
+
+ KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
+
+ entry = Dictionary->List;
+ while (entry != NULL) {
+
+ if (entry->Key == Key) {
+ data = entry->Data;
+ break;
+ } else {
+ entry = entry->Next;
+ }
+ }
+
+ KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
+
+ return data;
+}
+
+
+VOID
+FreeDictionaryEntry(
+ IN PDICTIONARY Dictionary,
+ IN PVOID Entry
+ )
+{
+ PDICTIONARY_HEADER header;
+ PDICTIONARY_HEADER *entry;
+ KIRQL oldIrql;
+ BOOLEAN found;
+
+ found = FALSE;
+ header = CONTAINING_RECORD(Entry, DICTIONARY_HEADER, Data);
+
+ KeAcquireSpinLock(&(Dictionary->SpinLock), &oldIrql);
+
+ entry = &(Dictionary->List);
+ while(*entry != NULL) {
+
+ if(*entry == header) {
+ *entry = header->Next;
+ found = TRUE;
+ break;
+ } else {
+ entry = &(*entry)->Next;
+ }
+ }
+
+ KeReleaseSpinLock(&(Dictionary->SpinLock), oldIrql);
+
+ //
+ // calling this w/an invalid pointer invalidates the dictionary system,
+ // so ASSERT() that we never try to Free something not in the list
+ //
+
+ ASSERT(found);
+ if (found) {
+ ExFreePool(header);
+ }
+
+ return;
+
+}
+
+
diff --git a/reactos/drivers/storage/classpnp/lock.c b/reactos/drivers/storage/classpnp/lock.c
new file mode 100644
index 00000000000..deea29eead6
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/lock.c
@@ -0,0 +1,418 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1990 - 1998
+
+Module Name:
+
+ lock.c
+
+Abstract:
+
+ This is the NT SCSI port driver.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+ This module is a driver dll for scsi miniports.
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+
+LONG LockHighWatermark = 0;
+LONG LockLowWatermark = 0;
+LONG MaxLockedMinutes = 5;
+
+//
+// Structure used for tracking remove lock allocations in checked builds
+//
+typedef struct _REMOVE_TRACKING_BLOCK {
+ struct _REMOVE_TRACKING_BLOCK *NextBlock;
+ PVOID Tag;
+ LARGE_INTEGER TimeLocked;
+ PCSTR File;
+ ULONG Line;
+} REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK;
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassAcquireRemoveLockEx()
+
+Routine Description:
+
+ This routine is called to acquire the remove lock on the device object.
+ While the lock is held, the caller can assume that no pending pnp REMOVE
+ requests will be completed.
+
+ The lock should be acquired immediately upon entering a dispatch routine.
+ It should also be acquired before creating any new reference to the
+ device object if there's a chance of releasing the reference before the
+ new one is done.
+
+ This routine will return TRUE if the lock was successfully acquired or
+ FALSE if it cannot be because the device object has already been removed.
+
+Arguments:
+
+ DeviceObject - the device object to lock
+
+ Tag - Used for tracking lock allocation and release. If an irp is
+ specified when acquiring the lock then the same Tag must be
+ used to release the lock before the Tag is completed.
+
+Return Value:
+
+ The value of the IsRemoved flag in the device extension. If this is
+ non-zero then the device object has received a Remove irp and non-cleanup
+ IRP's should fail.
+
+ If the value is REMOVE_COMPLETE, the caller should not even release the
+ lock.
+
+--*/
+ULONG
+ClassAcquireRemoveLockEx(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OPTIONAL PVOID Tag,
+ IN PCSTR File,
+ IN ULONG Line
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ LONG lockValue;
+
+
+
+ //
+ // Grab the remove lock
+ //
+ lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
+
+ #if DBG
+
+ DebugPrint((ClassDebugRemoveLock, "ClassAcquireRemoveLock: "
+ "Acquired for Object %p & irp %p - count is %d\n",
+ DeviceObject, Tag, lockValue));
+
+ ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
+ (lockValue > 0));
+
+ ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
+ ((LockHighWatermark == 0) ||
+ (lockValue != LockHighWatermark)));
+
+ if (commonExtension->IsRemoved != REMOVE_COMPLETE){
+ PREMOVE_TRACKING_BLOCK trackingBlock;
+
+ trackingBlock = ExAllocatePool(NonPagedPool,
+ sizeof(REMOVE_TRACKING_BLOCK));
+
+ if(trackingBlock == NULL) {
+
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ &oldIrql);
+
+ commonExtension->RemoveTrackingUntrackedCount++;
+
+ DebugPrint((ClassDebugWarning, ">>>>>ClassAcquireRemoveLock: "
+ "Cannot track Tag %p - currently %d untracked requsts\n",
+ Tag, commonExtension->RemoveTrackingUntrackedCount));
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ oldIrql);
+ }
+ else {
+ PREMOVE_TRACKING_BLOCK *removeTrackingList =
+ (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList;
+
+ KIRQL oldIrql;
+
+ trackingBlock->Tag = Tag;
+
+ trackingBlock->File = File;
+ trackingBlock->Line = Line;
+
+ KeQueryTickCount((&trackingBlock->TimeLocked));
+
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ &oldIrql);
+
+ while(*removeTrackingList != NULL) {
+
+ if((*removeTrackingList)->Tag > Tag) {
+ break;
+ }
+
+ if((*removeTrackingList)->Tag == Tag) {
+
+ DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
+ "already tracking Tag %p\n", Tag));
+ DebugPrint((ClassDebugError, ">>>>>ClassAcquireRemoveLock: "
+ "acquired in file %s on line %d\n",
+ (*removeTrackingList)->File,
+ (*removeTrackingList)->Line));
+ ASSERT(FALSE);
+ }
+
+ removeTrackingList = &((*removeTrackingList)->NextBlock);
+ }
+
+ trackingBlock->NextBlock = *removeTrackingList;
+ *removeTrackingList = trackingBlock;
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ oldIrql);
+
+ }
+ }
+
+ #endif
+
+ return (commonExtension->IsRemoved);
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassReleaseRemoveLock()
+
+Routine Description:
+
+ This routine is called to release the remove lock on the device object. It
+ must be called when finished using a previously locked reference to the
+ device object. If an Tag was specified when acquiring the lock then the
+ same Tag must be specified when releasing the lock.
+
+ When the lock count reduces to zero, this routine will signal the waiting
+ remove Tag to delete the device object. As a result the DeviceObject
+ pointer should not be used again once the lock has been released.
+
+Arguments:
+
+ DeviceObject - the device object to lock
+
+ Tag - The irp (if any) specified when acquiring the lock. This is used
+ for lock tracking purposes
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClassReleaseRemoveLock(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN OPTIONAL PIRP Tag
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ LONG lockValue;
+
+ #if DBG
+ PREMOVE_TRACKING_BLOCK *listEntry =
+ (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList;
+
+ BOOLEAN found = FALSE;
+
+ LONGLONG maxCount;
+
+ BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE);
+
+ KIRQL oldIrql;
+
+ if(isRemoved) {
+ DBGTRAP(("ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen"));
+ InterlockedDecrement(&(commonExtension->RemoveLock));
+ return;
+ }
+
+ //
+ // Check the tick count and make sure this thing hasn't been locked
+ // for more than MaxLockedMinutes.
+ //
+
+ maxCount = KeQueryTimeIncrement() * 10; // microseconds
+ maxCount *= 1000; // milliseconds
+ maxCount *= 1000; // seconds
+ maxCount *= 60; // minutes
+ maxCount *= MaxLockedMinutes;
+
+ DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
+ "maxCount = %0I64x\n", maxCount));
+
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ &oldIrql);
+
+ while(*listEntry != NULL) {
+
+ PREMOVE_TRACKING_BLOCK block;
+ LARGE_INTEGER difference;
+
+ block = *listEntry;
+
+ KeQueryTickCount((&difference));
+
+ difference.QuadPart -= block->TimeLocked.QuadPart;
+
+ DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
+ "Object %p (tag %p) locked for %I64d ticks\n",
+ DeviceObject, block->Tag, difference.QuadPart));
+
+ if(difference.QuadPart >= maxCount) {
+
+ DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
+ "Object %p (tag %p) locked for %I64d ticks - TOO LONG\n",
+ DeviceObject, block->Tag, difference.QuadPart));
+ DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
+ "Lock acquired in file %s on line %d\n",
+ block->File, block->Line));
+ ASSERT(FALSE);
+ }
+
+ if((found == FALSE) && ((*listEntry)->Tag == Tag)) {
+
+ *listEntry = block->NextBlock;
+ ExFreePool(block);
+ found = TRUE;
+
+ } else {
+
+ listEntry = &((*listEntry)->NextBlock);
+
+ }
+ }
+
+ if(!found) {
+ if(commonExtension->RemoveTrackingUntrackedCount == 0) {
+ DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
+ "Couldn't find Tag %p in the lock tracking list\n",
+ Tag));
+ ASSERT(FALSE);
+ } else {
+ DebugPrint((ClassDebugError, ">>>>>ClassReleaseRemoveLock: "
+ "Couldn't find Tag %p in the lock tracking list - "
+ "may be one of the %d untracked requests still "
+ "outstanding\n",
+ Tag,
+ commonExtension->RemoveTrackingUntrackedCount));
+
+ commonExtension->RemoveTrackingUntrackedCount--;
+ ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0);
+ }
+ }
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ oldIrql);
+
+ #endif
+
+ lockValue = InterlockedDecrement(&commonExtension->RemoveLock);
+
+ DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
+ "Released for Object %p & irp %p - count is %d\n",
+ DeviceObject, Tag, lockValue));
+
+ ASSERT(lockValue >= 0);
+
+ ASSERTMSG("RemoveLock decreased to meet LockLowWatermark",
+ ((LockLowWatermark == 0) || !(lockValue == LockLowWatermark)));
+
+ if(lockValue == 0) {
+
+ ASSERT(commonExtension->IsRemoved);
+
+ //
+ // The device needs to be removed. Signal the remove event
+ // that it's safe to go ahead.
+ //
+
+ DebugPrint((ClassDebugRemoveLock, "ClassReleaseRemoveLock: "
+ "Release for object %p & irp %p caused lock to go to zero\n",
+ DeviceObject, Tag));
+
+ KeSetEvent(&commonExtension->RemoveEvent,
+ IO_NO_INCREMENT,
+ FALSE);
+
+ }
+ return;
+}
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassCompleteRequest()
+
+Routine Description:
+
+ This routine is a wrapper around (and should be used instead of)
+ IoCompleteRequest. It is used primarily for debugging purposes.
+ The routine will assert if the Irp being completed is still holding
+ the release lock.
+
+Arguments:
+
+ DeviceObject - the device object that was handling this request
+
+ Irp - the irp to be completed by IoCompleteRequest
+
+ PriorityBoost - the priority boost to pass to IoCompleteRequest
+
+Return Value:
+
+ none
+
+--*/
+VOID
+ClassCompleteRequest(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN CCHAR PriorityBoost
+ )
+{
+
+ #if DBG
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PREMOVE_TRACKING_BLOCK *listEntry =
+ (PREMOVE_TRACKING_BLOCK)&commonExtension->RemoveTrackingList;
+
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
+ &oldIrql);
+
+ while(*listEntry != NULL) {
+
+ if((*listEntry)->Tag == Irp) {
+ break;
+ }
+
+ listEntry = &((*listEntry)->NextBlock);
+ }
+
+ if(*listEntry != NULL) {
+
+ DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
+ "Irp %p completed while still holding the remove lock\n",
+ Irp));
+ DebugPrint((ClassDebugError, ">>>>>ClassCompleteRequest: "
+ "Lock acquired in file %s on line %d\n",
+ (*listEntry)->File, (*listEntry)->Line));
+ ASSERT(FALSE);
+ }
+
+ KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
+ #endif
+
+ IoCompleteRequest(Irp, PriorityBoost);
+ return;
+} // end ClassCompleteRequest()
+
diff --git a/reactos/drivers/storage/classpnp/obsolete.c b/reactos/drivers/storage/classpnp/obsolete.c
new file mode 100644
index 00000000000..1dec49c4306
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/obsolete.c
@@ -0,0 +1,1060 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ obsolete.c
+
+Abstract:
+
+ THESE ARE EXPORTED CLASSPNP FUNCTIONS (and their subroutines)
+ WHICH ARE NOW OBSOLETE.
+ BUT WE NEED TO KEEP THEM AROUND FOR LEGACY REASONS.
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+PIRP ClassRemoveCScanList(IN PCSCAN_LIST List);
+VOID ClasspInitializeCScanList(IN PCSCAN_LIST List);
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(PAGE, ClassDeleteSrbLookasideList)
+ #pragma alloc_text(PAGE, ClassInitializeSrbLookasideList)
+#endif
+
+typedef struct _CSCAN_LIST_ENTRY {
+ LIST_ENTRY Entry;
+ ULONGLONG BlockNumber;
+} CSCAN_LIST_ENTRY, *PCSCAN_LIST_ENTRY;
+
+
+
+
+
+/*
+ * ClassSplitRequest
+ *
+ * This is a legacy exported function.
+ * It is called by storage miniport driver that have their own
+ * StartIo routine when the transfer size is too large for the hardware.
+ * We map it to our new read/write handler.
+ */
+VOID ClassSplitRequest(IN PDEVICE_OBJECT Fdo, IN PIRP Irp, IN ULONG MaximumBytes)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+
+ if (MaximumBytes > fdoData->HwMaxXferLen) {
+ DBGERR(("ClassSplitRequest - driver requesting split to size that "
+ "hardware is unable to handle!\n"));
+ }
+
+ if (MaximumBytes < fdoData->HwMaxXferLen){
+ DBGWARN(("ClassSplitRequest - driver requesting smaller HwMaxXferLen "
+ "than required"));
+ fdoData->HwMaxXferLen = MAX(MaximumBytes, PAGE_SIZE);
+ }
+
+ ServiceTransferRequest(Fdo, Irp);
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassIoCompleteAssociated()
+
+Routine Description:
+
+ This routine executes when the port driver has completed a request.
+ It looks at the SRB status in the completing SRB and if not success
+ it checks for valid request sense buffer information. If valid, the
+ info is used to update status with more precise message of type of
+ error. This routine deallocates the SRB. This routine is used for
+ requests which were build by split request. After it has processed
+ the request it decrements the Irp count in the master Irp. If the
+ count goes to zero then the master Irp is completed.
+
+Arguments:
+
+ Fdo - Supplies the functional device object which represents the target.
+
+ Irp - Supplies the Irp which has completed.
+
+ Context - Supplies a pointer to the SRB.
+
+Return Value:
+
+ NT status
+
+--*/
+NTSTATUS
+ClassIoCompleteAssociated(
+ IN PDEVICE_OBJECT Fdo,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = Context;
+
+ PIRP originalIrp = Irp->AssociatedIrp.MasterIrp;
+ LONG irpCount;
+
+ NTSTATUS status;
+ BOOLEAN retry;
+
+ DBGWARN(("ClassIoCompleteAssociated is OBSOLETE !"));
+
+ //
+ // Check SRB status for success of completing request.
+ //
+
+ if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ ULONG retryInterval;
+
+ DebugPrint((2,"ClassIoCompleteAssociated: IRP %p, SRB %p", Irp, srb));
+
+ //
+ // Release the queue if it is frozen.
+ //
+
+ if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
+ ClassReleaseQueue(Fdo);
+ }
+
+ retry = ClassInterpretSenseInfo(
+ Fdo,
+ srb,
+ irpStack->MajorFunction,
+ irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ?
+ irpStack->Parameters.DeviceIoControl.IoControlCode :
+ 0,
+ MAXIMUM_RETRIES -
+ ((ULONG)(ULONG_PTR)irpStack->Parameters.Others.Argument4),
+ &status,
+ &retryInterval);
+
+ //
+ // If the status is verified required and the this request
+ // should bypass verify required then retry the request.
+ //
+
+ if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME &&
+ status == STATUS_VERIFY_REQUIRED) {
+
+ status = STATUS_IO_DEVICE_ERROR;
+ retry = TRUE;
+ }
+
+ if (retry && (irpStack->Parameters.Others.Argument4--)) {
+
+ //
+ // Retry request. If the class driver has supplied a StartIo,
+ // call it directly for retries.
+ //
+
+ DebugPrint((1, "Retry request %p\n", Irp));
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ RetryRequest(Fdo, Irp, srb, TRUE, retryInterval);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ } else {
+
+ //
+ // Set status for successful request.
+ //
+
+ status = STATUS_SUCCESS;
+
+ } // end if (SRB_STATUS(srb->SrbStatus) ...
+
+ //
+ // Return SRB to list.
+ //
+
+ if (PORT_ALLOCATED_SENSE(fdoExtension, srb)) {
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExtension, srb);
+ }
+
+ ClassFreeOrReuseSrb(fdoExtension, srb);
+
+ //
+ // Set status in completing IRP.
+ //
+
+ Irp->IoStatus.Status = status;
+
+ DebugPrint((2, "ClassIoCompleteAssociated: Partial xfer IRP %p\n", Irp));
+
+ //
+ // Get next stack location. This original request is unused
+ // except to keep track of the completing partial IRPs so the
+ // stack location is valid.
+ //
+
+ irpStack = IoGetNextIrpStackLocation(originalIrp);
+
+ //
+ // Update status only if error so that if any partial transfer
+ // completes with error, then the original IRP will return with
+ // error. If any of the asynchronous partial transfer IRPs fail,
+ // with an error then the original IRP will return 0 bytes transfered.
+ // This is an optimization for successful transfers.
+ //
+
+ if (!NT_SUCCESS(status)) {
+
+ originalIrp->IoStatus.Status = status;
+ originalIrp->IoStatus.Information = 0;
+
+ //
+ // Set the hard error if necessary.
+ //
+
+ if (IoIsErrorUserInduced(status)) {
+
+ //
+ // Store DeviceObject for filesystem.
+ //
+
+ IoSetHardErrorOrVerifyDevice(originalIrp, Fdo);
+ }
+ }
+
+ //
+ // Decrement and get the count of remaining IRPs.
+ //
+
+ irpCount = InterlockedDecrement(
+ (PLONG)&irpStack->Parameters.Others.Argument1);
+
+ DebugPrint((2, "ClassIoCompleteAssociated: Partial IRPs left %d\n",
+ irpCount));
+
+ //
+ // Ensure that the irpCount doesn't go negative. This was happening once
+ // because classpnp would get confused if it ran out of resources when
+ // splitting the request.
+ //
+
+ ASSERT(irpCount >= 0);
+
+ if (irpCount == 0) {
+
+ //
+ // All partial IRPs have completed.
+ //
+
+ DebugPrint((2,
+ "ClassIoCompleteAssociated: All partial IRPs complete %p\n",
+ originalIrp));
+
+ if (fdoExtension->CommonExtension.DriverExtension->InitData.ClassStartIo) {
+
+ //
+ // Acquire a separate copy of the remove lock so the debugging code
+ // works okay and we don't have to hold up the completion of this
+ // irp until after we start the next packet(s).
+ //
+
+ KIRQL oldIrql;
+ UCHAR uniqueAddress;
+ ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddress);
+ ClassReleaseRemoveLock(Fdo, originalIrp);
+ ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+ IoStartNextPacket(Fdo, FALSE);
+ KeLowerIrql(oldIrql);
+
+ ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddress);
+
+ } else {
+
+ //
+ // just complete this request
+ //
+
+ ClassReleaseRemoveLock(Fdo, originalIrp);
+ ClassCompleteRequest(Fdo, originalIrp, IO_DISK_INCREMENT);
+
+ }
+
+ }
+
+ //
+ // Deallocate IRP and indicate the I/O system should not attempt any more
+ // processing.
+ //
+
+ IoFreeIrp(Irp);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // end ClassIoCompleteAssociated()
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+RetryRequest()
+
+Routine Description:
+
+ This is a wrapper around the delayed retry DPC routine, RetryRequestDPC.
+ This reinitalizes the necessary fields, queues the request, and sets
+ a timer to call the DPC if someone hasn't already done so.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - Supplies the request to be retried.
+
+ Srb - Supplies a Pointer to the SCSI request block to be retied.
+
+ Assocaiated - Indicates this is an assocatied Irp created by split request.
+
+ RetryInterval - How long, in seconds, before retrying the request.
+
+Return Value:
+
+ None
+
+--*/
+VOID
+RetryRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PSCSI_REQUEST_BLOCK Srb,
+ BOOLEAN Associated,
+ ULONG RetryInterval
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+ ULONG transferByteCount;
+
+ // This function is obsolete but is still used by some of our class drivers.
+ // DBGWARN(("RetryRequest is OBSOLETE !"));
+
+ //
+ // Determine the transfer count of the request. If this is a read or a
+ // write then the transfer count is in the Irp stack. Otherwise assume
+ // the MDL contains the correct length. If there is no MDL then the
+ // transfer length must be zero.
+ //
+
+ if (currentIrpStack->MajorFunction == IRP_MJ_READ ||
+ currentIrpStack->MajorFunction == IRP_MJ_WRITE) {
+
+ transferByteCount = currentIrpStack->Parameters.Read.Length;
+
+ } else if (Irp->MdlAddress != NULL) {
+
+ //
+ // Note this assumes that only read and write requests are spilt and
+ // other request do not need to be. If the data buffer address in
+ // the MDL and the SRB don't match then transfer length is most
+ // likely incorrect.
+ //
+
+ ASSERT(Srb->DataBuffer == MmGetMdlVirtualAddress(Irp->MdlAddress));
+ transferByteCount = Irp->MdlAddress->ByteCount;
+
+ } else {
+
+ transferByteCount = 0;
+ }
+
+ //
+ // this is a safety net. this should not normally be hit, since we are
+ // not guaranteed to be an fdoExtension
+ //
+
+ ASSERT(!TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+
+ Srb->DataTransferLength = transferByteCount;
+
+ //
+ // Zero SRB statuses.
+ //
+
+ Srb->SrbStatus = Srb->ScsiStatus = 0;
+
+ //
+ // Set the no disconnect flag, disable synchronous data transfers and
+ // disable tagged queuing. This fixes some errors.
+ // NOTE: Cannot clear these flags, just add to them
+ //
+
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ CLEAR_FLAG(Srb->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ Srb->QueueTag = SP_UNTAGGED;
+
+ //
+ // Set up major SCSI function.
+ //
+
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ nextIrpStack->Parameters.Scsi.Srb = Srb;
+
+
+ IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
+
+ {
+ LARGE_INTEGER retry100ns;
+ retry100ns.QuadPart = RetryInterval; // seconds
+ retry100ns.QuadPart *= (LONGLONG)1000 * 1000 * 10;
+
+ ClassRetryRequest(DeviceObject, Irp, retry100ns);
+ }
+ return;
+} // end RetryRequest()
+
+
+/*++
+
+ClassBuildRequest()
+
+Routine Description:
+
+ This routine allocates an SRB for the specified request then calls
+ ClasspBuildRequestEx to create a SCSI operation to read or write the device.
+
+ If no SRB is available then the request will be queued to be issued later
+ when requests are available. Drivers which do not want the queueing
+ behavior should allocate the SRB themselves and call ClasspBuildRequestEx
+ to issue it.
+
+Arguments:
+
+ Fdo - Supplies the functional device object associated with this request.
+
+ Irp - Supplies the request to be retried.
+
+Note:
+
+ If the IRP is for a disk transfer, the byteoffset field
+ will already have been adjusted to make it relative to
+ the beginning of the disk.
+
+
+Return Value:
+
+ NT Status
+
+--*/
+NTSTATUS
+ClassBuildRequest(
+ PDEVICE_OBJECT Fdo,
+ PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
+
+ PSCSI_REQUEST_BLOCK srb;
+
+ // This function is obsolete, but still called by CDROM.SYS .
+ // DBGWARN(("ClassBuildRequest is OBSOLETE !"));
+
+ //
+ // Allocate an Srb.
+ //
+
+ srb = ClasspAllocateSrb(fdoExtension);
+
+ if(srb == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ClasspBuildRequestEx(fdoExtension, Irp, srb);
+ return STATUS_SUCCESS;
+
+} // end ClassBuildRequest()
+
+
+VOID
+ClasspBuildRequestEx(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PIRP Irp,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+ClasspBuildRequestEx()
+
+Routine Description:
+
+ This routine allocates and builds an Srb for a read or write request.
+ The block address and length are supplied by the Irp. The retry count
+ is stored in the current stack for use by ClassIoComplete which
+ processes these requests when they complete. The Irp is ready to be
+ passed to the port driver when this routine returns.
+
+Arguments:
+
+ FdoExtension - Supplies the device extension associated with this request.
+
+ Irp - Supplies the request to be issued.
+
+ Srb - Supplies an SRB to be used for the request.
+
+Note:
+
+ If the IRP is for a disk transfer, the byteoffset field
+ will already have been adjusted to make it relative to
+ the beginning of the disk.
+
+
+Return Value:
+
+ NT Status
+
+--*/
+{
+ PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+
+ LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
+
+ PCDB cdb;
+ ULONG logicalBlockAddress;
+ USHORT transferBlocks;
+
+ // This function is obsolete, but still called by CDROM.SYS .
+ // DBGWARN(("ClasspBuildRequestEx is OBSOLETE !"));
+
+ //
+ // Prepare the SRB.
+ //
+
+ RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ //
+ // Calculate relative sector address.
+ //
+
+ logicalBlockAddress =
+ (ULONG)(Int64ShrlMod32(startingOffset.QuadPart,
+ FdoExtension->SectorShift));
+
+ //
+ // Write length to SRB.
+ //
+
+ Srb->Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ //
+ // Set up IRP Address.
+ //
+
+ Srb->OriginalRequest = Irp;
+
+ //
+ // Set up target ID and logical unit number.
+ //
+
+ Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ Srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
+
+ //
+ // Save byte count of transfer in SRB Extension.
+ //
+
+ Srb->DataTransferLength = currentIrpStack->Parameters.Read.Length;
+
+ //
+ // Initialize the queue actions field.
+ //
+
+ Srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
+
+ //
+ // Queue sort key is Relative Block Address.
+ //
+
+ Srb->QueueSortKey = logicalBlockAddress;
+
+ //
+ // Indicate auto request sense by specifying buffer and size.
+ //
+
+ Srb->SenseInfoBuffer = FdoExtension->SenseData;
+ Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+
+ //
+ // Set timeout value of one unit per 64k bytes of data.
+ //
+
+ Srb->TimeOutValue = ((Srb->DataTransferLength + 0xFFFF) >> 16) *
+ FdoExtension->TimeOutValue;
+
+ //
+ // Zero statuses.
+ //
+
+ Srb->SrbStatus = Srb->ScsiStatus = 0;
+ Srb->NextSrb = 0;
+
+ //
+ // Indicate that 10-byte CDB's will be used.
+ //
+
+ Srb->CdbLength = 10;
+
+ //
+ // Fill in CDB fields.
+ //
+
+ cdb = (PCDB)Srb->Cdb;
+
+ transferBlocks = (USHORT)(currentIrpStack->Parameters.Read.Length >>
+ FdoExtension->SectorShift);
+
+ //
+ // Move little endian values into CDB in big endian format.
+ //
+
+ cdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte3;
+ cdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte2;
+ cdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte1;
+ cdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddress)->Byte0;
+
+ cdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&transferBlocks)->Byte1;
+ cdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&transferBlocks)->Byte0;
+
+ //
+ // Set transfer direction flag and Cdb command.
+ //
+
+ if (currentIrpStack->MajorFunction == IRP_MJ_READ) {
+
+ DebugPrint((3, "ClassBuildRequest: Read Command\n"));
+
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
+ cdb->CDB10.OperationCode = SCSIOP_READ;
+
+ } else {
+
+ DebugPrint((3, "ClassBuildRequest: Write Command\n"));
+
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
+ cdb->CDB10.OperationCode = SCSIOP_WRITE;
+ }
+
+ //
+ // If this is not a write-through request, then allow caching.
+ //
+
+ if (!(currentIrpStack->Flags & SL_WRITE_THROUGH)) {
+
+ SET_FLAG(Srb->SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
+
+ } else {
+
+ //
+ // If write caching is enable then force media access in the
+ // cdb.
+ //
+
+ if (FdoExtension->DeviceFlags & DEV_WRITE_CACHE) {
+ cdb->CDB10.ForceUnitAccess = TRUE;
+ }
+ }
+
+ if(TEST_FLAG(Irp->Flags, (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO))) {
+ SET_FLAG(Srb->SrbFlags, SRB_CLASS_FLAGS_PAGING);
+ }
+
+ //
+ // OR in the default flags from the device object.
+ //
+
+ SET_FLAG(Srb->SrbFlags, FdoExtension->SrbFlags);
+
+ //
+ // Set up major SCSI function.
+ //
+
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ nextIrpStack->Parameters.Scsi.Srb = Srb;
+
+ //
+ // Save retry count in current IRP stack.
+ //
+
+ currentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
+
+ //
+ // Set up IoCompletion routine address.
+ //
+
+ IoSetCompletionRoutine(Irp, ClassIoComplete, Srb, TRUE, TRUE, TRUE);
+
+}
+
+
+VOID ClasspInsertCScanList(IN PLIST_ENTRY ListHead, IN PCSCAN_LIST_ENTRY Entry)
+{
+ PCSCAN_LIST_ENTRY t;
+
+ DBGWARN(("ClasspInsertCScanList is OBSOLETE !"));
+
+ //
+ // Iterate through the list. Insert this entry in the sorted list in
+ // order (after other requests for the same block). At each stop if
+ // blockNumber(Entry) >= blockNumber(t) then move on.
+ //
+
+ for(t = (PCSCAN_LIST_ENTRY) ListHead->Flink;
+ t != (PCSCAN_LIST_ENTRY) ListHead;
+ t = (PCSCAN_LIST_ENTRY) t->Entry.Flink) {
+
+ if(Entry->BlockNumber < t->BlockNumber) {
+
+ //
+ // Set the pointers in entry to the right location.
+ //
+
+ Entry->Entry.Flink = &(t->Entry);
+ Entry->Entry.Blink = t->Entry.Blink;
+
+ //
+ // Set the pointers in the surrounding elements to refer to us.
+ //
+
+ t->Entry.Blink->Flink = &(Entry->Entry);
+ t->Entry.Blink = &(Entry->Entry);
+ return;
+ }
+ }
+
+ //
+ // Insert this entry at the tail of the list. If the list was empty this
+ // will also be the head of the list.
+ //
+
+ InsertTailList(ListHead, &(Entry->Entry));
+
+}
+
+
+VOID ClassInsertCScanList(IN PCSCAN_LIST List, IN PIRP Irp, IN ULONGLONG BlockNumber, IN BOOLEAN LowPriority)
+/*++
+
+Routine Description:
+
+ This routine inserts an entry into the CScan list based on it's block number
+ and priority. It is assumed that the caller is providing synchronization
+ to the access of the list.
+
+ Low priority requests are always scheduled to run on the next sweep across
+ the disk. Normal priority requests will be inserted into the current or
+ next sweep based on the standard C-SCAN algorithm.
+
+Arguments:
+
+ List - the list to insert into
+
+ Irp - the irp to be inserted.
+
+ BlockNumber - the block number for this request.
+
+ LowPriority - indicates that the request is lower priority and should be
+ done on the next sweep across the disk.
+
+Return Value:
+
+ none
+
+--*/
+{
+ PCSCAN_LIST_ENTRY entry = (PCSCAN_LIST_ENTRY)Irp->Tail.Overlay.DriverContext;
+
+ DBGWARN(("ClassInsertCScanList is OBSOLETE !"));
+
+ //
+ // Set the block number in the entry. We need this to keep the list sorted.
+ //
+ entry->BlockNumber = BlockNumber;
+
+ //
+ // If it's a normal priority request and further down the disk than our
+ // current position then insert this entry into the current sweep.
+ //
+
+ if((LowPriority != TRUE) && (BlockNumber > List->BlockNumber)) {
+ ClasspInsertCScanList(&(List->CurrentSweep), entry);
+ } else {
+ ClasspInsertCScanList(&(List->NextSweep), entry);
+ }
+ return;
+}
+
+
+
+
+VOID ClassFreeOrReuseSrb( IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PSCSI_REQUEST_BLOCK Srb)
+/*++
+
+Routine Description:
+
+ This routine will attempt to reuse the provided SRB to start a blocked
+ read/write request.
+ If there is no need to reuse the request it will be returned
+ to the SRB lookaside list.
+
+Arguments:
+
+ Fdo - the device extension
+
+ Srb - the SRB which is to be reused or freed.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ PCLASS_PRIVATE_FDO_DATA privateData = FdoExtension->PrivateFdoData;
+ PCOMMON_DEVICE_EXTENSION commonExt = &FdoExtension->CommonExtension;
+ KIRQL oldIrql;
+ PIRP blockedIrp;
+
+ // This function is obsolete, but still called by DISK.SYS .
+ // DBGWARN(("ClassFreeOrReuseSrb is OBSOLETE !"));
+
+ //
+ // safety net. this should never occur. if it does, it's a potential
+ // memory leak.
+ //
+ ASSERT(!TEST_FLAG(Srb->SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+
+ if (commonExt->IsSrbLookasideListInitialized){
+ /*
+ * Put the SRB back in our lookaside list.
+ *
+ * BUGBUG - Some class drivers use ClassIoComplete
+ * to complete SRBs that they themselves allocated.
+ * So we may be putting a "foreign" SRB
+ * (e.g. with a different pool tag) into our lookaside list.
+ */
+ ClasspFreeSrb(FdoExtension, Srb);
+ }
+ else {
+ DBGERR(("ClassFreeOrReuseSrb: someone is trying to use an uninitialized SrbLookasideList !!!"));;
+ ExFreePool(Srb);
+ }
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDeleteSrbLookasideList()
+
+Routine Description:
+
+ This routine deletes a lookaside listhead for srbs, and should be called
+ only during the final removal.
+
+ If called at other times, the caller is responsible for
+ synchronization and removal issues.
+
+Arguments:
+
+ CommonExtension - Pointer to the CommonExtension containing the listhead.
+
+Return Value:
+
+ None
+
+--*/
+VOID ClassDeleteSrbLookasideList(IN PCOMMON_DEVICE_EXTENSION CommonExtension)
+{
+ PAGED_CODE();
+
+ // This function is obsolete, but is still called by some of our code.
+ // DBGWARN(("ClassDeleteSrbLookasideList is OBSOLETE !"));
+
+ if (CommonExtension->IsSrbLookasideListInitialized){
+ CommonExtension->IsSrbLookasideListInitialized = FALSE;
+ ExDeleteNPagedLookasideList(&CommonExtension->SrbLookasideList);
+ }
+ else {
+ DBGWARN(("ClassDeleteSrbLookasideList: attempt to delete uninitialized or freed srblookasidelist"));
+ }
+}
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassInitializeSrbLookasideList()
+
+Routine Description:
+
+ This routine sets up a lookaside listhead for srbs, and should be called
+ only from the ClassInitDevice() routine to prevent race conditions.
+
+ If called from other locations, the caller is responsible for
+ synchronization and removal issues.
+
+Arguments:
+
+ CommonExtension - Pointer to the CommonExtension containing the listhead.
+
+ NumberElements - Supplies the maximum depth of the lookaside list.
+
+
+Note:
+
+ The Windows 2000 version of classpnp did not return any status value from
+ this call.
+
+--*/
+
+VOID ClassInitializeSrbLookasideList( IN PCOMMON_DEVICE_EXTENSION CommonExtension,
+ IN ULONG NumberElements)
+{
+ PAGED_CODE();
+
+ // This function is obsolete, but still called by DISK.SYS .
+ // DBGWARN(("ClassInitializeSrbLookasideList is OBSOLETE !"));
+
+ ASSERT(!CommonExtension->IsSrbLookasideListInitialized);
+ if (!CommonExtension->IsSrbLookasideListInitialized){
+
+ ExInitializeNPagedLookasideList(&CommonExtension->SrbLookasideList,
+ NULL,
+ NULL,
+ NonPagedPool,
+ sizeof(SCSI_REQUEST_BLOCK),
+ '$scS',
+ (USHORT)NumberElements);
+
+ CommonExtension->IsSrbLookasideListInitialized = TRUE;
+ }
+
+}
+
+
+
+
+VOID ClasspInitializeCScanList(IN PCSCAN_LIST List)
+{
+ PAGED_CODE();
+ RtlZeroMemory(List, sizeof(CSCAN_LIST));
+ InitializeListHead(&(List->CurrentSweep));
+ InitializeListHead(&(List->NextSweep));
+}
+
+
+
+VOID ClasspStartNextSweep(PCSCAN_LIST List)
+{
+ ASSERT(IsListEmpty(&(List->CurrentSweep)) == TRUE);
+
+ //
+ // If the next sweep is empty then there's nothing to do.
+ //
+
+ if(IsListEmpty(&(List->NextSweep))) {
+ return;
+ }
+
+ //
+ // Copy the next sweep list head into the current sweep list head.
+ //
+
+ List->CurrentSweep = List->NextSweep;
+
+ //
+ // Unlink the next sweep list from the list head now that we have a copy
+ // of it.
+ //
+
+ InitializeListHead(&(List->NextSweep));
+
+ //
+ // Update the next sweep list to point back to the current sweep list head.
+ //
+
+ List->CurrentSweep.Flink->Blink = &(List->CurrentSweep);
+ List->CurrentSweep.Blink->Flink = &(List->CurrentSweep);
+
+ return;
+}
+
+
+
+PIRP ClassRemoveCScanList(IN PCSCAN_LIST List)
+{
+ PCSCAN_LIST_ENTRY entry;
+
+ //
+ // If the current sweep is empty then promote the next sweep.
+ //
+
+ if(IsListEmpty(&(List->CurrentSweep))) {
+ ClasspStartNextSweep(List);
+ }
+
+ //
+ // If the current sweep is still empty then we're done.
+ //
+
+ if(IsListEmpty(&(List->CurrentSweep))) {
+ return NULL;
+ }
+
+ //
+ // Remove the head entry from the current sweep. Record it's block number
+ // so that nothing before it on the disk gets into the current sweep.
+ //
+
+ entry = (PCSCAN_LIST_ENTRY) RemoveHeadList(&(List->CurrentSweep));
+
+ List->BlockNumber = entry->BlockNumber;
+
+ return CONTAINING_RECORD(entry, IRP, Tail.Overlay.DriverContext);
+}
+
diff --git a/reactos/drivers/storage/classpnp/power.c b/reactos/drivers/storage/classpnp/power.c
new file mode 100644
index 00000000000..26d4d0172c2
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/power.c
@@ -0,0 +1,1607 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ class.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "stddef.h"
+#include "ntddk.h"
+#include "scsi.h"
+#include "classp.h"
+
+#include
+
+#define CLASS_TAG_POWER 'WLcS'
+
+NTSTATUS
+ClasspPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN CLASS_POWER_OPTIONS Options
+ );
+
+NTSTATUS
+ClasspPowerDownCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PCLASS_POWER_CONTEXT Context
+ );
+
+NTSTATUS
+ClasspPowerUpCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PCLASS_POWER_CONTEXT Context
+ );
+
+VOID
+RetryPowerRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PCLASS_POWER_CONTEXT Context
+ );
+
+NTSTATUS
+ClasspStartNextPowerIrpCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassDispatchPower()
+
+Routine Description:
+
+ This routine acquires the removelock for the irp and then calls the
+ appropriate power callback.
+
+Arguments:
+
+ DeviceObject -
+ Irp -
+
+Return Value:
+
+--*/
+NTSTATUS
+ClassDispatchPower(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ ULONG isRemoved;
+ PCLASS_POWER_DEVICE powerRoutine = NULL;
+
+ //
+ // NOTE: This code may be called at PASSIVE or DISPATCH, depending
+ // upon the device object it is being called for.
+ // don't do anything that would break under either circumstance.
+ //
+
+ NTSTATUS status;
+
+ isRemoved = ClassAcquireRemoveLock(DeviceObject, Irp);
+
+ if(isRemoved) {
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;
+ PoStartNextPowerIrp(Irp);
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ return commonExtension->DevInfo->ClassPowerDevice(DeviceObject, Irp);
+} // end ClassDispatchPower()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspPowerUpCompletion()
+
+Routine Description:
+
+ This routine is used for intermediate completion of a power up request.
+ PowerUp requires four requests to be sent to the lower driver in sequence.
+
+ * The queue is "power locked" to ensure that the class driver power-up
+ work can be done before request processing resumes.
+
+ * The power irp is sent down the stack for any filter drivers and the
+ port driver to return power and resume command processing for the
+ device. Since the queue is locked, no queued irps will be sent
+ immediately.
+
+ * A start unit command is issued to the device with appropriate flags
+ to override the "power locked" queue.
+
+ * The queue is "power unlocked" to start processing requests again.
+
+ This routine uses the function in the srb which just completed to determine
+ which state it is in.
+
+Arguments:
+
+ DeviceObject - the device object being powered up
+
+ Irp - the IO_REQUEST_PACKET containing the power request
+
+ Srb - the SRB used to perform port/class operations.
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED or
+ STATUS_SUCCESS
+
+--*/
+NTSTATUS
+ClasspPowerUpCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PCLASS_POWER_CONTEXT Context
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
+
+
+ NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
+
+ DebugPrint((1, "ClasspPowerUpCompletion: Device Object %p, Irp %p, "
+ "Context %p\n",
+ DeviceObject, Irp, Context));
+
+ ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+ ASSERT(Context->Options.PowerDown == FALSE);
+ ASSERT(Context->Options.HandleSpinUp);
+
+ if(Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ Context->PowerChangeState.PowerUp++;
+
+ switch(Context->PowerChangeState.PowerUp) {
+
+ case PowerUpDeviceLocked: {
+
+ DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp));
+
+ //
+ // Issue the actual power request to the lower driver.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ //
+ // If the lock wasn't successful then just bail out on the power
+ // request unless we can ignore failed locks
+ //
+
+ if((Context->Options.LockQueue == TRUE) &&
+ (!NT_SUCCESS(Irp->IoStatus.Status))) {
+
+ DebugPrint((1, "(%p)\tIrp status was %lx\n",
+ Irp, Irp->IoStatus.Status));
+ DebugPrint((1, "(%p)\tSrb status was %lx\n",
+ Irp, Context->Srb.SrbStatus));
+
+ //
+ // Lock was not successful - throw down the power IRP
+ // by itself and don't try to spin up the drive or unlock
+ // the queue.
+ //
+
+ Context->InUse = FALSE;
+ Context = NULL;
+
+ //
+ // Set the new power state
+ //
+
+ fdoExtension->DevicePowerState =
+ currentStack->Parameters.Power.State.DeviceState;
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ IoSetCompletionRoutine(Irp,
+ ClasspStartNextPowerIrpCompletion,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ //
+ // Indicate to Po that we've been successfully powered up so
+ // it can do it's notification stuff.
+ //
+
+ PoSetPowerState(DeviceObject,
+ currentStack->Parameters.Power.Type,
+ currentStack->Parameters.Power.State);
+
+ PoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ ClassReleaseRemoveLock(commonExtension->DeviceObject,
+ Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ } else {
+ Context->QueueLocked = (UCHAR) Context->Options.LockQueue;
+ }
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ Context->PowerChangeState.PowerUp = PowerUpDeviceLocked;
+
+ IoSetCompletionRoutine(Irp,
+ ClasspPowerUpCompletion,
+ Context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ DebugPrint((2, "(%p)\tPoCallDriver returned %lx\n", Irp, status));
+ break;
+ }
+
+ case PowerUpDeviceOn: {
+
+ PCDB cdb;
+
+ if(NT_SUCCESS(Irp->IoStatus.Status)) {
+
+ DebugPrint((1, "(%p)\tSending start unit to device\n", Irp));
+
+ //
+ // Issue the start unit command to the device.
+ //
+
+ Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
+ Context->Srb.DataTransferLength = 0;
+
+ Context->Srb.TimeOutValue = START_UNIT_TIMEOUT;
+
+ Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+ SRB_FLAGS_NO_QUEUE_FREEZE;
+
+ if(Context->Options.LockQueue) {
+ SET_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_BYPASS_LOCKED_QUEUE);
+ }
+
+ Context->Srb.CdbLength = 6;
+
+ cdb = (PCDB) (Context->Srb.Cdb);
+ RtlZeroMemory(cdb, sizeof(CDB));
+
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.Start = 1;
+
+ Context->PowerChangeState.PowerUp = PowerUpDeviceOn;
+
+ IoSetCompletionRoutine(Irp,
+ ClasspPowerUpCompletion,
+ Context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ nextStack->Parameters.Scsi.Srb = &(Context->Srb);
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ DebugPrint((2, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
+
+ } else {
+
+ //
+ // we're done.
+ //
+
+ Context->FinalStatus = Irp->IoStatus.Status;
+ goto ClasspPowerUpCompletionFailure;
+ }
+
+ break;
+ }
+
+ case PowerUpDeviceStarted: { // 3
+
+ //
+ // First deal with an error if one occurred.
+ //
+
+ if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ BOOLEAN retry;
+
+ DebugPrint((1, "%p\tError occured when issuing START_UNIT "
+ "command to device. Srb %p, Status %x\n",
+ Irp,
+ &Context->Srb,
+ Context->Srb.SrbStatus));
+
+ ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus,
+ SRB_STATUS_QUEUE_FROZEN)));
+ ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
+
+ Context->RetryInterval = 0;
+
+ retry = ClassInterpretSenseInfo(
+ commonExtension->DeviceObject,
+ &Context->Srb,
+ IRP_MJ_SCSI,
+ IRP_MJ_POWER,
+ MAXIMUM_RETRIES - Context->RetryCount,
+ &status,
+ &Context->RetryInterval);
+
+ if((retry == TRUE) && (Context->RetryCount-- != 0)) {
+
+ DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
+
+ //
+ // Decrement the state so we come back through here the
+ // next time.
+ //
+
+ Context->PowerChangeState.PowerUp--;
+
+ RetryPowerRequest(commonExtension->DeviceObject,
+ Irp,
+ Context);
+
+ break;
+
+ }
+
+ // reset retries
+ Context->RetryCount = MAXIMUM_RETRIES;
+
+ }
+
+ClasspPowerUpCompletionFailure:
+
+ DebugPrint((1, "(%p)\tPreviously spun device up\n", Irp));
+
+ if (Context->QueueLocked) {
+ DebugPrint((1, "(%p)\tUnlocking queue\n", Irp));
+
+ Context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
+ Context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE;
+ Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
+ Context->Srb.DataTransferLength = 0;
+
+ nextStack->Parameters.Scsi.Srb = &(Context->Srb);
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ Context->PowerChangeState.PowerUp = PowerUpDeviceStarted;
+
+ IoSetCompletionRoutine(Irp,
+ ClasspPowerUpCompletion,
+ Context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n",
+ Irp, status));
+ break;
+ }
+
+ // Fall-through to next case...
+
+ }
+
+ case PowerUpDeviceUnlocked: {
+
+ //
+ // This is the end of the dance. Free the srb and complete the
+ // request finally. We're ignoring possible intermediate
+ // error conditions ....
+ //
+
+ if (Context->QueueLocked) {
+ DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp));
+ ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
+ ASSERT(Context->Srb.SrbStatus == SRB_STATUS_SUCCESS);
+ } else {
+ DebugPrint((1, "(%p)\tFall-through (queue not locked)\n", Irp));
+ }
+
+ DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp));
+ Context->InUse = FALSE;
+
+ status = Context->FinalStatus;
+ Irp->IoStatus.Status = status;
+
+ Context = NULL;
+
+ //
+ // Set the new power state
+ //
+
+ if(NT_SUCCESS(status)) {
+ fdoExtension->DevicePowerState =
+ currentStack->Parameters.Power.State.DeviceState;
+ }
+
+ //
+ // Indicate to Po that we've been successfully powered up so
+ // it can do it's notification stuff.
+ //
+
+ PoSetPowerState(DeviceObject,
+ currentStack->Parameters.Power.Type,
+ currentStack->Parameters.Power.State);
+
+ DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ PoStartNextPowerIrp(Irp);
+
+ return status;
+ }
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClasspPowerUpCompletion()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspPowerDownCompletion()
+
+Routine Description:
+
+ This routine is used for intermediate completion of a power up request.
+ PowerUp requires four requests to be sent to the lower driver in sequence.
+
+ * The queue is "power locked" to ensure that the class driver power-up
+ work can be done before request processing resumes.
+
+ * The power irp is sent down the stack for any filter drivers and the
+ port driver to return power and resume command processing for the
+ device. Since the queue is locked, no queued irps will be sent
+ immediately.
+
+ * A start unit command is issued to the device with appropriate flags
+ to override the "power locked" queue.
+
+ * The queue is "power unlocked" to start processing requests again.
+
+ This routine uses the function in the srb which just completed to determine
+ which state it is in.
+
+Arguments:
+
+ DeviceObject - the device object being powered up
+
+ Irp - the IO_REQUEST_PACKET containing the power request
+
+ Srb - the SRB used to perform port/class operations.
+
+Return Value:
+
+ STATUS_MORE_PROCESSING_REQUIRED or
+ STATUS_SUCCESS
+
+--*/
+NTSTATUS
+ClasspPowerDownCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PCLASS_POWER_CONTEXT Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+
+ PIO_STACK_LOCATION currentStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextStack = IoGetNextIrpStackLocation(Irp);
+
+ NTSTATUS status = STATUS_MORE_PROCESSING_REQUIRED;
+
+ DebugPrint((1, "ClasspPowerDownCompletion: Device Object %p, "
+ "Irp %p, Context %p\n",
+ DeviceObject, Irp, Context));
+
+ ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+ ASSERT(Context->Options.PowerDown == TRUE);
+ ASSERT(Context->Options.HandleSpinDown);
+
+ if(Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ Context->PowerChangeState.PowerDown2++;
+
+ switch(Context->PowerChangeState.PowerDown2) {
+
+ case PowerDownDeviceLocked2: {
+
+ PCDB cdb;
+
+ DebugPrint((1, "(%p)\tPreviously sent power lock\n", Irp));
+
+ if((Context->Options.LockQueue == TRUE) &&
+ (!NT_SUCCESS(Irp->IoStatus.Status))) {
+
+ DebugPrint((1, "(%p)\tIrp status was %lx\n",
+ Irp,
+ Irp->IoStatus.Status));
+ DebugPrint((1, "(%p)\tSrb status was %lx\n",
+ Irp,
+ Context->Srb.SrbStatus));
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ //
+ // Lock was not successful - throw down the power IRP
+ // by itself and don't try to spin down the drive or unlock
+ // the queue.
+ //
+
+ Context->InUse = FALSE;
+ Context = NULL;
+
+ //
+ // Set the new power state
+ //
+
+ fdoExtension->DevicePowerState =
+ currentStack->Parameters.Power.State.DeviceState;
+
+ //
+ // Indicate to Po that we've been successfully powered down
+ // so it can do it's notification stuff.
+ //
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ IoSetCompletionRoutine(Irp,
+ ClasspStartNextPowerIrpCompletion,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ PoSetPowerState(DeviceObject,
+ currentStack->Parameters.Power.Type,
+ currentStack->Parameters.Power.State);
+
+ fdoExtension->PowerDownInProgress = FALSE;
+
+ PoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ ClassReleaseRemoveLock(commonExtension->DeviceObject,
+ Irp);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ } else {
+ Context->QueueLocked = (UCHAR) Context->Options.LockQueue;
+ }
+
+ if (!TEST_FLAG(fdoExtension->PrivateFdoData->HackFlags,
+ FDO_HACK_NO_SYNC_CACHE)) {
+
+ //
+ // send SCSIOP_SYNCHRONIZE_CACHE
+ //
+
+ Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ Context->Srb.TimeOutValue = fdoExtension->TimeOutValue;
+
+ Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+ SRB_FLAGS_NO_QUEUE_FREEZE |
+ SRB_FLAGS_BYPASS_LOCKED_QUEUE;
+
+ Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
+ Context->Srb.DataTransferLength = 0;
+
+ Context->Srb.CdbLength = 10;
+
+ cdb = (PCDB) Context->Srb.Cdb;
+
+ RtlZeroMemory(cdb, sizeof(CDB));
+ cdb->SYNCHRONIZE_CACHE10.OperationCode = SCSIOP_SYNCHRONIZE_CACHE;
+
+ IoSetCompletionRoutine(Irp,
+ ClasspPowerDownCompletion,
+ Context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ nextStack->Parameters.Scsi.Srb = &(Context->Srb);
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
+ break;
+
+ } else {
+
+ DebugPrint((1, "(%p)\tPower Down: not sending SYNCH_CACHE\n",
+ DeviceObject));
+ Context->PowerChangeState.PowerDown2++;
+ Context->Srb.SrbStatus = SRB_STATUS_SUCCESS;
+ // and fall through....
+ }
+ // no break in case the device doesn't like synch_cache commands
+
+ }
+
+ case PowerDownDeviceFlushed2: {
+
+ PCDB cdb;
+
+ DebugPrint((1, "(%p)\tPreviously send SCSIOP_SYNCHRONIZE_CACHE\n",
+ Irp));
+
+ //
+ // SCSIOP_SYNCHRONIZE_CACHE was sent
+ //
+
+ if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ BOOLEAN retry;
+
+ DebugPrint((1, "(%p)\tError occured when issuing "
+ "SYNCHRONIZE_CACHE command to device. "
+ "Srb %p, Status %lx\n",
+ Irp,
+ &Context->Srb,
+ Context->Srb.SrbStatus));
+
+ ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus,
+ SRB_STATUS_QUEUE_FROZEN)));
+ ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
+
+ Context->RetryInterval = 0;
+ retry = ClassInterpretSenseInfo(
+ commonExtension->DeviceObject,
+ &Context->Srb,
+ IRP_MJ_SCSI,
+ IRP_MJ_POWER,
+ MAXIMUM_RETRIES - Context->RetryCount,
+ &status,
+ &Context->RetryInterval);
+
+ if((retry == TRUE) && (Context->RetryCount-- != 0)) {
+
+ DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
+
+ //
+ // decrement the state so we come back through here
+ // the next time.
+ //
+
+ Context->PowerChangeState.PowerDown2--;
+ RetryPowerRequest(commonExtension->DeviceObject,
+ Irp,
+ Context);
+ break;
+ }
+
+ DebugPrint((1, "(%p)\tSYNCHRONIZE_CACHE not retried\n", Irp));
+ Context->RetryCount = MAXIMUM_RETRIES;
+
+ } // end !SRB_STATUS_SUCCESS
+
+ //
+ // note: we are purposefully ignoring any errors. if the drive
+ // doesn't support a synch_cache, then we're up a creek
+ // anyways.
+ //
+
+ DebugPrint((1, "(%p)\tSending stop unit to device\n", Irp));
+
+ //
+ // Issue the start unit command to the device.
+ //
+
+ Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ Context->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+
+ Context->Srb.TimeOutValue = START_UNIT_TIMEOUT;
+
+ Context->Srb.SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
+ SRB_FLAGS_DISABLE_AUTOSENSE |
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER |
+ SRB_FLAGS_NO_QUEUE_FREEZE |
+ SRB_FLAGS_BYPASS_LOCKED_QUEUE;
+
+ Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
+ Context->Srb.DataTransferLength = 0;
+
+ Context->Srb.CdbLength = 6;
+
+ cdb = (PCDB) Context->Srb.Cdb;
+ RtlZeroMemory(cdb, sizeof(CDB));
+
+ cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ cdb->START_STOP.Start = 0;
+ cdb->START_STOP.Immediate = 1;
+
+ IoSetCompletionRoutine(Irp,
+ ClasspPowerDownCompletion,
+ Context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ nextStack->Parameters.Scsi.Srb = &(Context->Srb);
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n", Irp, status));
+ break;
+
+ }
+
+ case PowerDownDeviceStopped2: {
+
+ BOOLEAN ignoreError = TRUE;
+
+ //
+ // stop was sent
+ //
+
+ if(SRB_STATUS(Context->Srb.SrbStatus) != SRB_STATUS_SUCCESS) {
+
+ BOOLEAN retry;
+
+ DebugPrint((1, "(%p)\tError occured when issueing STOP_UNIT "
+ "command to device. Srb %p, Status %lx\n",
+ Irp,
+ &Context->Srb,
+ Context->Srb.SrbStatus));
+
+ ASSERT(!(TEST_FLAG(Context->Srb.SrbStatus,
+ SRB_STATUS_QUEUE_FROZEN)));
+ ASSERT(Context->Srb.Function == SRB_FUNCTION_EXECUTE_SCSI);
+
+ Context->RetryInterval = 0;
+ retry = ClassInterpretSenseInfo(
+ commonExtension->DeviceObject,
+ &Context->Srb,
+ IRP_MJ_SCSI,
+ IRP_MJ_POWER,
+ MAXIMUM_RETRIES - Context->RetryCount,
+ &status,
+ &Context->RetryInterval);
+
+ if((retry == TRUE) && (Context->RetryCount-- != 0)) {
+
+ DebugPrint((1, "(%p)\tRetrying failed request\n", Irp));
+
+ //
+ // decrement the state so we come back through here
+ // the next time.
+ //
+
+ Context->PowerChangeState.PowerDown2--;
+ RetryPowerRequest(commonExtension->DeviceObject,
+ Irp,
+ Context);
+ break;
+ }
+
+ DebugPrint((1, "(%p)\tSTOP_UNIT not retried\n", Irp));
+ Context->RetryCount = MAXIMUM_RETRIES;
+
+ } // end !SRB_STATUS_SUCCESS
+
+
+ DebugPrint((1, "(%p)\tPreviously sent stop unit\n", Irp));
+
+ //
+ // some operations, such as a physical format in progress,
+ // should not be ignored and should fail the power operation.
+ //
+
+ if (!NT_SUCCESS(status)) {
+
+ PSENSE_DATA senseBuffer = Context->Srb.SenseInfoBuffer;
+
+ if (TEST_FLAG(Context->Srb.SrbStatus,
+ SRB_STATUS_AUTOSENSE_VALID) &&
+ ((senseBuffer->SenseKey & 0xf) == SCSI_SENSE_NOT_READY) &&
+ (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
+ (senseBuffer->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)
+ ) {
+ ignoreError = FALSE;
+ Context->FinalStatus = STATUS_DEVICE_BUSY;
+ status = Context->FinalStatus;
+ }
+
+ }
+
+ if (NT_SUCCESS(status) || ignoreError) {
+
+ //
+ // Issue the actual power request to the lower driver.
+ //
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+
+ IoSetCompletionRoutine(Irp,
+ ClasspPowerDownCompletion,
+ Context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = PoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ DebugPrint((1, "(%p)\tPoCallDriver returned %lx\n", Irp, status));
+ break;
+ }
+
+ // else fall through w/o sending the power irp, since the device
+ // is reporting an error that would be "really bad" to power down
+ // during.
+
+ }
+
+ case PowerDownDeviceOff2: {
+
+ //
+ // SpinDown request completed ... whether it succeeded or not is
+ // another matter entirely.
+ //
+
+ DebugPrint((1, "(%p)\tPreviously sent power irp\n", Irp));
+
+ if (Context->QueueLocked) {
+
+ DebugPrint((1, "(%p)\tUnlocking queue\n", Irp));
+
+ Context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+
+ Context->Srb.SrbStatus = Context->Srb.ScsiStatus = 0;
+ Context->Srb.DataTransferLength = 0;
+
+ Context->Srb.Function = SRB_FUNCTION_UNLOCK_QUEUE;
+ Context->Srb.SrbFlags = SRB_FLAGS_BYPASS_LOCKED_QUEUE;
+ nextStack->Parameters.Scsi.Srb = &(Context->Srb);
+ nextStack->MajorFunction = IRP_MJ_SCSI;
+
+ IoSetCompletionRoutine(Irp,
+ ClasspPowerDownCompletion,
+ Context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
+ DebugPrint((1, "(%p)\tIoCallDriver returned %lx\n",
+ Irp,
+ status));
+ break;
+ }
+
+ }
+
+ case PowerDownDeviceUnlocked2: {
+
+ //
+ // This is the end of the dance. Free the srb and complete the
+ // request finally. We're ignoring possible intermediate
+ // error conditions ....
+ //
+
+ if (Context->QueueLocked == FALSE) {
+ DebugPrint((1, "(%p)\tFall through (queue not locked)\n", Irp));
+ } else {
+ DebugPrint((1, "(%p)\tPreviously unlocked queue\n", Irp));
+ ASSERT(NT_SUCCESS(Irp->IoStatus.Status));
+ ASSERT(Context->Srb.SrbStatus == SRB_STATUS_SUCCESS);
+ }
+
+ DebugPrint((1, "(%p)\tFreeing srb and completing\n", Irp));
+ Context->InUse = FALSE;
+ status = Context->FinalStatus; // allow failure to propogate
+ Context = NULL;
+
+ if(Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ if (NT_SUCCESS(status)) {
+
+ //
+ // Set the new power state
+ //
+
+ fdoExtension->DevicePowerState =
+ currentStack->Parameters.Power.State.DeviceState;
+
+ }
+
+
+ DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ PoStartNextPowerIrp(Irp);
+ fdoExtension->PowerDownInProgress = FALSE;
+
+ return status;
+ }
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+} // end ClasspPowerDownCompletion()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspPowerHandler()
+
+Routine Description:
+
+ This routine reduces the number of useless spinups and spindown requests
+ sent to a given device by ignoring transitions to power states we are
+ currently in.
+
+ ISSUE-2000/02/20-henrygab - by ignoring spin-up requests, we may be
+ allowing the drive
+
+Arguments:
+
+ DeviceObject - the device object which is transitioning power states
+ Irp - the power irp
+ Options - a set of flags indicating what the device handles
+
+Return Value:
+
+--*/
+NTSTATUS
+ClasspPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN CLASS_POWER_OPTIONS Options // ISSUE-2000/02/20-henrygab - pass pointer, not whole struct
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ PIO_STACK_LOCATION nextIrpStack;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCLASS_POWER_CONTEXT context;
+
+ if (!commonExtension->IsFdo) {
+
+ //
+ // certain assumptions are made here,
+ // particularly: having the fdoExtension
+ //
+
+ DebugPrint((0, "ClasspPowerHandler: Called for PDO %p???\n",
+ DeviceObject));
+ ASSERT(!"PDO using ClasspPowerHandler");
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ DebugPrint((1, "ClasspPowerHandler: Power irp %p to %s %p\n",
+ Irp, (commonExtension->IsFdo ? "fdo" : "pdo"), DeviceObject));
+
+ switch(irpStack->MinorFunction) {
+
+ case IRP_MN_SET_POWER: {
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExtension->PrivateFdoData;
+
+ DebugPrint((1, "(%p)\tIRP_MN_SET_POWER\n", Irp));
+
+ DebugPrint((1, "(%p)\tSetting %s state to %d\n",
+ Irp,
+ (irpStack->Parameters.Power.Type == SystemPowerState ?
+ "System" : "Device"),
+ irpStack->Parameters.Power.State.SystemState));
+
+ switch (irpStack->Parameters.Power.ShutdownType){
+
+ case PowerActionSleep:
+ case PowerActionHibernate:
+ if (fdoData->HotplugInfo.MediaRemovable || fdoData->HotplugInfo.MediaHotplug){
+ /*
+ * We are suspending and this drive is either hot-pluggable
+ * or contains removeable media.
+ * Set the media dirty bit, since the media may change while
+ * we are suspended.
+ */
+ SET_FLAG(DeviceObject->Flags, DO_VERIFY_VOLUME);
+ }
+ break;
+ }
+
+ break;
+ }
+
+ default: {
+
+ DebugPrint((1, "(%p)\tIrp minor code = %#x\n",
+ Irp, irpStack->MinorFunction));
+ break;
+ }
+ }
+
+ if (irpStack->Parameters.Power.Type != DevicePowerState ||
+ irpStack->MinorFunction != IRP_MN_SET_POWER) {
+
+ DebugPrint((1, "(%p)\tSending to lower device\n", Irp));
+
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ nextIrpStack = IoGetNextIrpStackLocation(Irp);
+
+ //
+ // already in exact same state, don't work to transition to it.
+ //
+
+ if(irpStack->Parameters.Power.State.DeviceState ==
+ fdoExtension->DevicePowerState) {
+
+ DebugPrint((1, "(%p)\tAlready in device state %x\n",
+ Irp, fdoExtension->DevicePowerState));
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ //
+ // or powering down from non-d0 state (device already stopped)
+ // NOTE -- we're not sure whether this case can exist or not (the
+ // power system may never send this sort of request) but it's trivial
+ // to deal with.
+ //
+
+ if ((irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0) &&
+ (fdoExtension->DevicePowerState != PowerDeviceD0)) {
+ DebugPrint((1, "(%p)\tAlready powered down to %x???\n",
+ Irp, fdoExtension->DevicePowerState));
+ fdoExtension->DevicePowerState =
+ irpStack->Parameters.Power.State.DeviceState;
+ goto ClasspPowerHandlerCleanup;
+ }
+
+ //
+ // or going into a hibernation state when we're in the hibernation path.
+ // If the device is spinning then we should leave it spinning - if it's not
+ // then the dump driver will start it up for us.
+ //
+
+ if((irpStack->Parameters.Power.State.DeviceState == PowerDeviceD3) &&
+ (irpStack->Parameters.Power.ShutdownType == PowerActionHibernate) &&
+ (commonExtension->HibernationPathCount != 0)) {
+
+ DebugPrint((1, "(%p)\tdoing nothing for hibernation request for "
+ "state %x???\n",
+ Irp, fdoExtension->DevicePowerState));
+ fdoExtension->DevicePowerState =
+ irpStack->Parameters.Power.State.DeviceState;
+ goto ClasspPowerHandlerCleanup;
+ }
+ //
+ // or when not handling powering up and are powering up
+ //
+
+ if ((!Options.HandleSpinUp) &&
+ (irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0)) {
+
+ DebugPrint((2, "(%p)\tNot handling spinup to state %x\n",
+ Irp, fdoExtension->DevicePowerState));
+ fdoExtension->DevicePowerState =
+ irpStack->Parameters.Power.State.DeviceState;
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ //
+ // or when not handling powering down and are powering down
+ //
+
+ if ((!Options.HandleSpinDown) &&
+ (irpStack->Parameters.Power.State.DeviceState != PowerDeviceD0)) {
+
+ DebugPrint((2, "(%p)\tNot handling spindown to state %x\n",
+ Irp, fdoExtension->DevicePowerState));
+ fdoExtension->DevicePowerState =
+ irpStack->Parameters.Power.State.DeviceState;
+ goto ClasspPowerHandlerCleanup;
+
+ }
+
+ context = &(fdoExtension->PowerContext);
+
+#if DBG
+ //
+ // Mark the context as in use. We should be synchronizing this but
+ // since it's just for debugging purposes we don't worry too much.
+ //
+
+ ASSERT(context->InUse == FALSE);
+#endif
+
+ RtlZeroMemory(context, sizeof(CLASS_POWER_CONTEXT));
+ context->InUse = TRUE;
+
+ nextIrpStack->Parameters.Scsi.Srb = &(context->Srb);
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ context->FinalStatus = STATUS_SUCCESS;
+
+ context->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ context->Srb.OriginalRequest = Irp;
+ context->Srb.SrbFlags |= SRB_FLAGS_BYPASS_LOCKED_QUEUE
+ | SRB_FLAGS_NO_QUEUE_FREEZE;
+ context->Srb.Function = SRB_FUNCTION_LOCK_QUEUE;
+
+ context->Srb.SenseInfoBuffer =
+ commonExtension->PartitionZeroExtension->SenseData;
+ context->Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE;
+ context->RetryCount = MAXIMUM_RETRIES;
+
+ context->Options = Options;
+ context->DeviceObject = DeviceObject;
+ context->Irp = Irp;
+
+ if(irpStack->Parameters.Power.State.DeviceState == PowerDeviceD0) {
+
+ ASSERT(Options.HandleSpinUp);
+
+ DebugPrint((2, "(%p)\tpower up - locking queue\n", Irp));
+
+ //
+ // We need to issue a queue lock request so that we
+ // can spin the drive back up after the power is restored
+ // but before any requests are processed.
+ //
+
+ context->Options.PowerDown = FALSE;
+ context->PowerChangeState.PowerUp = PowerUpDeviceInitial;
+ context->CompletionRoutine = ClasspPowerUpCompletion;
+
+ } else {
+
+ ASSERT(Options.HandleSpinDown);
+
+ fdoExtension->PowerDownInProgress = TRUE;
+
+ DebugPrint((2, "(%p)\tPowering down - locking queue\n", Irp));
+
+ PoSetPowerState(DeviceObject,
+ irpStack->Parameters.Power.Type,
+ irpStack->Parameters.Power.State);
+
+ context->Options.PowerDown = TRUE;
+ context->PowerChangeState.PowerDown2 = PowerDownDeviceInitial2;
+ context->CompletionRoutine = ClasspPowerDownCompletion;
+
+ }
+
+ //
+ // we are not dealing with port-allocated sense in these routines.
+ //
+
+ ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ ASSERT(!TEST_FLAG(context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+
+ //
+ // we are always returning STATUS_PENDING, so we need to always
+ // set the irp as pending.
+ //
+
+ IoMarkIrpPending(Irp);
+
+ if(Options.LockQueue) {
+
+ //
+ // Send the lock irp down.
+ //
+
+ IoSetCompletionRoutine(Irp,
+ context->CompletionRoutine,
+ context,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ IoCallDriver(lowerDevice, Irp);
+
+ } else {
+
+ //
+ // Call the completion routine directly. It won't care what the
+ // status of the "lock" was - it will just go and do the next
+ // step of the operation.
+ //
+
+ context->CompletionRoutine(DeviceObject, Irp, context);
+ }
+
+ return STATUS_PENDING;
+
+ClasspPowerHandlerCleanup:
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+
+ DebugPrint((1, "(%p)\tStarting next power irp\n", Irp));
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ IoSetCompletionRoutine(Irp,
+ ClasspStartNextPowerIrpCompletion,
+ NULL,
+ TRUE,
+ TRUE,
+ TRUE);
+ return PoCallDriver(lowerDevice, Irp);
+} // end ClasspPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassMinimalPowerHandler()
+
+Routine Description:
+
+ This routine is the minimum power handler for a storage driver. It does
+ the least amount of work possible.
+
+--*/
+NTSTATUS
+ClassMinimalPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
+ NTSTATUS status;
+
+ ClassReleaseRemoveLock(DeviceObject, Irp);
+ PoStartNextPowerIrp(Irp);
+
+ if(commonExtension->IsFdo) {
+
+ if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
+
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
+ DeviceObject->DeviceExtension;
+
+ //
+ // Check if the system is going to hibernate or standby.
+ //
+ if (irpStack->MinorFunction == IRP_MN_SET_POWER){
+ PVPB vpb;
+
+ switch (irpStack->Parameters.Power.ShutdownType){
+
+ case PowerActionSleep:
+ case PowerActionHibernate:
+ //
+ // If the volume is mounted, set the verify bit so that
+ // the filesystem will be forced re-read the media
+ // after coming out of hibernation or standby.
+ //
+ vpb = ClassGetVpb(fdoExtension->DeviceObject);
+ if (vpb && (vpb->Flags & VPB_MOUNTED)){
+ SET_FLAG(fdoExtension->DeviceObject->Flags, DO_VERIFY_VOLUME);
+ }
+ break;
+ }
+ }
+ }
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ return PoCallDriver(commonExtension->LowerDeviceObject, Irp);
+
+ } else {
+
+ if (irpStack->MinorFunction != IRP_MN_SET_POWER &&
+ irpStack->MinorFunction != IRP_MN_QUERY_POWER) {
+
+ NOTHING;
+
+ } else {
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = 0;
+
+ }
+ status = Irp->IoStatus.Status;
+
+ ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
+ return status;
+ }
+} // end ClassMinimalPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassSpinDownPowerHandler()
+
+Routine Description:
+
+ This routine is a callback for disks and other things which require both
+ a start and a stop to be sent to the device. (actually the starts are
+ almost always optional, since most device power themselves on to process
+ commands, but i digress).
+
+ Determines proper use of spinup, spindown, and queue locking based upon
+ ScanForSpecialFlags in the FdoExtension. This is the most common power
+ handler passed into classpnp.sys
+
+Arguments:
+
+ DeviceObject - Supplies the functional device object
+
+ Irp - Supplies the request to be retried.
+
+Return Value:
+
+ None
+
+--*/
+NTSTATUS
+ClassSpinDownPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+ CLASS_POWER_OPTIONS options;
+
+ fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ //
+ // this will set all options to FALSE
+ //
+
+ RtlZeroMemory(&options, sizeof(CLASS_POWER_OPTIONS));
+
+ //
+ // check the flags to see what options we need to worry about
+ //
+
+ if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_SPIN_DOWN)) {
+ options.HandleSpinDown = TRUE;
+ }
+
+ if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_SPIN_UP)) {
+ options.HandleSpinUp = TRUE;
+ }
+
+ if (!TEST_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_NO_QUEUE_LOCK)) {
+ options.LockQueue = TRUE;
+ }
+
+ DebugPrint((3, "ClasspPowerHandler: Devobj %p\n"
+ "\t%shandling spin down\n"
+ "\t%shandling spin up\n"
+ "\t%slocking queue\n",
+ DeviceObject,
+ (options.HandleSpinDown ? "" : "not "),
+ (options.HandleSpinUp ? "" : "not "),
+ (options.LockQueue ? "" : "not ")
+ ));
+
+ //
+ // do all the dirty work
+ //
+
+ return ClasspPowerHandler(DeviceObject, Irp, options);
+} // end ClassSpinDownPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClassStopUnitPowerHandler()
+
+Routine Description:
+
+ This routine is an outdated call. To achieve equivalent functionality,
+ the driver should set the following flags in ScanForSpecialFlags in the
+ FdoExtension:
+
+ CLASS_SPECIAL_DISABLE_SPIN_UP
+ CLASS_SPECIAL_NO_QUEUE_LOCK
+
+--*/
+NTSTATUS
+ClassStopUnitPowerHandler(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
+
+ DebugPrint((0, "ClassStopUnitPowerHandler - Devobj %p using outdated call\n"
+ "Drivers should set the following flags in ScanForSpecialFlags "
+ " in the FDO extension:\n"
+ "\tCLASS_SPECIAL_DISABLE_SPIN_UP\n"
+ "\tCLASS_SPECIAL_NO_QUEUE_LOCK\n"
+ "This will provide equivalent functionality if the power "
+ "routine is then set to ClassSpinDownPowerHandler\n\n",
+ DeviceObject));
+
+ fdoExtension = (PFUNCTIONAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+ SET_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_DISABLE_SPIN_UP);
+ SET_FLAG(fdoExtension->ScanForSpecialFlags,
+ CLASS_SPECIAL_NO_QUEUE_LOCK);
+
+ return ClassSpinDownPowerHandler(DeviceObject, Irp);
+} // end ClassStopUnitPowerHandler()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+RetryPowerRequest()
+
+Routine Description:
+
+ This routine reinitalizes the necessary fields, and sends the request
+ to the lower driver.
+
+Arguments:
+
+ DeviceObject - Supplies the device object associated with this request.
+
+ Irp - Supplies the request to be retried.
+
+ Context - Supplies a pointer to the power up context for this request.
+
+Return Value:
+
+ None
+
+--*/
+VOID
+RetryPowerRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PCLASS_POWER_CONTEXT Context
+ )
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
+ PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
+ PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
+ PSCSI_REQUEST_BLOCK srb = &(Context->Srb);
+ LARGE_INTEGER dueTime;
+
+ DebugPrint((1, "(%p)\tDelaying retry by queueing DPC\n", Irp));
+
+ ASSERT(Context->Irp == Irp);
+ ASSERT(Context->DeviceObject == DeviceObject);
+ ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_FREE_SENSE_BUFFER));
+ ASSERT(!TEST_FLAG(Context->Srb.SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE));
+
+ //
+ // reset the retry interval
+ //
+
+ Context->RetryInterval = 0;
+
+ //
+ // Reset byte count of transfer in SRB Extension.
+ //
+
+ srb->DataTransferLength = 0;
+
+ //
+ // Zero SRB statuses.
+ //
+
+ srb->SrbStatus = srb->ScsiStatus = 0;
+
+ //
+ // Set up major SCSI function.
+ //
+
+ nextIrpStack->MajorFunction = IRP_MJ_SCSI;
+
+ //
+ // Save SRB address in next stack for port driver.
+ //
+
+ nextIrpStack->Parameters.Scsi.Srb = srb;
+
+ //
+ // Set the completion routine up again.
+ //
+
+ IoSetCompletionRoutine(Irp, Context->CompletionRoutine, Context,
+ TRUE, TRUE, TRUE);
+
+
+ if (Context->RetryInterval == 0) {
+
+ DebugPrint((2, "(%p)\tDelaying minimum time (.2 sec)\n", Irp));
+ dueTime.QuadPart = (LONGLONG)1000000 * 2;
+
+ } else {
+
+ DebugPrint((2, "(%p)\tDelaying %x seconds\n",
+ Irp, Context->RetryInterval));
+ dueTime.QuadPart = (LONGLONG)1000000 * 10 * Context->RetryInterval;
+
+ }
+
+ ClassRetryRequest(DeviceObject, Irp, dueTime);
+
+ return;
+
+} // end RetryRequest()
+
+/*++////////////////////////////////////////////////////////////////////////////
+
+ClasspStartNextPowerIrpCompletion()
+
+Routine Description:
+
+ This routine guarantees that the next power irp (power up or down) is not
+ sent until the previous one has fully completed.
+
+--*/
+NTSTATUS
+ClasspStartNextPowerIrpCompletion(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ if(Irp->PendingReturned) {
+ IoMarkIrpPending(Irp);
+ }
+
+ PoStartNextPowerIrp(Irp);
+ return STATUS_SUCCESS;
+} // end ClasspStartNextPowerIrpCompletion()
+
diff --git a/reactos/drivers/storage/classpnp/retry.c b/reactos/drivers/storage/classpnp/retry.c
new file mode 100644
index 00000000000..3a88e647582
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/retry.c
@@ -0,0 +1,349 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ retry.c
+
+Abstract:
+
+ Packet retry routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+
+
+/*
+ * InterpretTransferPacketError
+ *
+ * Interpret the SRB error into a meaningful IRP status.
+ * ClassInterpretSenseInfo also may modify the SRB for the retry.
+ *
+ * Return TRUE iff packet should be retried.
+ */
+BOOLEAN InterpretTransferPacketError(PTRANSFER_PACKET Pkt)
+{
+ BOOLEAN shouldRetry = FALSE;
+ PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
+
+ /*
+ * Interpret the error using the returned sense info first.
+ */
+ Pkt->RetryIntervalSec = 0;
+ if (pCdb->MEDIA_REMOVAL.OperationCode == SCSIOP_MEDIUM_REMOVAL){
+ /*
+ * This is an Ejection Control SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ &Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ MAXIMUM_RETRIES - Pkt->NumRetries,
+ &Pkt->Irp->IoStatus.Status,
+ &Pkt->RetryIntervalSec);
+ if (shouldRetry){
+ /*
+ * If the device is not ready, wait at least 2 seconds before retrying.
+ */
+ PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer;
+ ASSERT(senseInfoBuffer);
+ if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
+ (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) ||
+ (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){
+
+ Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
+ }
+ }
+ }
+ else if ((pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) ||
+ (pCdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10)){
+ /*
+ * This is an Mode Sense SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ &Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ MAXIMUM_RETRIES - Pkt->NumRetries,
+ &Pkt->Irp->IoStatus.Status,
+ &Pkt->RetryIntervalSec);
+ if (shouldRetry){
+ /*
+ * If the device is not ready, wait at least 2 seconds before retrying.
+ */
+ PSENSE_DATA senseInfoBuffer = Pkt->Srb.SenseInfoBuffer;
+ ASSERT(senseInfoBuffer);
+ if (((Pkt->Irp->IoStatus.Status == STATUS_DEVICE_NOT_READY) &&
+ (senseInfoBuffer->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY)) ||
+ (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT)){
+
+ Pkt->RetryIntervalSec = MAX(Pkt->RetryIntervalSec, 2);
+ }
+ }
+
+ /*
+ * Some special cases for mode sense.
+ */
+ if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
+ shouldRetry = TRUE;
+ }
+ else if (SRB_STATUS(Pkt->Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN){
+ /*
+ * This is a HACK.
+ * Atapi returns SRB_STATUS_DATA_OVERRUN when it really means
+ * underrun (i.e. success, and the buffer is longer than needed).
+ * So treat this as a success.
+ */
+ Pkt->Irp->IoStatus.Status = STATUS_SUCCESS;
+ InterlockedExchangeAdd((PLONG)&Pkt->OriginalIrp->IoStatus.Information, (LONG)Pkt->Srb.DataTransferLength);
+ shouldRetry = FALSE;
+ }
+ }
+ else if (pCdb->CDB10.OperationCode == SCSIOP_READ_CAPACITY){
+ /*
+ * This is a Drive Capacity SRB. Interpret its sense info specially.
+ */
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ &Pkt->Srb,
+ IRP_MJ_SCSI,
+ 0,
+ MAXIMUM_RETRIES - Pkt->NumRetries,
+ &Pkt->Irp->IoStatus.Status,
+ &Pkt->RetryIntervalSec);
+ if (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED){
+ shouldRetry = TRUE;
+ }
+ }
+ else if ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
+ (pCdb->CDB10.OperationCode == SCSIOP_WRITE)){
+ /*
+ * This is a Read/Write Data packet.
+ */
+ PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
+
+ shouldRetry = ClassInterpretSenseInfo(
+ Pkt->Fdo,
+ &Pkt->Srb,
+ origCurrentSp->MajorFunction,
+ 0,
+ MAXIMUM_RETRIES - Pkt->NumRetries,
+ &Pkt->Irp->IoStatus.Status,
+ &Pkt->RetryIntervalSec);
+ /*
+ * Deal with some special cases.
+ */
+ if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
+ /*
+ * We are in extreme low-memory stress.
+ * We will retry in smaller chunks.
+ */
+ shouldRetry = TRUE;
+ }
+ else if (TEST_FLAG(origCurrentSp->Flags, SL_OVERRIDE_VERIFY_VOLUME) &&
+ (Pkt->Irp->IoStatus.Status == STATUS_VERIFY_REQUIRED)){
+ /*
+ * We are still verifying a (possibly) reloaded disk/cdrom.
+ * So retry the request.
+ */
+ Pkt->Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
+ shouldRetry = TRUE;
+ }
+ }
+ else {
+ DBGERR(("Unhandled SRB Function %xh in error path for packet %p (did miniport change Srb.Cdb.OperationCode ?)", (ULONG)pCdb->CDB10.OperationCode, Pkt));
+ }
+
+ return shouldRetry;
+}
+
+
+/*
+ * RetryTransferPacket
+ *
+ * Retry sending a TRANSFER_PACKET.
+ *
+ * Return TRUE iff the packet is complete.
+ * (if so the status in pkt->irp is the final status).
+ */
+BOOLEAN RetryTransferPacket(PTRANSFER_PACKET Pkt)
+{
+ BOOLEAN packetDone;
+
+ DBGTRACE(ClassDebugTrace, ("retrying failed transfer (pkt=%ph, op=%s)", Pkt, DBGGETSCSIOPSTR(&Pkt->Srb)));
+
+ ASSERT(Pkt->NumRetries > 0);
+ Pkt->NumRetries--;
+
+ /*
+ * Tone down performance on the retry.
+ * This increases the chance for success on the retry.
+ * We've seen instances of drives that fail consistently but then start working
+ * once this scale-down is applied.
+ */
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ CLEAR_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ Pkt->Srb.QueueTag = SP_UNTAGGED;
+
+ if (Pkt->Irp->IoStatus.Status == STATUS_INSUFFICIENT_RESOURCES){
+ PCDB pCdb = (PCDB)Pkt->Srb.Cdb;
+ BOOLEAN isReadWrite = ((pCdb->CDB10.OperationCode == SCSIOP_READ) ||
+ (pCdb->CDB10.OperationCode == SCSIOP_WRITE));
+
+ if (Pkt->InLowMemRetry || !isReadWrite){
+ /*
+ * This should never happen.
+ * The memory manager guarantees that at least four pages will
+ * be available to allow forward progress in the port driver.
+ * So a one-page transfer should never fail with insufficient resources.
+ */
+ ASSERT(isReadWrite && !Pkt->InLowMemRetry);
+ packetDone = TRUE;
+ }
+ else {
+ /*
+ * We are in low-memory stress.
+ * Start the low-memory retry state machine, which tries to
+ * resend the packet in little one-page chunks.
+ */
+ InitLowMemRetry( Pkt,
+ Pkt->BufPtrCopy,
+ Pkt->BufLenCopy,
+ Pkt->TargetLocationCopy);
+ StepLowMemRetry(Pkt);
+ packetDone = FALSE;
+ }
+ }
+ else {
+ /*
+ * Retry the packet by simply resending it after a delay.
+ * Put the packet back in the pending queue and
+ * schedule a timer to retry the transfer.
+ *
+ * Do not call SetupReadWriteTransferPacket again because:
+ * (1) The minidriver may have set some bits
+ * in the SRB that it needs again and
+ * (2) doing so would reset numRetries.
+ *
+ * BECAUSE we do not call SetupReadWriteTransferPacket again,
+ * we have to reset a couple fields in the SRB that
+ * some miniports overwrite when they fail an SRB.
+ */
+
+ Pkt->Srb.DataBuffer = Pkt->BufPtrCopy;
+ Pkt->Srb.DataTransferLength = Pkt->BufLenCopy;
+
+ if (Pkt->RetryIntervalSec == 0){
+ /*
+ * Always delay by at least a little when retrying.
+ * Some problems (e.g. CRC errors) are not recoverable without a slight delay.
+ */
+ LARGE_INTEGER timerPeriod;
+
+ timerPeriod.HighPart = -1;
+ timerPeriod.LowPart = -(LONG)((ULONG)MINIMUM_RETRY_UNITS*KeQueryTimeIncrement());
+ KeInitializeTimer(&Pkt->RetryTimer);
+ KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
+ KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
+ }
+ else {
+ LARGE_INTEGER timerPeriod;
+
+ ASSERT(Pkt->RetryIntervalSec < 100); // sanity check
+ timerPeriod.HighPart = -1;
+ timerPeriod.LowPart = Pkt->RetryIntervalSec*-10000000;
+ KeInitializeTimer(&Pkt->RetryTimer);
+ KeInitializeDpc(&Pkt->RetryTimerDPC, TransferPacketRetryTimerDpc, Pkt);
+ KeSetTimer(&Pkt->RetryTimer, timerPeriod, &Pkt->RetryTimerDPC);
+ }
+ packetDone = FALSE;
+ }
+
+ return packetDone;
+}
+
+
+VOID TransferPacketRetryTimerDpc( IN PKDPC Dpc,
+ IN PVOID DeferredContext,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2)
+{
+ PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)DeferredContext;
+ SubmitTransferPacket(pkt);
+}
+
+
+VOID InitLowMemRetry(PTRANSFER_PACKET Pkt, PVOID BufPtr, ULONG Len, LARGE_INTEGER TargetLocation)
+{
+ ASSERT(Len > 0);
+ ASSERT(!Pkt->InLowMemRetry);
+ Pkt->InLowMemRetry = TRUE;
+ Pkt->LowMemRetry_remainingBufPtr = BufPtr;
+ Pkt->LowMemRetry_remainingBufLen = Len;
+ Pkt->LowMemRetry_nextChunkTargetLocation = TargetLocation;
+}
+
+
+/*
+ * StepLowMemRetry
+ *
+ * During extreme low-memory stress, this function retries
+ * a packet in small one-page chunks, sent serially.
+ *
+ * Returns TRUE iff the packet is done.
+ */
+BOOLEAN StepLowMemRetry(PTRANSFER_PACKET Pkt)
+{
+ BOOLEAN packetDone;
+
+ if (Pkt->LowMemRetry_remainingBufLen == 0){
+ packetDone = TRUE;
+ }
+ else {
+ ULONG thisChunkLen;
+ ULONG bytesToNextPageBoundary;
+
+ /*
+ * Make sure the little chunk we send is <= a page length
+ * AND that it does not cross any page boundaries.
+ */
+ bytesToNextPageBoundary = PAGE_SIZE-(ULONG)((ULONG_PTR)Pkt->LowMemRetry_remainingBufPtr%PAGE_SIZE);
+ thisChunkLen = MIN(Pkt->LowMemRetry_remainingBufLen, bytesToNextPageBoundary);
+
+ /*
+ * Set up the transfer packet for the new little chunk.
+ * This will reset numRetries so that we retry each chunk as required.
+ */
+ SetupReadWriteTransferPacket(Pkt,
+ Pkt->LowMemRetry_remainingBufPtr,
+ thisChunkLen,
+ Pkt->LowMemRetry_nextChunkTargetLocation,
+ Pkt->OriginalIrp);
+
+ Pkt->LowMemRetry_remainingBufPtr += thisChunkLen;
+ Pkt->LowMemRetry_remainingBufLen -= thisChunkLen;
+ Pkt->LowMemRetry_nextChunkTargetLocation.QuadPart += thisChunkLen;
+
+ SubmitTransferPacket(Pkt);
+ packetDone = FALSE;
+ }
+
+ return packetDone;
+}
+
diff --git a/reactos/drivers/storage/classpnp/sources b/reactos/drivers/storage/classpnp/sources
new file mode 100644
index 00000000000..2493527c7a5
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/sources
@@ -0,0 +1,85 @@
+!IF 0
+
+Copyright (C) Microsoft Corporation, 1996 - 1999
+
+Module Name:
+
+ sources.
+
+!ENDIF
+
+TARGETNAME=classpnp
+TARGETPATH=obj
+TARGETTYPE=EXPORT_DRIVER
+
+!IFNDEF MSC_WARNING_LEVEL
+MSC_WARNING_LEVEL=/W3
+!ENDIF
+
+MSC_WARNING_LEVEL=$(MSC_WARNING_LEVEL) /WX
+
+PASS1_PUBLISH={$(O)\classpnp.lib=$(DDK_LIB_PATH)\classpnp.lib}
+
+INCLUDES=..\inc;..\..\inc
+
+#
+# Specify whether to break into the debugger if synchronous irps
+# sent via ClassSendSrbSynchronous() do not complete within the
+# given timeout period
+#
+C_DEFINES=$(C_DEFINES) -DCLASS_GLOBAL_BREAK_ON_LOST_IRPS=0
+
+#
+# Specifies the number of seconds for breaking for above
+# Implementation details are within the source.
+#
+C_DEFINES=$(C_DEFINES) -DCLASS_GLOBAL_SECONDS_TO_WAIT_FOR_SYNCHRONOUS_SRB=100
+
+#
+# Specifies that retried requests shall be delayed as
+# specified by ClassInterpretSenseInfo().
+# Implementation details are within the source.
+#
+C_DEFINES=$(C_DEFINES) -DCLASS_GLOBAL_USE_DELAYED_RETRY=1
+
+#
+# You can enable buffering of debug prints to a global buffer by setting
+# this to 1. This allows debug prints while tracking timing issues.
+# Implementation details are within the source.
+#
+C_DEFINES=$(C_DEFINES) -DCLASS_GLOBAL_BUFFERED_DEBUG_PRINT=0
+
+#
+# This is the maximum size for each debug print string sent to DebugPrint(()),
+# including the trailing NUL character.
+# Implementation details are within the source.
+#
+C_DEFINES=$(C_DEFINES) -DCLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFER_SIZE=512
+
+#
+# This is the number of unique buffers allocated for buffered printing.
+# Implementation details are within the source.
+#
+C_DEFINES=$(C_DEFINES) -DCLASS_GLOBAL_BUFFERED_DEBUG_PRINT_BUFFERS=512
+
+
+SOURCES=autorun.c \
+ class.c \
+ classwmi.c \
+ create.c \
+ data.c \
+ dictlib.c \
+ lock.c \
+ power.c \
+ xferpkt.c \
+ clntirp.c \
+ retry.c \
+ utils.c \
+ obsolete.c \
+ debug.c \
+ class.rc
+
+DLLDEF=$(O)\class.def
+
+
+
diff --git a/reactos/drivers/storage/classpnp/utils.c b/reactos/drivers/storage/classpnp/utils.c
new file mode 100644
index 00000000000..43dd04916d1
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/utils.c
@@ -0,0 +1,565 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ utils.c
+
+Abstract:
+
+ SCSI class driver routines
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(PAGE, ClassGetDeviceParameter)
+ #pragma alloc_text(PAGE, ClassScanForSpecial)
+ #pragma alloc_text(PAGE, ClassSetDeviceParameter)
+#endif
+
+
+
+// custom string match -- careful!
+BOOLEAN ClasspMyStringMatches(IN PCHAR StringToMatch OPTIONAL, IN PCHAR TargetString)
+{
+ ULONG length; // strlen returns an int, not size_t (!)
+ PAGED_CODE();
+ ASSERT(TargetString);
+ // if no match requested, return TRUE
+ if (StringToMatch == NULL) {
+ return TRUE;
+ }
+ // cache the string length for efficiency
+ length = strlen(StringToMatch);
+ // ZERO-length strings may only match zero-length strings
+ if (length == 0) {
+ return (strlen(TargetString) == 0);
+ }
+ // strncmp returns zero if the strings match
+ return (strncmp(StringToMatch, TargetString, length) == 0);
+}
+
+
+VOID ClassGetDeviceParameter(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PWSTR SubkeyName OPTIONAL,
+ IN PWSTR ParameterName,
+ IN OUT PULONG ParameterValue // also default value
+ )
+{
+ NTSTATUS status;
+ RTL_QUERY_REGISTRY_TABLE queryTable[2];
+ HANDLE deviceParameterHandle;
+ HANDLE deviceSubkeyHandle;
+ ULONG defaultParameterValue;
+
+ PAGED_CODE();
+
+ //
+ // open the given parameter
+ //
+
+ status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_READ,
+ &deviceParameterHandle);
+
+ if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
+
+ UNICODE_STRING subkeyName;
+ OBJECT_ATTRIBUTES objectAttributes;
+
+ RtlInitUnicodeString(&subkeyName, SubkeyName);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ NULL);
+
+ status = ZwOpenKey(&deviceSubkeyHandle,
+ KEY_READ,
+ &objectAttributes);
+ if (!NT_SUCCESS(status)) {
+ ZwClose(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_DWORD;
+ queryTable->DefaultData = NULL;
+ queryTable->DefaultLength = 0;
+
+ status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
+ (PWSTR)(SubkeyName ?
+ deviceSubkeyHandle :
+ deviceParameterHandle),
+ queryTable,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(status)) {
+ *ParameterValue = defaultParameterValue; // use default value
+ }
+
+ //
+ // close what we open
+ //
+
+ if (SubkeyName) {
+ ZwClose(deviceSubkeyHandle);
+ }
+
+ ZwClose(deviceParameterHandle);
+ }
+
+ return;
+
+} // end ClassGetDeviceParameter()
+
+
+NTSTATUS ClassSetDeviceParameter(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN PWSTR SubkeyName OPTIONAL,
+ IN PWSTR ParameterName,
+ IN ULONG ParameterValue)
+{
+ NTSTATUS status;
+ HANDLE deviceParameterHandle;
+ HANDLE deviceSubkeyHandle;
+
+ PAGED_CODE();
+
+ //
+ // open the given parameter
+ //
+
+ status = IoOpenDeviceRegistryKey(FdoExtension->LowerPdo,
+ PLUGPLAY_REGKEY_DEVICE,
+ KEY_READ | KEY_WRITE,
+ &deviceParameterHandle);
+
+ if (NT_SUCCESS(status) && (SubkeyName != NULL)) {
+
+ UNICODE_STRING subkeyName;
+ OBJECT_ATTRIBUTES objectAttributes;
+
+ RtlInitUnicodeString(&subkeyName, SubkeyName);
+ InitializeObjectAttributes(&objectAttributes,
+ &subkeyName,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ deviceParameterHandle,
+ NULL);
+
+ status = ZwCreateKey(&deviceSubkeyHandle,
+ KEY_READ | KEY_WRITE,
+ &objectAttributes,
+ 0, NULL, 0, NULL);
+ if (!NT_SUCCESS(status)) {
+ ZwClose(deviceParameterHandle);
+ }
+
+ }
+
+ if (NT_SUCCESS(status)) {
+
+ status = RtlWriteRegistryValue(
+ RTL_REGISTRY_HANDLE,
+ (PWSTR) (SubkeyName ?
+ deviceSubkeyHandle :
+ deviceParameterHandle),
+ ParameterName,
+ REG_DWORD,
+ &ParameterValue,
+ sizeof(ULONG));
+
+ //
+ // close what we open
+ //
+
+ if (SubkeyName) {
+ ZwClose(deviceSubkeyHandle);
+ }
+
+ ZwClose(deviceParameterHandle);
+ }
+
+ return status;
+
+} // end ClassSetDeviceParameter()
+
+
+/*
+ * ClassScanForSpecial
+ *
+ * This routine was written to simplify scanning for special
+ * hardware based upon id strings. it does not check the registry.
+ */
+
+VOID ClassScanForSpecial(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension,
+ IN CLASSPNP_SCAN_FOR_SPECIAL_INFO DeviceList[],
+ IN PCLASS_SCAN_FOR_SPECIAL_HANDLER Function)
+{
+ PSTORAGE_DEVICE_DESCRIPTOR deviceDescriptor;
+ PUCHAR vendorId;
+ PUCHAR productId;
+ PUCHAR productRevision;
+ UCHAR nullString[] = "";
+ ULONG j;
+
+ PAGED_CODE();
+ ASSERT(DeviceList);
+ ASSERT(Function);
+
+ deviceDescriptor = FdoExtension->DeviceDescriptor;
+
+ if (DeviceList == NULL) {
+ return;
+ }
+ if (Function == NULL) {
+ return;
+ }
+
+ //
+ // SCSI sets offsets to -1, ATAPI sets to 0. check for both.
+ //
+
+ if (deviceDescriptor->VendorIdOffset != 0 &&
+ deviceDescriptor->VendorIdOffset != -1) {
+ vendorId = ((PUCHAR)deviceDescriptor);
+ vendorId += deviceDescriptor->VendorIdOffset;
+ } else {
+ vendorId = nullString;
+ }
+ if (deviceDescriptor->ProductIdOffset != 0 &&
+ deviceDescriptor->ProductIdOffset != -1) {
+ productId = ((PUCHAR)deviceDescriptor);
+ productId += deviceDescriptor->ProductIdOffset;
+ } else {
+ productId = nullString;
+ }
+ if (deviceDescriptor->VendorIdOffset != 0 &&
+ deviceDescriptor->VendorIdOffset != -1) {
+ productRevision = ((PUCHAR)deviceDescriptor);
+ productRevision += deviceDescriptor->ProductRevisionOffset;
+ } else {
+ productRevision = nullString;
+ }
+
+ //
+ // loop while the device list is valid (not null-filled)
+ //
+
+ for (;(DeviceList->VendorId != NULL ||
+ DeviceList->ProductId != NULL ||
+ DeviceList->ProductRevision != NULL);DeviceList++) {
+
+ if (ClasspMyStringMatches(DeviceList->VendorId, vendorId) &&
+ ClasspMyStringMatches(DeviceList->ProductId, productId) &&
+ ClasspMyStringMatches(DeviceList->ProductRevision, productRevision)
+ ) {
+
+ DebugPrint((1, "ClasspScanForSpecialByInquiry: Found matching "
+ "controller Ven: %s Prod: %s Rev: %s\n",
+ vendorId, productId, productRevision));
+
+ //
+ // pass the context to the call back routine and exit
+ //
+
+ (Function)(FdoExtension, DeviceList->Data);
+
+ //
+ // for CHK builds, try to prevent wierd stacks by having a debug
+ // print here. it's a hack, but i know of no other way to prevent
+ // the stack from being wrong.
+ //
+
+ DebugPrint((16, "ClasspScanForSpecialByInquiry: "
+ "completed callback\n"));
+ return;
+
+ } // else the strings did not match
+
+ } // none of the devices matched.
+
+ DebugPrint((1, "ClasspScanForSpecialByInquiry: no match found for %p\n",
+ FdoExtension->DeviceObject));
+ return;
+
+} // end ClasspScanForSpecialByInquiry()
+
+
+//
+// In order to provide better performance without the need to reboot,
+// we need to implement a self-adjusting method to set and clear the
+// srb flags based upon current performance.
+//
+// whenever there is an error, immediately grab the spin lock. the
+// MP perf hit here is acceptable, since we're in an error path. this
+// is also neccessary because we are guaranteed to be modifying the
+// SRB flags here, setting SuccessfulIO to zero, and incrementing the
+// actual error count (which is always done within this spinlock).
+//
+// whenever there is no error, increment a counter. if there have been
+// errors on the device, and we've enabled dynamic perf, *and* we've
+// just crossed the perf threshhold, then grab the spin lock and
+// double check that the threshhold has, indeed been hit(*). then
+// decrement the error count, and if it's dropped sufficiently, undo
+// some of the safety changes made in the SRB flags due to the errors.
+//
+// * this works in all cases. even if lots of ios occur after the
+// previous guy went in and cleared the successfulio counter, that
+// just means that we've hit the threshhold again, and so it's proper
+// to run the inner loop again.
+//
+
+VOID
+ClasspPerfIncrementErrorCount(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+ ULONG errors;
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ fdoData->Perf.SuccessfulIO = 0; // implicit interlock
+ errors = InterlockedIncrement(&FdoExtension->ErrorCount);
+
+ if (errors >= CLASS_ERROR_LEVEL_1) {
+
+ //
+ // If the error count has exceeded the error limit, then disable
+ // any tagged queuing, multiple requests per lu queueing
+ // and sychronous data transfers.
+ //
+ // Clearing the no queue freeze flag prevents the port driver
+ // from sending multiple requests per logical unit.
+ //
+
+ CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+ CLEAR_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
+
+ SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: "
+ "Too many errors; disabling tagged queuing and "
+ "synchronous data tranfers.\n"));
+
+ }
+
+ if (errors >= CLASS_ERROR_LEVEL_2) {
+
+ //
+ // If a second threshold is reached, disable disconnects.
+ //
+
+ SET_FLAG(FdoExtension->SrbFlags, SRB_FLAGS_DISABLE_DISCONNECT);
+ DebugPrint((ClassDebugError, "ClasspPerfIncrementErrorCount: "
+ "Too many errors; disabling disconnects.\n"));
+ }
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ return;
+}
+
+VOID
+ClasspPerfIncrementSuccessfulIo(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+{
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+ ULONG errors;
+ ULONG succeeded = 0;
+
+ //
+ // don't take a hit from the interlocked op unless we're in
+ // a degraded state and we've got a threshold to hit.
+ //
+
+ if (FdoExtension->ErrorCount == 0) {
+ return;
+ }
+
+ if (fdoData->Perf.ReEnableThreshhold == 0) {
+ return;
+ }
+
+ succeeded = InterlockedIncrement(&fdoData->Perf.SuccessfulIO);
+ if (succeeded < fdoData->Perf.ReEnableThreshhold) {
+ return;
+ }
+
+ //
+ // if we hit the threshold, grab the spinlock and verify we've
+ // actually done so. this allows us to ignore the spinlock 99%
+ // of the time.
+ //
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ //
+ // re-read the value, so we don't run this multiple times
+ // for a single threshhold being hit. this keeps errorcount
+ // somewhat useful.
+ //
+
+ succeeded = fdoData->Perf.SuccessfulIO;
+
+ if ((FdoExtension->ErrorCount != 0) &&
+ (fdoData->Perf.ReEnableThreshhold <= succeeded)
+ ) {
+
+ fdoData->Perf.SuccessfulIO = 0; // implicit interlock
+
+ ASSERT(FdoExtension->ErrorCount > 0);
+ errors = InterlockedDecrement(&FdoExtension->ErrorCount);
+
+ //
+ // note: do in reverse order of the sets "just in case"
+ //
+
+ if (errors < CLASS_ERROR_LEVEL_2) {
+ if (errors == CLASS_ERROR_LEVEL_2 - 1) {
+ DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: "
+ "Error level 2 no longer required.\n"));
+ }
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT);
+ }
+ }
+
+ if (errors < CLASS_ERROR_LEVEL_1) {
+ if (errors == CLASS_ERROR_LEVEL_1 - 1) {
+ DebugPrint((ClassDebugError, "ClasspPerfIncrementSuccessfulIo: "
+ "Error level 1 no longer required.\n"));
+ }
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE);
+ }
+ }
+ } // end of threshhold definitely being hit for first time
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ return;
+}
+
+
+PMDL BuildDeviceInputMdl(PVOID Buffer, ULONG BufferLen)
+{
+ PMDL mdl;
+
+ mdl = IoAllocateMdl(Buffer, BufferLen, FALSE, FALSE, NULL);
+ if (mdl){
+ _SEH2_TRY {
+ /*
+ * We are reading from the device.
+ * Therefore, the device is WRITING to the locked memory.
+ * So we request IoWriteAccess.
+ */
+ MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
+
+ } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ NTSTATUS status = _SEH2_GetExceptionCode();
+
+ DBGWARN(("BuildReadMdl: MmProbeAndLockPages failed with %xh.", status));
+ IoFreeMdl(mdl);
+ mdl = NULL;
+ } _SEH2_END;
+ }
+ else {
+ DBGWARN(("BuildReadMdl: IoAllocateMdl failed"));
+ }
+
+ return mdl;
+}
+
+
+VOID FreeDeviceInputMdl(PMDL Mdl)
+{
+ MmUnlockPages(Mdl);
+ IoFreeMdl(Mdl);
+}
+
+
+#if 0
+ VOID
+ ClasspPerfResetCounters(
+ IN PFUNCTIONAL_DEVICE_EXTENSION FdoExtension
+ )
+ {
+ PCLASS_PRIVATE_FDO_DATA fdoData = FdoExtension->PrivateFdoData;
+ KIRQL oldIrql;
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ DebugPrint((ClassDebugError, "ClasspPerfResetCounters: "
+ "Resetting all perf counters.\n"));
+ fdoData->Perf.SuccessfulIO = 0;
+ FdoExtension->ErrorCount = 0;
+
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_DISCONNECT);
+ }
+ if (!TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER)) {
+ CLEAR_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_QUEUE_ACTION_ENABLE);
+ }
+ if (TEST_FLAG(fdoData->Perf.OriginalSrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE)) {
+ SET_FLAG(FdoExtension->SrbFlags,
+ SRB_FLAGS_NO_QUEUE_FREEZE);
+ }
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ return;
+ }
+#endif
+
+
diff --git a/reactos/drivers/storage/classpnp/xferpkt.c b/reactos/drivers/storage/classpnp/xferpkt.c
new file mode 100644
index 00000000000..7cd1f92cd63
--- /dev/null
+++ b/reactos/drivers/storage/classpnp/xferpkt.c
@@ -0,0 +1,911 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1991 - 1999
+
+Module Name:
+
+ xferpkt.c
+
+Abstract:
+
+ Packet routines for CLASSPNP
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "classp.h"
+#include "debug.h"
+
+#ifdef ALLOC_PRAGMA
+ #pragma alloc_text(PAGE, InitializeTransferPackets)
+ #pragma alloc_text(PAGE, DestroyAllTransferPackets)
+ #pragma alloc_text(PAGE, SetupEjectionTransferPacket)
+ #pragma alloc_text(PAGE, SetupModeSenseTransferPacket)
+#endif
+
+
+ULONG MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
+ULONG MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
+
+
+/*
+ * InitializeTransferPackets
+ *
+ * Allocate/initialize TRANSFER_PACKETs and related resources.
+ */
+NTSTATUS InitializeTransferPackets(PDEVICE_OBJECT Fdo)
+{
+ PCOMMON_DEVICE_EXTENSION commonExt = Fdo->DeviceExtension;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PSTORAGE_ADAPTER_DESCRIPTOR adapterDesc = commonExt->PartitionZeroExtension->AdapterDescriptor;
+ ULONG hwMaxPages;
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ /*
+ * Precompute the maximum transfer length
+ */
+ ASSERT(adapterDesc->MaximumTransferLength);
+ ASSERT(adapterDesc->MaximumPhysicalPages);
+ hwMaxPages = adapterDesc->MaximumPhysicalPages ? adapterDesc->MaximumPhysicalPages-1 : 0;
+
+#if defined(_AMD64_SIMULATOR_)
+
+ //
+ // The simulator appears to have a problem with large transfers.
+ //
+
+ if (hwMaxPages > 4) {
+ hwMaxPages = 4;
+ }
+
+#endif
+
+ fdoData->HwMaxXferLen = MIN(adapterDesc->MaximumTransferLength, hwMaxPages << PAGE_SHIFT);
+ fdoData->HwMaxXferLen = MAX(fdoData->HwMaxXferLen, PAGE_SIZE);
+
+ fdoData->NumTotalTransferPackets = 0;
+ fdoData->NumFreeTransferPackets = 0;
+ InitializeSListHead(&fdoData->FreeTransferPacketsList);
+ InitializeListHead(&fdoData->AllTransferPacketsList);
+ InitializeListHead(&fdoData->DeferredClientIrpList);
+
+ /*
+ * Set the packet threshold numbers based on the Windows SKU.
+ */
+ if (ExVerifySuite(Personal)){
+ // this is Windows Personal
+ MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
+ MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
+ }
+ else if (ExVerifySuite(Enterprise) || ExVerifySuite(DataCenter)){
+ // this is Advanced Server or Datacenter
+ MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Enterprise;
+ MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Enterprise;
+ }
+ else if (ExVerifySuite(TerminalServer)){
+ // this is standard Server or Pro with terminal server
+ MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Server;
+ MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Server;
+ }
+ else {
+ // this is Professional without terminal server
+ MinWorkingSetTransferPackets = MIN_WORKINGSET_TRANSFER_PACKETS_Consumer;
+ MaxWorkingSetTransferPackets = MAX_WORKINGSET_TRANSFER_PACKETS_Consumer;
+ }
+
+ while (fdoData->NumFreeTransferPackets < MIN_INITIAL_TRANSFER_PACKETS){
+ PTRANSFER_PACKET pkt = NewTransferPacket(Fdo);
+ if (pkt){
+ InterlockedIncrement(&fdoData->NumTotalTransferPackets);
+ EnqueueFreeTransferPacket(Fdo, pkt);
+ }
+ else {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+ }
+ fdoData->DbgPeakNumTransferPackets = fdoData->NumTotalTransferPackets;
+
+ /*
+ * Pre-initialize our SCSI_REQUEST_BLOCK template with all
+ * the constant fields. This will save a little time for each xfer.
+ * NOTE: a CdbLength field of 10 may not always be appropriate
+ */
+ RtlZeroMemory(&fdoData->SrbTemplate, sizeof(SCSI_REQUEST_BLOCK));
+ fdoData->SrbTemplate.Length = sizeof(SCSI_REQUEST_BLOCK);
+ fdoData->SrbTemplate.Function = SRB_FUNCTION_EXECUTE_SCSI;
+ fdoData->SrbTemplate.QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ fdoData->SrbTemplate.SenseInfoBufferLength = sizeof(SENSE_DATA);
+ fdoData->SrbTemplate.CdbLength = 10;
+
+ return status;
+}
+
+
+VOID DestroyAllTransferPackets(PDEVICE_OBJECT Fdo)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ TRANSFER_PACKET *pkt;
+
+ PAGED_CODE();
+
+ ASSERT(IsListEmpty(&fdoData->DeferredClientIrpList));
+
+ while (pkt = DequeueFreeTransferPacket(Fdo, FALSE)){
+ DestroyTransferPacket(pkt);
+ InterlockedDecrement(&fdoData->NumTotalTransferPackets);
+ }
+
+ ASSERT(fdoData->NumTotalTransferPackets == 0);
+}
+
+
+PTRANSFER_PACKET NewTransferPacket(PDEVICE_OBJECT Fdo)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PTRANSFER_PACKET newPkt;
+
+ newPkt = ExAllocatePoolWithTag(NonPagedPool, sizeof(TRANSFER_PACKET), 'pnPC');
+ if (newPkt){
+ RtlZeroMemory(newPkt, sizeof(TRANSFER_PACKET)); // just to be sure
+
+ /*
+ * Allocate resources for the packet.
+ */
+ newPkt->Irp = IoAllocateIrp(Fdo->StackSize, FALSE);
+ if (newPkt->Irp){
+ KIRQL oldIrql;
+
+ newPkt->Fdo = Fdo;
+
+ /*
+ * Enqueue the packet in our static AllTransferPacketsList
+ * (just so we can find it during debugging if its stuck somewhere).
+ */
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ InsertTailList(&fdoData->AllTransferPacketsList, &newPkt->AllPktsListEntry);
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+ }
+ else {
+ ExFreePool(newPkt);
+ newPkt = NULL;
+ }
+ }
+
+ return newPkt;
+}
+
+
+/*
+ * DestroyTransferPacket
+ *
+ */
+VOID DestroyTransferPacket(PTRANSFER_PACKET Pkt)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ KIRQL oldIrql;
+
+ ASSERT(!Pkt->SlistEntry.Next);
+ ASSERT(!Pkt->OriginalIrp);
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+
+ /*
+ * Delete the packet from our all-packets queue.
+ */
+ ASSERT(!IsListEmpty(&Pkt->AllPktsListEntry));
+ ASSERT(!IsListEmpty(&fdoData->AllTransferPacketsList));
+ RemoveEntryList(&Pkt->AllPktsListEntry);
+ InitializeListHead(&Pkt->AllPktsListEntry);
+
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ IoFreeIrp(Pkt->Irp);
+ ExFreePool(Pkt);
+}
+
+
+VOID EnqueueFreeTransferPacket(PDEVICE_OBJECT Fdo, PTRANSFER_PACKET Pkt)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ KIRQL oldIrql;
+ ULONG newNumPkts;
+
+ ASSERT(!Pkt->SlistEntry.Next);
+
+ InterlockedPushEntrySList(&fdoData->FreeTransferPacketsList, &Pkt->SlistEntry);
+ newNumPkts = InterlockedIncrement(&fdoData->NumFreeTransferPackets);
+ ASSERT(newNumPkts <= fdoData->NumTotalTransferPackets);
+
+ /*
+ * If the total number of packets is larger than MinWorkingSetTransferPackets,
+ * that means that we've been in stress. If all those packets are now
+ * free, then we are now out of stress and can free the extra packets.
+ * Free down to MaxWorkingSetTransferPackets immediately, and
+ * down to MinWorkingSetTransferPackets lazily (one at a time).
+ */
+ if (fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets){
+
+ /*
+ * 1. Immediately snap down to our UPPER threshold.
+ */
+ if (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets){
+ SINGLE_LIST_ENTRY pktList;
+ PSINGLE_LIST_ENTRY slistEntry;
+ PTRANSFER_PACKET pktToDelete;
+
+ DBGTRACE(ClassDebugTrace, ("Exiting stress, block freeing (%d-%d) packets.", fdoData->NumTotalTransferPackets, MaxWorkingSetTransferPackets));
+
+ /*
+ * Check the counter again with lock held. This eliminates a race condition
+ * while still allowing us to not grab the spinlock in the common codepath.
+ *
+ * Note that the spinlock does not synchronize with threads dequeuing free
+ * packets to send (DequeueFreeTransferPacket does that with a lightweight
+ * interlocked exchange); the spinlock prevents multiple threads in this function
+ * from deciding to free too many extra packets at once.
+ */
+ SimpleInitSlistHdr(&pktList);
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ while ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) &&
+ (fdoData->NumTotalTransferPackets > MaxWorkingSetTransferPackets)){
+
+ pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE);
+ if (pktToDelete){
+ SimplePushSlist(&pktList, &pktToDelete->SlistEntry);
+ InterlockedDecrement(&fdoData->NumTotalTransferPackets);
+ }
+ else {
+ DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (1).", MaxWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets));
+ break;
+ }
+ }
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ while (slistEntry = SimplePopSlist(&pktList)){
+ pktToDelete = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ DestroyTransferPacket(pktToDelete);
+ }
+
+ }
+
+ /*
+ * 2. Lazily work down to our LOWER threshold (by only freeing one packet at a time).
+ */
+ if (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets){
+ /*
+ * Check the counter again with lock held. This eliminates a race condition
+ * while still allowing us to not grab the spinlock in the common codepath.
+ *
+ * Note that the spinlock does not synchronize with threads dequeuing free
+ * packets to send (DequeueFreeTransferPacket does that with a lightweight
+ * interlocked exchange); the spinlock prevents multiple threads in this function
+ * from deciding to free too many extra packets at once.
+ */
+ PTRANSFER_PACKET pktToDelete = NULL;
+
+ DBGTRACE(ClassDebugTrace, ("Exiting stress, lazily freeing one of %d/%d packets.", fdoData->NumTotalTransferPackets, MinWorkingSetTransferPackets));
+
+ KeAcquireSpinLock(&fdoData->SpinLock, &oldIrql);
+ if ((fdoData->NumFreeTransferPackets >= fdoData->NumTotalTransferPackets) &&
+ (fdoData->NumTotalTransferPackets > MinWorkingSetTransferPackets)){
+
+ pktToDelete = DequeueFreeTransferPacket(Fdo, FALSE);
+ if (pktToDelete){
+ InterlockedDecrement(&fdoData->NumTotalTransferPackets);
+ }
+ else {
+ DBGTRACE(ClassDebugTrace, ("Extremely unlikely condition (non-fatal): %d packets dequeued at once for Fdo %p. NumTotalTransferPackets=%d (2).", MinWorkingSetTransferPackets, Fdo, fdoData->NumTotalTransferPackets));
+ }
+ }
+ KeReleaseSpinLock(&fdoData->SpinLock, oldIrql);
+
+ if (pktToDelete){
+ DestroyTransferPacket(pktToDelete);
+ }
+ }
+
+ }
+
+}
+
+
+PTRANSFER_PACKET DequeueFreeTransferPacket(PDEVICE_OBJECT Fdo, BOOLEAN AllocIfNeeded)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PTRANSFER_PACKET pkt;
+ PSINGLE_LIST_ENTRY slistEntry;
+ KIRQL oldIrql;
+
+ slistEntry = InterlockedPopEntrySList(&fdoData->FreeTransferPacketsList);
+ if (slistEntry){
+ slistEntry->Next = NULL;
+ pkt = CONTAINING_RECORD(slistEntry, TRANSFER_PACKET, SlistEntry);
+ ASSERT(fdoData->NumFreeTransferPackets > 0);
+ InterlockedDecrement(&fdoData->NumFreeTransferPackets);
+ }
+ else {
+ if (AllocIfNeeded){
+ /*
+ * We are in stress and have run out of lookaside packets.
+ * In order to service the current transfer,
+ * allocate an extra packet.
+ * We will free it lazily when we are out of stress.
+ */
+ pkt = NewTransferPacket(Fdo);
+ if (pkt){
+ InterlockedIncrement(&fdoData->NumTotalTransferPackets);
+ fdoData->DbgPeakNumTransferPackets = max(fdoData->DbgPeakNumTransferPackets, fdoData->NumTotalTransferPackets);
+ }
+ else {
+ DBGWARN(("DequeueFreeTransferPacket: packet allocation failed"));
+ }
+ }
+ else {
+ pkt = NULL;
+ }
+ }
+
+ return pkt;
+}
+
+
+
+/*
+ * SetupReadWriteTransferPacket
+ *
+ * This function is called once to set up the first attempt to send a packet.
+ * It is not called before a retry, as SRB fields may be modified for the retry.
+ *
+ * Set up the Srb of the TRANSFER_PACKET for the transfer.
+ * The Irp is set up in SubmitTransferPacket because it must be reset
+ * for each packet submission.
+ */
+VOID SetupReadWriteTransferPacket( PTRANSFER_PACKET Pkt,
+ PVOID Buf,
+ ULONG Len,
+ LARGE_INTEGER DiskLocation,
+ PIRP OriginalIrp)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(OriginalIrp);
+ UCHAR majorFunc = origCurSp->MajorFunction;
+ ULONG logicalBlockAddr;
+ ULONG numTransferBlocks;
+ PCDB pCdb;
+
+ logicalBlockAddr = (ULONG)Int64ShrlMod32(DiskLocation.QuadPart, fdoExt->SectorShift);
+ numTransferBlocks = Len >> fdoExt->SectorShift;
+
+ /*
+ * Slap the constant SRB fields in from our pre-initialized template.
+ * We'll then only have to fill in the unique fields for this transfer.
+ * Tell lower drivers to sort the SRBs by the logical block address
+ * so that disk seeks are minimized.
+ */
+ Pkt->Srb = fdoData->SrbTemplate; // copies _contents_ of SRB blocks
+ Pkt->Srb.DataBuffer = Buf;
+ Pkt->Srb.DataTransferLength = Len;
+ Pkt->Srb.QueueSortKey = logicalBlockAddr;
+ Pkt->Srb.OriginalRequest = Pkt->Irp;
+ Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
+ Pkt->Srb.TimeOutValue = (Len/0x10000) + ((Len%0x10000) ? 1 : 0);
+ Pkt->Srb.TimeOutValue *= fdoExt->TimeOutValue;
+
+ /*
+ * Arrange values in CDB in big-endian format.
+ */
+ pCdb = (PCDB)Pkt->Srb.Cdb;
+ pCdb->CDB10.LogicalBlockByte0 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte3;
+ pCdb->CDB10.LogicalBlockByte1 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte2;
+ pCdb->CDB10.LogicalBlockByte2 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte1;
+ pCdb->CDB10.LogicalBlockByte3 = ((PFOUR_BYTE)&logicalBlockAddr)->Byte0;
+ pCdb->CDB10.TransferBlocksMsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte1;
+ pCdb->CDB10.TransferBlocksLsb = ((PFOUR_BYTE)&numTransferBlocks)->Byte0;
+ pCdb->CDB10.OperationCode = (majorFunc==IRP_MJ_READ) ? SCSIOP_READ : SCSIOP_WRITE;
+
+ /*
+ * Set SRB and IRP flags
+ */
+ Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
+ if (TEST_FLAG(OriginalIrp->Flags, IRP_PAGING_IO) ||
+ TEST_FLAG(OriginalIrp->Flags, IRP_SYNCHRONOUS_PAGING_IO)){
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_CLASS_FLAGS_PAGING);
+ }
+ SET_FLAG(Pkt->Srb.SrbFlags, (majorFunc==IRP_MJ_READ) ? SRB_FLAGS_DATA_IN : SRB_FLAGS_DATA_OUT);
+
+ /*
+ * Allow caching only if this is not a write-through request.
+ * If write-through and caching is enabled on the device, force
+ * media access.
+ */
+ if (TEST_FLAG(origCurSp->Flags, SL_WRITE_THROUGH)){
+ if (TEST_FLAG(fdoExt->DeviceFlags, DEV_WRITE_CACHE)){
+ pCdb->CDB10.ForceUnitAccess = TRUE;
+ }
+ }
+ else {
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_ADAPTER_CACHE_ENABLE);
+ }
+
+ /*
+ * Remember the buf and len in the SRB because miniports
+ * can overwrite SRB.DataTransferLength and we may need it again
+ * for the retry.
+ */
+ Pkt->BufPtrCopy = Buf;
+ Pkt->BufLenCopy = Len;
+ Pkt->TargetLocationCopy = DiskLocation;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = MAXIMUM_RETRIES;
+ Pkt->SyncEventPtr = NULL;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = TRUE;
+}
+
+
+/*
+ * SubmitTransferPacket
+ *
+ * Set up the IRP for the TRANSFER_PACKET submission and send it down.
+ */
+VOID SubmitTransferPacket(PTRANSFER_PACKET Pkt)
+{
+ PCOMMON_DEVICE_EXTENSION commonExtension = Pkt->Fdo->DeviceExtension;
+ PDEVICE_OBJECT nextDevObj = commonExtension->LowerDeviceObject;
+ PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(Pkt->Irp);
+
+ ASSERT(Pkt->Irp->CurrentLocation == Pkt->Irp->StackCount+1);
+
+ /*
+ * Attach the SRB to the IRP.
+ * The reused IRP's stack location has to be rewritten for each retry
+ * call because IoCompleteRequest clears the stack locations.
+ */
+ IoReuseIrp(Pkt->Irp, STATUS_NOT_SUPPORTED);
+ nextSp->MajorFunction = IRP_MJ_SCSI;
+ nextSp->Parameters.Scsi.Srb = &Pkt->Srb;
+ Pkt->Srb.ScsiStatus = Pkt->Srb.SrbStatus = 0;
+ if (Pkt->CompleteOriginalIrpWhenLastPacketCompletes){
+ /*
+ * Only dereference the "original IRP"'s stack location
+ * if its a real client irp (as opposed to a static irp
+ * we're using just for result status for one of the non-IO scsi commands).
+ *
+ * For read/write, propagate the storage-specific IRP stack location flags
+ * (e.g. SL_OVERRIDE_VERIFY_VOLUME, SL_WRITE_THROUGH).
+ */
+ PIO_STACK_LOCATION origCurSp = IoGetCurrentIrpStackLocation(Pkt->OriginalIrp);
+ nextSp->Flags = origCurSp->Flags;
+ }
+
+ /*
+ * Write MDL address to new IRP. In the port driver the SRB DataBuffer
+ * field is used as the actual buffer pointer within the MDL,
+ * so the same MDL can be used for each partial transfer.
+ * This saves having to build a new MDL for each partial transfer.
+ */
+ Pkt->Irp->MdlAddress = Pkt->OriginalIrp->MdlAddress;
+
+ IoSetCompletionRoutine(Pkt->Irp, TransferPktComplete, Pkt, TRUE, TRUE, TRUE);
+ IoCallDriver(nextDevObj, Pkt->Irp);
+}
+
+
+NTSTATUS TransferPktComplete(IN PDEVICE_OBJECT NullFdo, IN PIRP Irp, IN PVOID Context)
+{
+ PTRANSFER_PACKET pkt = (PTRANSFER_PACKET)Context;
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PIO_STACK_LOCATION origCurrentSp = IoGetCurrentIrpStackLocation(pkt->OriginalIrp);
+ BOOLEAN packetDone = FALSE;
+
+ /*
+ * Put all the assertions and spew in here so we don't have to look at them.
+ */
+ DBGCHECKRETURNEDPKT(pkt);
+
+ if (SRB_STATUS(pkt->Srb.SrbStatus) == SRB_STATUS_SUCCESS){
+
+ fdoData->LoggedTURFailureSinceLastIO = FALSE;
+
+ /*
+ * The port driver should not have allocated a sense buffer
+ * if the SRB succeeded.
+ */
+ ASSERT(!PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb));
+
+ /*
+ * Add this packet's transferred length to the original IRP's.
+ */
+ InterlockedExchangeAdd((PLONG)&pkt->OriginalIrp->IoStatus.Information,
+ (LONG)pkt->Srb.DataTransferLength);
+
+ if (pkt->InLowMemRetry){
+ packetDone = StepLowMemRetry(pkt);
+ }
+ else {
+ packetDone = TRUE;
+ }
+
+ }
+ else {
+ /*
+ * The packet failed. We may retry it if possible.
+ */
+ BOOLEAN shouldRetry;
+
+ /*
+ * Make sure IRP status matches SRB error status (since we propagate it).
+ */
+ if (NT_SUCCESS(Irp->IoStatus.Status)){
+ Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ * Interpret the SRB error (to a meaningful IRP status)
+ * and determine if we should retry this packet.
+ * This call looks at the returned SENSE info to figure out what to do.
+ */
+ shouldRetry = InterpretTransferPacketError(pkt);
+
+ /*
+ * Sometimes the port driver can allocates a new 'sense' buffer
+ * to report transfer errors, e.g. when the default sense buffer
+ * is too small. If so, it is up to us to free it.
+ * Now that we're done interpreting the sense info, free it if appropriate.
+ */
+ if (PORT_ALLOCATED_SENSE(fdoExt, &pkt->Srb)) {
+ DBGTRACE(ClassDebugSenseInfo, ("Freeing port-allocated sense buffer for pkt %ph.", pkt));
+ FREE_PORT_ALLOCATED_SENSE_BUFFER(fdoExt, &pkt->Srb);
+ pkt->Srb.SenseInfoBuffer = &pkt->SrbErrorSenseData;
+ pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
+ }
+
+ /*
+ * If the SRB queue is locked-up, release it.
+ * Do this after calling the error handler.
+ */
+ if (pkt->Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN){
+ ClassReleaseQueue(pkt->Fdo);
+ }
+
+ if (shouldRetry && (pkt->NumRetries > 0)){
+ packetDone = RetryTransferPacket(pkt);
+ }
+ else {
+ packetDone = TRUE;
+ }
+
+ }
+
+ /*
+ * If the packet is completed, put it back in the free list.
+ * If it is the last packet servicing the original request, complete the original irp.
+ */
+ if (packetDone){
+ LONG numPacketsRemaining;
+ PIRP deferredIrp;
+ PDEVICE_OBJECT Fdo = pkt->Fdo;
+ UCHAR uniqueAddr;
+
+ /*
+ * In case a remove is pending, bump the lock count so we don't get freed
+ * right after we complete the original irp.
+ */
+ ClassAcquireRemoveLock(Fdo, (PIRP)&uniqueAddr);
+
+ /*
+ * The original IRP should get an error code
+ * if any one of the packets failed.
+ */
+ if (!NT_SUCCESS(Irp->IoStatus.Status)){
+ pkt->OriginalIrp->IoStatus.Status = Irp->IoStatus.Status;
+
+ /*
+ * If the original I/O originated in user space (i.e. it is thread-queued),
+ * and the error is user-correctable (e.g. media is missing, for removable media),
+ * alert the user.
+ * Since this is only one of possibly several packets completing for the original IRP,
+ * we may do this more than once for a single request. That's ok; this allows
+ * us to test each returned status with IoIsErrorUserInduced().
+ */
+ if (IoIsErrorUserInduced(Irp->IoStatus.Status) &&
+ pkt->CompleteOriginalIrpWhenLastPacketCompletes &&
+ pkt->OriginalIrp->Tail.Overlay.Thread){
+
+ IoSetHardErrorOrVerifyDevice(pkt->OriginalIrp, pkt->Fdo);
+ }
+ }
+
+ /*
+ * We use a field in the original IRP to count
+ * down the transfer pieces as they complete.
+ */
+ numPacketsRemaining = InterlockedDecrement(
+ (PLONG)&pkt->OriginalIrp->Tail.Overlay.DriverContext[0]);
+
+ if (numPacketsRemaining > 0){
+ /*
+ * More transfer pieces remain for the original request.
+ * Wait for them to complete before completing the original irp.
+ */
+ }
+ else {
+
+ /*
+ * All the transfer pieces are done.
+ * Complete the original irp if appropriate.
+ */
+ ASSERT(numPacketsRemaining == 0);
+ if (pkt->CompleteOriginalIrpWhenLastPacketCompletes){
+ if (NT_SUCCESS(pkt->OriginalIrp->IoStatus.Status)){
+ ASSERT((ULONG)pkt->OriginalIrp->IoStatus.Information == origCurrentSp->Parameters.Read.Length);
+ ClasspPerfIncrementSuccessfulIo(fdoExt);
+ }
+ ClassReleaseRemoveLock(pkt->Fdo, pkt->OriginalIrp);
+
+ ClassCompleteRequest(pkt->Fdo, pkt->OriginalIrp, IO_DISK_INCREMENT);
+
+ /*
+ * We may have been called by one of the class drivers (e.g. cdrom)
+ * via the legacy API ClassSplitRequest.
+ * This is the only case for which the packet engine is called for an FDO
+ * with a StartIo routine; in that case, we have to call IoStartNextPacket
+ * now that the original irp has been completed.
+ */
+ if (fdoExt->CommonExtension.DriverExtension->InitData.ClassStartIo) {
+ if (TEST_FLAG(pkt->Srb.SrbFlags, SRB_FLAGS_DONT_START_NEXT_PACKET)){
+ DBGTRAP(("SRB_FLAGS_DONT_START_NEXT_PACKET should never be set here (?)"));
+ }
+ else {
+ KIRQL oldIrql;
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+ IoStartNextPacket(pkt->Fdo, FALSE);
+ KeLowerIrql(oldIrql);
+ }
+ }
+ }
+ }
+
+ /*
+ * If the packet was synchronous, write the final
+ * result back to the issuer's status buffer and
+ * signal his event.
+ */
+ if (pkt->SyncEventPtr){
+ KeSetEvent(pkt->SyncEventPtr, 0, FALSE);
+ pkt->SyncEventPtr = NULL;
+ }
+
+ /*
+ * Free the completed packet.
+ */
+ pkt->OriginalIrp = NULL;
+ pkt->InLowMemRetry = FALSE;
+ EnqueueFreeTransferPacket(pkt->Fdo, pkt);
+
+ /*
+ * Now that we have freed some resources,
+ * try again to send one of the previously deferred irps.
+ */
+ deferredIrp = DequeueDeferredClientIrp(fdoData);
+ if (deferredIrp){
+ DBGWARN(("... retrying deferred irp %xh.", deferredIrp));
+ ServiceTransferRequest(pkt->Fdo, deferredIrp);
+ }
+
+ ClassReleaseRemoveLock(Fdo, (PIRP)&uniqueAddr);
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+
+/*
+ * SetupEjectionTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Ejection Control transfer.
+ */
+VOID SetupEjectionTransferPacket( TRANSFER_PACKET *Pkt,
+ BOOLEAN PreventMediaRemoval,
+ PKEVENT SyncEventPtr,
+ PIRP OriginalIrp)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+
+ PAGED_CODE();
+
+ RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+ Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ Pkt->Srb.CdbLength = 6;
+ Pkt->Srb.OriginalRequest = Pkt->Irp;
+ Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
+ Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
+ Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue;
+
+ Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = (PCDB)Pkt->Srb.Cdb;
+ pCdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
+ pCdb->MEDIA_REMOVAL.Prevent = PreventMediaRemoval;
+
+ Pkt->BufPtrCopy = NULL;
+ Pkt->BufLenCopy = 0;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_LOCKMEDIAREMOVAL_RETRIES;
+ Pkt->SyncEventPtr = SyncEventPtr;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+}
+
+
+/*
+ * SetupModeSenseTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Mode Sense transfer.
+ */
+VOID SetupModeSenseTransferPacket( TRANSFER_PACKET *Pkt,
+ PKEVENT SyncEventPtr,
+ PVOID ModeSenseBuffer,
+ UCHAR ModeSenseBufferLen,
+ UCHAR PageMode,
+ PIRP OriginalIrp)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+
+ PAGED_CODE();
+
+ RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+ Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ Pkt->Srb.CdbLength = 6;
+ Pkt->Srb.OriginalRequest = Pkt->Irp;
+ Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
+ Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
+ Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue;
+ Pkt->Srb.DataBuffer = ModeSenseBuffer;
+ Pkt->Srb.DataTransferLength = ModeSenseBufferLen;
+
+ Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DATA_IN);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = (PCDB)Pkt->Srb.Cdb;
+ pCdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
+ pCdb->MODE_SENSE.PageCode = PageMode;
+ pCdb->MODE_SENSE.AllocationLength = (UCHAR)ModeSenseBufferLen;
+
+ Pkt->BufPtrCopy = ModeSenseBuffer;
+ Pkt->BufLenCopy = ModeSenseBufferLen;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_MODESENSE_RETRIES;
+ Pkt->SyncEventPtr = SyncEventPtr;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+}
+
+
+/*
+ * SetupDriveCapacityTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Drive Capacity transfer.
+ */
+VOID SetupDriveCapacityTransferPacket( TRANSFER_PACKET *Pkt,
+ PVOID ReadCapacityBuffer,
+ ULONG ReadCapacityBufferLen,
+ PKEVENT SyncEventPtr,
+ PIRP OriginalIrp)
+{
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+
+ RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+ Pkt->Srb.QueueAction = SRB_SIMPLE_TAG_REQUEST;
+ Pkt->Srb.CdbLength = 10;
+ Pkt->Srb.OriginalRequest = Pkt->Irp;
+ Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
+ Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
+ Pkt->Srb.TimeOutValue = fdoExt->TimeOutValue;
+ Pkt->Srb.DataBuffer = ReadCapacityBuffer;
+ Pkt->Srb.DataTransferLength = ReadCapacityBufferLen;
+
+ Pkt->Srb.SrbFlags = fdoExt->SrbFlags;
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DATA_IN);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
+
+ pCdb = (PCDB)Pkt->Srb.Cdb;
+ pCdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
+
+ Pkt->BufPtrCopy = ReadCapacityBuffer;
+ Pkt->BufLenCopy = ReadCapacityBufferLen;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = NUM_DRIVECAPACITY_RETRIES;
+ Pkt->SyncEventPtr = SyncEventPtr;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+}
+
+
+#if 0
+ /*
+ * SetupSendStartUnitTransferPacket
+ *
+ * Set up a transferPacket for a synchronous Send Start Unit transfer.
+ */
+ VOID SetupSendStartUnitTransferPacket( TRANSFER_PACKET *Pkt,
+ PIRP OriginalIrp)
+ {
+ PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Pkt->Fdo->DeviceExtension;
+ PCLASS_PRIVATE_FDO_DATA fdoData = fdoExt->PrivateFdoData;
+ PCDB pCdb;
+
+ PAGED_CODE();
+
+ RtlZeroMemory(&Pkt->Srb, sizeof(SCSI_REQUEST_BLOCK));
+
+ /*
+ * Initialize the SRB.
+ * Use a very long timeout value to give the drive time to spin up.
+ */
+ Pkt->Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+ Pkt->Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+ Pkt->Srb.TimeOutValue = START_UNIT_TIMEOUT;
+ Pkt->Srb.CdbLength = 6;
+ Pkt->Srb.OriginalRequest = Pkt->Irp;
+ Pkt->Srb.SenseInfoBuffer = &Pkt->SrbErrorSenseData;
+ Pkt->Srb.SenseInfoBufferLength = sizeof(SENSE_DATA);
+ Pkt->Srb.Lun = 0;
+
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_AUTOSENSE);
+ SET_FLAG(Pkt->Srb.SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER);
+
+ pCdb = (PCDB)Pkt->Srb.Cdb;
+ pCdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
+ pCdb->START_STOP.Start = 1;
+ pCdb->START_STOP.Immediate = 0;
+ pCdb->START_STOP.LogicalUnitNumber = 0;
+
+ Pkt->OriginalIrp = OriginalIrp;
+ Pkt->NumRetries = 0;
+ Pkt->SyncEventPtr = NULL;
+ Pkt->CompleteOriginalIrpWhenLastPacketCompletes = FALSE;
+ }
+#endif
+
+
diff --git a/reactos/drivers/storage/directory.rbuild b/reactos/drivers/storage/directory.rbuild
index c0c43724a9e..e25b40aae9e 100644
--- a/reactos/drivers/storage/directory.rbuild
+++ b/reactos/drivers/storage/directory.rbuild
@@ -16,4 +16,7 @@
+
+
+
diff --git a/reactos/drivers/storage/inc/class.h b/reactos/drivers/storage/inc/class.h
new file mode 100644
index 00000000000..f983d4ce3c0
--- /dev/null
+++ b/reactos/drivers/storage/inc/class.h
@@ -0,0 +1,374 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ class.h
+
+Abstract:
+
+ These are the structures and defines that are used in the
+ SCSI class drivers.
+
+Author:
+
+ Mike Glass (mglass)
+ Jeff Havens (jhavens)
+
+Revision History:
+
+--*/
+
+#ifndef _CLASS_
+
+#include
+#include
+#include
+#include
+#include
+#include "ntddscsi.h"
+#include
+
+// begin_ntminitape
+
+#if DBG
+
+#define DebugPrint(x) ScsiDebugPrint x
+
+#else
+
+#define DebugPrint(x)
+
+#endif // DBG
+
+// end_ntminitape
+
+#ifdef POOL_TAGGING
+#undef ExAllocatePool
+#undef ExAllocatePoolWithQuota
+#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'HscS')
+#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,'HscS')
+#endif
+
+#define MAXIMUM_RETRIES 4
+
+typedef
+VOID
+(*PCLASS_ERROR) (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN OUT NTSTATUS *Status,
+ IN OUT BOOLEAN *Retry
+ );
+
+typedef struct _DEVICE_EXTENSION {
+
+ //
+ // Back pointer to device object
+ //
+
+ PDEVICE_OBJECT DeviceObject;
+
+ //
+ // Pointer to port device object
+ //
+
+ PDEVICE_OBJECT PortDeviceObject;
+
+ //
+ // Length of partition in bytes
+ //
+
+ LARGE_INTEGER PartitionLength;
+
+ //
+ // Number of bytes before start of partition
+ //
+
+ LARGE_INTEGER StartingOffset;
+
+ //
+ // Bytes to skew all requests, since DM Driver has been placed on an IDE drive.
+ //
+
+ ULONG DMByteSkew;
+
+ //
+ // Sectors to skew all requests.
+ //
+
+ ULONG DMSkew;
+
+ //
+ // Flag to indicate whether DM driver has been located on an IDE drive.
+ //
+
+ BOOLEAN DMActive;
+
+ //
+ // Pointer to the specific class error routine.
+ //
+
+ PCLASS_ERROR ClassError;
+
+ //
+ // SCSI port driver capabilities
+ //
+
+ PIO_SCSI_CAPABILITIES PortCapabilities;
+
+ //
+ // Buffer for drive parameters returned in IO device control.
+ //
+
+ PDISK_GEOMETRY DiskGeometry;
+
+ //
+ // Back pointer to device object of physical device
+ //
+
+ PDEVICE_OBJECT PhysicalDevice;
+
+ //
+ // Request Sense Buffer
+ //
+
+ PSENSE_DATA SenseData;
+
+ //
+ // Request timeout in seconds;
+ //
+
+ ULONG TimeOutValue;
+
+ //
+ // System device number
+ //
+
+ ULONG DeviceNumber;
+
+ //
+ // Add default Srb Flags.
+ //
+
+ ULONG SrbFlags;
+
+ //
+ // Total number of SCSI protocol errors on the device.
+ //
+
+ ULONG ErrorCount;
+
+ //
+ // Spinlock for split requests
+ //
+
+ KSPIN_LOCK SplitRequestSpinLock;
+
+ //
+ // Zone header and spin lock for zoned SRB requests.
+ //
+
+ PZONE_HEADER SrbZone;
+
+ PKSPIN_LOCK SrbZoneSpinLock;
+
+ //
+ // Lock count for removable media.
+ //
+
+ LONG LockCount;
+
+ //
+ // Scsi port number
+ //
+
+ UCHAR PortNumber;
+
+ //
+ // SCSI path id
+ //
+
+ UCHAR PathId;
+
+ //
+ // SCSI bus target id
+ //
+
+ UCHAR TargetId;
+
+ //
+ // SCSI bus logical unit number
+ //
+
+ UCHAR Lun;
+
+ //
+ // Log2 of sector size
+ //
+
+ UCHAR SectorShift;
+
+ //
+ // Flag to indicate that the device has write caching enabled.
+ //
+
+ BOOLEAN WriteCache;
+
+ //
+ // Build SCSI 1 or SCSI 2 CDBs
+ //
+
+ BOOLEAN UseScsi1;
+
+} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
+
+//
+// Define context structure for asynchronous completions.
+//
+
+typedef struct _COMPLETION_CONTEXT {
+ PDEVICE_OBJECT DeviceObject;
+ SCSI_REQUEST_BLOCK Srb;
+}COMPLETION_CONTEXT, *PCOMPLETION_CONTEXT;
+
+
+NTSTATUS
+ScsiClassGetCapabilities(
+ IN PDEVICE_OBJECT PortDeviceObject,
+ OUT PIO_SCSI_CAPABILITIES *PortCapabilities
+ );
+
+NTSTATUS
+ScsiClassGetInquiryData(
+ IN PDEVICE_OBJECT PortDeviceObject,
+ IN PSCSI_ADAPTER_BUS_INFO *ConfigInfo
+ );
+
+NTSTATUS
+ScsiClassReadDriveCapacity(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+VOID
+ScsiClassReleaseQueue(
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+NTSTATUS
+ScsiClassRemoveDevice(
+ IN PDEVICE_OBJECT PortDeviceObject,
+ IN UCHAR PathId,
+ IN UCHAR TargetId,
+ IN UCHAR Lun
+ );
+
+NTSTATUS
+ScsiClassAsynchronousCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ );
+
+VOID
+ScsiClassSplitRequest(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN ULONG MaximumBytes
+ );
+
+NTSTATUS
+ScsiClassDeviceControl(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+NTSTATUS
+ScsiClassIoComplete(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+ScsiClassIoCompleteAssociated(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+BOOLEAN
+ScsiClassInterpretSenseInfo(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN UCHAR MajorFunctionCode,
+ IN ULONG IoDeviceCode,
+ IN ULONG RetryCount,
+ OUT NTSTATUS *Status
+ );
+
+NTSTATUS
+ScsiClassSendSrbSynchronous(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ PVOID BufferAddress,
+ ULONG BufferLength,
+ BOOLEAN WriteToDevice
+ );
+
+NTSTATUS
+ScsiClassSendSrbAsynchronous(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ PIRP Irp,
+ PVOID BufferAddress,
+ ULONG BufferLength,
+ BOOLEAN WriteToDevice
+ );
+
+VOID
+ScsiClassBuildRequest(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp
+ );
+
+ULONG
+ScsiClassModeSense(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PCHAR ModeSenseBuffer,
+ IN ULONG Length,
+ IN UCHAR PageMode
+ );
+
+BOOLEAN
+ScsiClassModeSelect(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PCHAR ModeSelectBuffer,
+ IN ULONG Length,
+ IN BOOLEAN SavePage
+ );
+
+PVOID
+ScsiClassFindModePage(
+ IN PCHAR ModeSenseBuffer,
+ IN ULONG Length,
+ IN UCHAR PageMode
+ );
+
+NTSTATUS
+ScsiClassClaimDevice(
+ IN PDEVICE_OBJECT PortDeviceObject,
+ IN PSCSI_INQUIRY_DATA LunInfo,
+ IN BOOLEAN Release,
+ OUT PDEVICE_OBJECT *NewPortDeviceObject OPTIONAL
+ );
+
+NTSTATUS
+ScsiClassInternalIoControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+#endif /* _CLASS_ */
+
diff --git a/reactos/drivers/storage/inc/ide.h b/reactos/drivers/storage/inc/ide.h
new file mode 100644
index 00000000000..9c868ca587e
--- /dev/null
+++ b/reactos/drivers/storage/inc/ide.h
@@ -0,0 +1,495 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1999 - 1999
+
+Module Name:
+
+ ide.h
+
+Abstract:
+
+ These are the structures and defines that are used in the
+ PCI IDE mini drivers.
+
+Revision History:
+
+--*/
+
+
+#if !defined (___ide_h___)
+#define ___ide_h___
+
+#include "ideuser.h"
+
+#define MAX_IDE_DEVICE 2
+#define MAX_IDE_LINE 2
+#define MAX_IDE_CHANNEL 2
+
+//
+// Some miniports need this structure.
+// IdentifyData is passed to the miniport in
+// the XfermodeSelect structure
+//
+
+//
+// IDENTIFY data
+//
+
+#pragma pack (1)
+typedef struct _IDENTIFY_DATA {
+ USHORT GeneralConfiguration; // 00 00
+ USHORT NumCylinders; // 02 1
+ USHORT Reserved1; // 04 2
+ USHORT NumHeads; // 06 3
+ USHORT UnformattedBytesPerTrack; // 08 4
+ USHORT UnformattedBytesPerSector; // 0A 5
+ USHORT NumSectorsPerTrack; // 0C 6
+ USHORT VendorUnique1[3]; // 0E 7-9
+ UCHAR SerialNumber[20]; // 14 10-19
+ USHORT BufferType; // 28 20
+ USHORT BufferSectorSize; // 2A 21
+ USHORT NumberOfEccBytes; // 2C 22
+ UCHAR FirmwareRevision[8]; // 2E 23-26
+ UCHAR ModelNumber[40]; // 36 27-46
+ UCHAR MaximumBlockTransfer; // 5E 47
+ UCHAR VendorUnique2; // 5F
+ USHORT DoubleWordIo; // 60 48
+ USHORT Capabilities; // 62 49
+ USHORT Reserved2; // 64 50
+ UCHAR VendorUnique3; // 66 51
+ UCHAR PioCycleTimingMode; // 67
+ UCHAR VendorUnique4; // 68 52
+ UCHAR DmaCycleTimingMode; // 69
+ USHORT TranslationFieldsValid:3; // 6A 53
+ USHORT Reserved3:13;
+ USHORT NumberOfCurrentCylinders; // 6C 54
+ USHORT NumberOfCurrentHeads; // 6E 55
+ USHORT CurrentSectorsPerTrack; // 70 56
+ ULONG CurrentSectorCapacity; // 72 57-58
+ USHORT CurrentMultiSectorSetting; // 59
+ ULONG UserAddressableSectors; // 60-61
+ USHORT SingleWordDMASupport : 8; // 62
+ USHORT SingleWordDMAActive : 8;
+ USHORT MultiWordDMASupport : 8; // 63
+ USHORT MultiWordDMAActive : 8;
+ USHORT AdvancedPIOModes : 8; // 64
+ USHORT Reserved4 : 8;
+ USHORT MinimumMWXferCycleTime; // 65
+ USHORT RecommendedMWXferCycleTime; // 66
+ USHORT MinimumPIOCycleTime; // 67
+ USHORT MinimumPIOCycleTimeIORDY; // 68
+ USHORT Reserved5[11]; // 69-79
+ USHORT MajorRevision; // 80
+ USHORT MinorRevision; // 81
+ USHORT Reserved6; // 82
+ USHORT CommandSetSupport; // 83
+ USHORT Reserved6a[2]; // 84-85
+ USHORT CommandSetActive; // 86
+ USHORT Reserved6b; // 87
+ USHORT UltraDMASupport : 8; // 88
+ USHORT UltraDMAActive : 8; //
+ USHORT Reserved7[11]; // 89-99
+ ULONG Max48BitLBA[2]; // 100-103
+ USHORT Reserved7a[22]; // 104-125
+ USHORT LastLun:3; // 126
+ USHORT Reserved8:13;
+ USHORT MediaStatusNotification:2; // 127
+ USHORT Reserved9:6;
+ USHORT DeviceWriteProtect:1;
+ USHORT Reserved10:7;
+ USHORT Reserved11[128]; // 128-255
+} IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+//
+// Identify data without the Reserved4.
+//
+
+//typedef struct _IDENTIFY_DATA2 {
+// USHORT GeneralConfiguration; // 00 00
+// USHORT NumCylinders; // 02 1
+// USHORT Reserved1; // 04 2
+// USHORT NumHeads; // 06 3
+// USHORT UnformattedBytesPerTrack; // 08 4
+// USHORT UnformattedBytesPerSector; // 0A 5
+// USHORT NumSectorsPerTrack; // 0C 6
+// USHORT VendorUnique1[3]; // 0E 7-9
+// UCHAR SerialNumber[20]; // 14 10-19
+// USHORT BufferType; // 28 20
+// USHORT BufferSectorSize; // 2A 21
+// USHORT NumberOfEccBytes; // 2C 22
+// UCHAR FirmwareRevision[8]; // 2E 23-26
+// UCHAR ModelNumber[40]; // 36 27-46
+// UCHAR MaximumBlockTransfer; // 5E 47
+// UCHAR VendorUnique2; // 5F
+// USHORT DoubleWordIo; // 60 48
+// USHORT Capabilities; // 62 49
+// USHORT Reserved2; // 64 50
+// UCHAR VendorUnique3; // 66 51
+// UCHAR PioCycleTimingMode; // 67
+// UCHAR VendorUnique4; // 68 52
+// UCHAR DmaCycleTimingMode; // 69
+// USHORT TranslationFieldsValid:3; // 6A 53
+// USHORT Reserved3:13;
+// USHORT NumberOfCurrentCylinders; // 6C 54
+// USHORT NumberOfCurrentHeads; // 6E 55
+// USHORT CurrentSectorsPerTrack; // 70 56
+// ULONG CurrentSectorCapacity; // 72 57-58
+// USHORT CurrentMultiSectorSetting; // 59
+// ULONG UserAddressableSectors; // 60-61
+// USHORT SingleWordDMASupport : 8; // 62
+// USHORT SingleWordDMAActive : 8;
+// USHORT MultiWordDMASupport : 8; // 63
+// USHORT MultiWordDMAActive : 8;
+// USHORT AdvancedPIOModes : 8; // 64
+// USHORT Reserved4 : 8;
+// USHORT MinimumMWXferCycleTime; // 65
+// USHORT RecommendedMWXferCycleTime; // 66
+// USHORT MinimumPIOCycleTime; // 67
+// USHORT MinimumPIOCycleTimeIORDY; // 68
+// USHORT Reserved5[11]; // 69-79
+// USHORT MajorRevision; // 80
+// USHORT MinorRevision; // 81
+// USHORT Reserved6[6]; // 82-87
+// USHORT UltraDMASupport : 8; // 88
+// USHORT UltraDMAActive : 8; //
+// USHORT Reserved7[37]; // 89-125
+// USHORT LastLun:3; // 126
+// USHORT Reserved8:13;
+// USHORT MediaStatusNotification:2; // 127
+// USHORT Reserved9:6;
+// USHORT DeviceWriteProtect:1;
+// USHORT Reserved10:7;
+//} IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+#pragma pack ()
+
+#define IDENTIFY_DATA_SIZE sizeof(IDENTIFY_DATA)
+
+
+//
+// The structure is passed to pci ide mini driver
+// TransferModeSelect callback for selecting
+// proper transfer mode the the devices connected
+// to the given IDE channel
+//
+typedef struct _PCIIDE_TRANSFER_MODE_SELECT {
+
+ //
+ // Input Parameters
+ //
+
+ //
+ // IDE Channel Number. 0 or 1
+ //
+ ULONG Channel;
+
+ //
+ // Indicate whether devices are present
+ //
+ BOOLEAN DevicePresent[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // Indicate whether devices are ATA harddisk
+ //
+ BOOLEAN FixedDisk[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // Indicate whether devices support IO Ready Line
+ //
+ BOOLEAN IoReadySupported[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // Indicate the data transfer modes devices support
+ //
+ ULONG DeviceTransferModeSupported[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // Indicate devices' best timings for PIO, single word DMA,
+ // multiword DMA, and Ultra DMA modes
+ //
+ ULONG BestPioCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE];
+ ULONG BestSwDmaCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE];
+ ULONG BestMwDmaCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE];
+ ULONG BestUDmaCycleTime[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // Indicate devices' current data transfer modes
+ //
+ ULONG DeviceTransferModeCurrent[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // The user's choice. This will allow pciidex to
+ // default to a transfer mode indicated by the mini driver
+ //
+ ULONG UserChoiceTransferMode[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // This enables UDMA66 on the intel chipsets
+ //
+ ULONG EnableUDMA66;
+
+ //
+ //Some miniports need this
+ // The miniport will save this data in their deviceExtension
+ //
+ IDENTIFY_DATA IdentifyData[MAX_IDE_DEVICE];
+
+
+ //
+ // Output Parameters
+ //
+
+ //
+ // Indicate devices' data transfer modes chosen by
+ // the pcii ide mini drive
+ //
+ ULONG DeviceTransferModeSelected[MAX_IDE_DEVICE * MAX_IDE_LINE];
+
+ //
+ // Transfermode timings
+ //
+ PULONG TransferModeTimingTable;
+ ULONG TransferModeTableLength;
+
+} PCIIDE_TRANSFER_MODE_SELECT, *PPCIIDE_TRANSFER_MODE_SELECT;
+
+//
+// possible ide channel state
+//
+
+typedef enum {
+ ChannelDisabled = 0,
+ ChannelEnabled,
+ ChannelStateUnknown
+} IDE_CHANNEL_STATE;
+
+
+//
+// Prototype for different PCI IDE mini driver
+// callbacks
+//
+typedef IDE_CHANNEL_STATE
+ (*PCIIDE_CHANNEL_ENABLED) (
+ IN PVOID DeviceExtension,
+ IN ULONG Channel
+ );
+
+typedef BOOLEAN
+ (*PCIIDE_SYNC_ACCESS_REQUIRED) (
+ IN PVOID DeviceExtension
+ );
+
+typedef NTSTATUS
+ (*PCIIDE_TRANSFER_MODE_SELECT_FUNC) (
+ IN PVOID DeviceExtension,
+ IN OUT PPCIIDE_TRANSFER_MODE_SELECT TransferModeSelect
+ );
+
+typedef ULONG
+ (*PCIIDE_USEDMA_FUNC)(
+ IN PVOID deviceExtension,
+ IN PVOID cdbCmd,
+ IN UCHAR targetID
+ );
+
+typedef NTSTATUS
+ (*PCIIDE_UDMA_MODES_SUPPORTED) (
+ IDENTIFY_DATA IdentifyData,
+ PULONG BestXferMode,
+ PULONG CurrentMode
+ );
+//
+// This structure is for the PCI IDE mini driver to
+// return its properties
+//
+typedef struct _IDE_CONTROLLER_PROPERTIES {
+
+ //
+ // sizeof (IDE_CONTROLLER_PROPERTIES)
+ //
+ ULONG Size;
+
+ //
+ // Indicate the amount of memory PCI IDE mini driver
+ // needs for its private data
+ //
+ ULONG ExtensionSize;
+
+ //
+ // Indicate all the data transfer modes the PCI IDE
+ // controller supports
+ //
+ ULONG SupportedTransferMode[MAX_IDE_CHANNEL][MAX_IDE_DEVICE];
+
+ //
+ // callback to query whether a IDE channel is enabled
+ //
+ PCIIDE_CHANNEL_ENABLED PciIdeChannelEnabled;
+
+ //
+ // callback to query whether both IDE channels requires
+ // synchronized access. (one channel at a time)
+ //
+ PCIIDE_SYNC_ACCESS_REQUIRED PciIdeSyncAccessRequired;
+
+ //
+ // callback to select proper transfer modes for the
+ // given devices
+ //
+ PCIIDE_TRANSFER_MODE_SELECT_FUNC PciIdeTransferModeSelect;
+
+ //
+ // at the end of a ATA data transfer, ignores busmaster
+ // status active bit. Normally, it should be FALSE
+ //
+ BOOLEAN IgnoreActiveBitForAtaDevice;
+
+ //
+ // always clear the busmaster interrupt on every interrupt
+ // generated by the device. Normnally, it should be FALSE
+ //
+ BOOLEAN AlwaysClearBusMasterInterrupt;
+
+ //
+ // callback to determine whether DMA should be used or not
+ // called for every IO
+ //
+ PCIIDE_USEDMA_FUNC PciIdeUseDma;
+
+
+ //
+ // if the miniport needs a different alignment
+ //
+ ULONG AlignmentRequirement;
+
+ ULONG DefaultPIO;
+
+ //
+ // retrieves the supported udma modes from the Identify data
+ //
+ PCIIDE_UDMA_MODES_SUPPORTED PciIdeUdmaModesSupported;
+
+} IDE_CONTROLLER_PROPERTIES, *PIDE_CONTROLLER_PROPERTIES;
+
+//
+// callback to query PCI IDE controller properties
+//
+typedef
+NTSTATUS (*PCONTROLLER_PROPERTIES) (
+ IN PVOID DeviceExtension,
+ IN PIDE_CONTROLLER_PROPERTIES ControllerProperties
+ );
+
+
+//
+// To initailize PCI IDE mini driver
+//
+NTSTATUS
+PciIdeXInitialize(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath,
+ IN PCONTROLLER_PROPERTIES PciIdeGetControllerProperties,
+ IN ULONG ExtensionSize
+ );
+
+//
+// To query PCI IDE config space data
+//
+NTSTATUS
+PciIdeXGetBusData(
+ IN PVOID DeviceExtension,
+ IN PVOID Buffer,
+ IN ULONG ConfigDataOffset,
+ IN ULONG BufferLength
+ );
+
+//
+// To save PCI IDE config space data
+//
+NTSTATUS
+PciIdeXSetBusData(
+ IN PVOID DeviceExtension,
+ IN PVOID Buffer,
+ IN PVOID DataMask,
+ IN ULONG ConfigDataOffset,
+ IN ULONG BufferLength
+ );
+
+
+#pragma pack(1)
+typedef struct _PCIIDE_CONFIG_HEADER {
+
+ USHORT VendorID; // (ro)
+ USHORT DeviceID; // (ro)
+
+ //
+ // Command
+ //
+ union {
+
+ struct {
+
+ USHORT IoAccessEnable:1; // Device control
+ USHORT MemAccessEnable:1;
+ USHORT MasterEnable:1;
+ USHORT SpecialCycle:1;
+ USHORT MemWriteInvalidateEnable:1;
+ USHORT VgaPaletteSnoopEnable:1;
+ USHORT ParityErrorResponse:1;
+ USHORT WaitCycleEnable:1;
+ USHORT SystemErrorEnable:1;
+ USHORT FastBackToBackEnable:1;
+ USHORT CommandReserved:6;
+ } b;
+
+ USHORT w;
+
+ } Command;
+
+
+ USHORT Status;
+ UCHAR RevisionID; // (ro)
+
+ //
+ // Program Interface
+ //
+ UCHAR Chan0OpMode:1;
+ UCHAR Chan0Programmable:1;
+ UCHAR Chan1OpMode:1;
+ UCHAR Chan1Programmable:1;
+ UCHAR ProgIfReserved:3;
+ UCHAR MasterIde:1;
+
+ UCHAR SubClass; // (ro)
+ UCHAR BaseClass; // (ro)
+ UCHAR CacheLineSize; // (ro+)
+ UCHAR LatencyTimer; // (ro+)
+ UCHAR HeaderType; // (ro)
+ UCHAR BIST; // Built in self test
+
+ struct _PCI_HEADER_TYPE_0 type0;
+
+} PCIIDE_CONFIG_HEADER, *PPCIIDE_CONFIG_HEADER;
+#pragma pack()
+
+//
+// Debug Print
+//
+#if DBG
+
+VOID
+PciIdeXDebugPrint(
+ ULONG DebugPrintLevel,
+ PCCHAR DebugMessage,
+ ...
+ );
+
+#define PciIdeXDebugPrint(x) PciIdeXDebugPrint x
+
+#else
+
+#define PciIdeXDebugPrint(x)
+
+#endif // DBG
+
+#endif // ___ide_h___
+
diff --git a/reactos/drivers/storage/inc/ideuser.h b/reactos/drivers/storage/inc/ideuser.h
new file mode 100644
index 00000000000..babde7162f2
--- /dev/null
+++ b/reactos/drivers/storage/inc/ideuser.h
@@ -0,0 +1,132 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1999 - 1999
+
+Module Name:
+
+ ideuser.h
+
+Abstract:
+
+ These are the structures and defines that are used in the
+ PCI IDE mini drivers.
+
+Revision History:
+
+--*/
+
+#if !defined (___ideuser_h___)
+#define ___ideuser_h___
+
+
+#define PIO_MODE0 (1 << 0)
+#define PIO_MODE1 (1 << 1)
+#define PIO_MODE2 (1 << 2)
+#define PIO_MODE3 (1 << 3)
+#define PIO_MODE4 (1 << 4)
+
+#define SWDMA_MODE0 (1 << 5)
+#define SWDMA_MODE1 (1 << 6)
+#define SWDMA_MODE2 (1 << 7)
+
+#define MWDMA_MODE0 (1 << 8)
+#define MWDMA_MODE1 (1 << 9)
+#define MWDMA_MODE2 (1 << 10)
+
+#define UDMA_MODE0 (1 << 11)
+#define UDMA_MODE1 (1 << 12)
+#define UDMA_MODE2 (1 << 13)
+#define UDMA_MODE3 (1 << 14)
+#define UDMA_MODE4 (1 << 15)
+#define UDMA_MODE5 (1 << 16)
+
+#define PIO_SUPPORT (PIO_MODE0 | PIO_MODE1 | PIO_MODE2 | PIO_MODE3 | PIO_MODE4)
+#define SWDMA_SUPPORT (SWDMA_MODE0 | SWDMA_MODE1 | SWDMA_MODE2)
+#define MWDMA_SUPPORT (MWDMA_MODE0 | MWDMA_MODE1 | MWDMA_MODE2)
+#define UDMA33_SUPPORT (UDMA_MODE0 | UDMA_MODE1 | UDMA_MODE2)
+#define UDMA66_SUPPORT (UDMA_MODE3 | UDMA_MODE4)
+#define UDMA100_SUPPORT (UDMA_MODE5 )
+#define UDMA_SUPPORT (UNINITIALIZED_TRANSFER_MODE & (~(PIO_SUPPORT | SWDMA_SUPPORT | MWDMA_SUPPORT)))
+
+#define DMA_SUPPORT (SWDMA_SUPPORT | MWDMA_SUPPORT | UDMA_SUPPORT)
+#define ALL_MODE_SUPPORT (PIO_SUPPORT | DMA_SUPPORT)
+
+#define PIO0 0
+#define PIO1 1
+#define PIO2 2
+#define PIO3 3
+#define PIO4 4
+#define SWDMA0 5
+#define SWDMA1 6
+#define SWDMA2 7
+#define MWDMA0 8
+#define MWDMA1 9
+#define MWDMA2 10
+#define UDMA0 11
+
+#define MAX_XFER_MODE 17
+#define UNINITIALIZED_CYCLE_TIME 0xffffffff
+#define UNINITIALIZED_TRANSFER_MODE 0x7fffffff
+#define IS_DEFAULT(mode) (!(mode & 0x80000000))
+
+#define GenTransferModeMask(i, mode) {\
+ ULONG temp=0xffffffff; \
+ mode |= (temp >> (31-(i)));\
+}
+
+//
+// mode should not be 0
+//
+#define GetHighestTransferMode(mode, i) {\
+ ULONG temp=(mode); \
+ ASSERT(temp); \
+ i=0; \
+ while ( temp) { \
+ temp = (temp >> 1);\
+ i++;\
+ } \
+ i--; \
+}
+
+#define GetHighestDMATransferMode(mode, i) {\
+ ULONG temp=mode >> 5;\
+ i=5; \
+ while ( temp) { \
+ temp = (temp >> 1); \
+ i++; \
+ } \
+ i--; \
+}
+#define GetHighestPIOTransferMode(mode, i) { \
+ ULONG temp = (mode & PIO_SUPPORT); \
+ i=0; \
+ temp = temp >> 1; \
+ while (temp) { \
+ temp = temp >> 1; \
+ i++; \
+ } \
+}
+
+#define SetDefaultTiming(timingTable, length) {\
+ timingTable[0]=PIO_MODE0_CYCLE_TIME; \
+ timingTable[1]=PIO_MODE1_CYCLE_TIME; \
+ timingTable[2]=PIO_MODE2_CYCLE_TIME; \
+ timingTable[3]=PIO_MODE3_CYCLE_TIME; \
+ timingTable[4]=PIO_MODE4_CYCLE_TIME; \
+ timingTable[5]=SWDMA_MODE0_CYCLE_TIME; \
+ timingTable[6]=SWDMA_MODE1_CYCLE_TIME; \
+ timingTable[7]=SWDMA_MODE2_CYCLE_TIME; \
+ timingTable[8]=MWDMA_MODE0_CYCLE_TIME; \
+ timingTable[9]=MWDMA_MODE1_CYCLE_TIME; \
+ timingTable[10]=MWDMA_MODE2_CYCLE_TIME; \
+ timingTable[11]=UDMA_MODE0_CYCLE_TIME; \
+ timingTable[12]=UDMA_MODE1_CYCLE_TIME; \
+ timingTable[13]=UDMA_MODE2_CYCLE_TIME; \
+ timingTable[14]=UDMA_MODE3_CYCLE_TIME; \
+ timingTable[15]=UDMA_MODE4_CYCLE_TIME; \
+ timingTable[16]=UDMA_MODE5_CYCLE_TIME; \
+ length = MAX_XFER_MODE; \
+}
+
+#endif // ___ideuser_h___
+
diff --git a/reactos/drivers/storage/inc/physlogi.h b/reactos/drivers/storage/inc/physlogi.h
new file mode 100644
index 00000000000..57e71e0c7c8
--- /dev/null
+++ b/reactos/drivers/storage/inc/physlogi.h
@@ -0,0 +1,115 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ physlogi.h
+
+Abstract:
+
+ This file contains structures and defines that are used
+ specifically for the tape drivers. Contains #define's,
+ function protypes, etc. for use in calling functions in
+ physlogi.c that do physical to pseudo-logical and pseudo-
+ logical to physical tape block address/position translation.
+
+Author:
+
+ Mike Colandreo (Maynard)
+
+Revision History:
+
+--*/
+
+// begin_ntminitape
+
+//
+// defines for QIC tape density codes
+//
+
+#define QIC_XX 0 // ????
+#define QIC_24 5 // 0x05
+#define QIC_120 15 // 0x0F
+#define QIC_150 16 // 0x10
+#define QIC_525 17 // 0x11
+#define QIC_1350 18 // 0x12
+#define QIC_1000 21 // 0x15
+#define QIC_1000C 30 // 0x1E
+#define QIC_2100 31 // 0x1F
+#define QIC_2GB 34 // 0x22
+#define QIC_5GB 40 // 0x28
+
+//
+// defines for QIC tape media codes
+//
+
+#define DCXXXX 0
+#define DC300 1
+#define DC300XLP 2
+#define DC615 3
+#define DC600 4
+#define DC6037 5
+#define DC6150 6
+#define DC6250 7
+#define DC6320 8
+#define DC6525 9
+#define DC9135SL 33 //0x21
+#define DC9210 34 //0x22
+#define DC9135 35 //0x23
+#define DC9100 36 //0x24
+#define DC9120 37 //0x25
+#define DC9120SL 38 //0x26
+#define DC9164 39 //0x27
+#define DCXXXXFW 48 //0x30
+#define DC9200SL 49 //0x31
+#define DC9210XL 50 //0x32
+#define DC10GB 51 //0x33
+#define DC9200 52 //0x34
+#define DC9120XL 53 //0x35
+#define DC9210SL 54 //0x36
+#define DC9164XL 55 //0x37
+#define DC9200XL 64 //0x40
+#define DC9400 65 //0x41
+#define DC9500 66 //0x42
+#define DC9500SL 70 //0x46
+
+//
+// defines for translation reference point
+//
+
+#define NOT_FROM_BOT 0
+#define FROM_BOT 1
+
+//
+// info/structure returned by/from
+// TapeLogicalBlockToPhysicalBlock( )
+//
+
+typedef struct _TAPE_PHYS_POSITION {
+ ULONG SeekBlockAddress;
+ ULONG SpaceBlockCount;
+} TAPE_PHYS_POSITION, PTAPE_PHYS_POSITION;
+
+//
+// function prototypes
+//
+
+TAPE_PHYS_POSITION
+TapeClassLogicalBlockToPhysicalBlock(
+ IN UCHAR DensityCode,
+ IN ULONG LogicalBlockAddress,
+ IN ULONG BlockLength,
+ IN BOOLEAN FromBOT
+ );
+
+ULONG
+TapeClassPhysicalBlockToLogicalBlock(
+ IN UCHAR DensityCode,
+ IN ULONG PhysicalBlockAddress,
+ IN ULONG BlockLength,
+ IN BOOLEAN FromBOT
+ );
+
+// end_ntminitape
+
diff --git a/reactos/drivers/storage/inc/rbc.h b/reactos/drivers/storage/inc/rbc.h
new file mode 100644
index 00000000000..5afa8bb1d7a
--- /dev/null
+++ b/reactos/drivers/storage/inc/rbc.h
@@ -0,0 +1,180 @@
+/*++
+
+Copyright (C) Microsoft Corporation, 1998 - 1999
+
+Module Name:
+
+ rbc.h
+
+Abstract:
+
+ These are the structures and defines used in the Reduced Block Command set
+
+Authors:
+
+ George Chrysanthakopoulos(georgioc) - April 1998
+
+Revision History:
+
+ Dan Knudson (DanKn), 23 Sep 1999 - updated per rev 10 of RBC spec
+
+--*/
+#ifndef _NTRBC_
+#define _NTRBC_
+
+#include "scsi.h"
+
+//
+// Command Descriptor Block. encapsulated under the bus/protocol specific request block
+//
+
+typedef union _CDB_RBC {
+
+ //
+ // format unit
+ //
+
+ struct _FORMAT_RBC {
+ UCHAR OperationCode;
+ UCHAR VendorSpecific;
+ UCHAR Increment : 1;
+ UCHAR Percent_Time : 1;
+ UCHAR Reserved1 : 1;
+ UCHAR VendorSpecific1 : 5;
+ UCHAR Reserved2[2];
+ UCHAR Control;
+ } FORMAT_RBC, *PFORMAT_RBC;
+
+ //
+ // prevent/allow medium removal
+ //
+
+ struct _MEDIA_REMOVAL_RBC {
+ UCHAR OperationCode;
+ UCHAR Reserved[3];
+
+ UCHAR Prevent : 1;
+ UCHAR Persistant : 1;
+ UCHAR Reserved3 : 6;
+
+ UCHAR Control;
+ } MEDIA_REMOVAL_RBC, *PMEDIA_REMOVAL_RBC;
+
+ //
+ // START_STOP_UNIT
+ //
+
+ struct _START_STOP_RBC {
+ UCHAR OperationCode;
+ UCHAR Immediate: 1;
+ UCHAR Reserved1 : 7;
+ UCHAR Reserved2[2];
+ UCHAR Start : 1;
+ UCHAR LoadEject : 1;
+ UCHAR Reserved3 : 2;
+ UCHAR PowerConditions : 4;
+ UCHAR Control;
+ } START_STOP_RBC, *PSTART_STOP_RBC;
+
+ struct _SYNCHRONIZE_CACHE_RBC {
+
+ UCHAR OperationCode; // 0x35
+ UCHAR Reserved[8];
+ UCHAR Control;
+
+ } SYNCHRONIZE_CACHE_RBC, *PSYNCHRONIZE_CACHE_RBC;
+
+
+} CDB_RBC, *PCDB_RBC;
+
+
+//
+// START_STOP_UNIT Power Condition descriptions
+//
+
+#define START_STOP_RBC_POWER_CND_NO_CHANGE 0
+#define START_STOP_RBC_POWER_CND_ACTIVE 1
+#define START_STOP_RBC_POWER_CND_IDLE 2
+#define START_STOP_RBC_POWER_CND_STANDBY 3
+#define START_STOP_RBC_POWER_CND_SLEEP 5
+#define START_STOP_RBC_POWER_CND_DEVICE_CTRL 7
+
+
+//
+// Mode Sense/Select page constants.
+//
+
+#define MODE_PAGE_RBC_DEVICE_PARAMETERS 0x06
+
+
+//
+// DeviceType field in inquiry Data
+//
+
+#define RBC_DEVICE 0x0E
+
+//
+// Define Device Capabilities page.
+//
+
+typedef struct _MODE_RBC_DEVICE_PARAMETERS_PAGE {
+ UCHAR PageCode : 6;
+ UCHAR Reserved : 1;
+ UCHAR PageSavable : 1;
+ UCHAR PageLength;
+ UCHAR WriteCacheDisable : 1;
+ UCHAR Reserved1 : 7;
+ UCHAR LogicalBlockSize[2];
+ UCHAR NumberOfLogicalBlocks[5];
+ UCHAR PowerPerformance;
+ UCHAR LockDisabled : 1;
+ UCHAR FormatDisabled : 1;
+ UCHAR WriteDisabled : 1;
+ UCHAR ReadDisabled : 1;
+ UCHAR Reserved2 : 4;
+ UCHAR Reserved3;
+
+}MODE_RBC_DEVICE_PARAMETERS_PAGE, *PMODE_RBC_DEVICE_PARAMETERS_PAGE;
+
+typedef struct _MODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE {
+
+ MODE_PARAMETER_HEADER Header;
+ MODE_RBC_DEVICE_PARAMETERS_PAGE Page;
+
+}MODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE,
+ *PMODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE;
+
+
+//
+// unsolicited status sense code qualifier values
+//
+
+#define RBC_UNSOLICITED_STATUS 0x02
+#define RBC_UNSOLICITED_SENSE_KEY 0x06
+
+#define RBC_UNSOLICITED_SC_PWR_STATE_CHNG 0xFF
+#define RBC_UNSOLICITED_SC_EVENT_STATUS 0xFE
+
+#define RBC_UNSOLICITED_CLASS_ASQ_DEVICE 0x06
+#define RBC_UNSOLICITED_CLASS_ASQ_MEDIA 0x04
+#define RBC_UNSOLICITED_CLASS_ASQ_POWER 0x02
+
+
+
+
+//
+// Translation routine used to convert SCSI requests that differ from RBC
+//
+
+NTSTATUS
+Rbc_Scsi_Conversion(
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN PSCSI_REQUEST_BLOCK *OriginalSrb,
+ IN PMODE_RBC_DEVICE_PARAMETERS_HEADER_AND_PAGE RbcHeaderAndPage,
+ IN BOOLEAN OutgoingRequest,
+ IN BOOLEAN RemovableMedia
+ );
+
+
+#endif
+
diff --git a/reactos/drivers/storage/inc/tape.h b/reactos/drivers/storage/inc/tape.h
new file mode 100644
index 00000000000..2f11917a24c
--- /dev/null
+++ b/reactos/drivers/storage/inc/tape.h
@@ -0,0 +1,312 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ tape.h
+
+Abstract:
+
+ These are the structures and defines that are used in the
+ SCSI tape class drivers. The tape class driver is separated
+ into two modules. Tape.c contains code common to all tape
+ class drivers including the driver's major entry points.
+ The major entry point names each begin with the prefix
+ 'ScsiTape.' The second module is the device specific code.
+ It provides support for a set of functions. Each device
+ specific function name is prefixed by 'Tape.'
+
+Author:
+
+ Mike Glass
+
+Revision History:
+
+--*/
+
+#include "scsi.h"
+#include "class.h"
+
+//
+// Define the maximum inquiry data length.
+//
+
+#define MAXIMUM_TAPE_INQUIRY_DATA 252
+
+//
+// Tape device data
+//
+
+typedef struct _TAPE_DATA {
+ ULONG Flags;
+ ULONG CurrentPartition;
+ PVOID DeviceSpecificExtension;
+ PSCSI_INQUIRY_DATA InquiryData;
+} TAPE_DATA, *PTAPE_DATA;
+
+#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION) + sizeof(TAPE_DATA)
+
+
+//
+// Define Device Configuration Page
+//
+
+typedef struct _MODE_DEVICE_CONFIGURATION_PAGE {
+
+ UCHAR PageCode : 6;
+ UCHAR Reserved1 : 1;
+ UCHAR PS : 1;
+ UCHAR PageLength;
+ UCHAR ActiveFormat : 5;
+ UCHAR CAFBit : 1;
+ UCHAR CAPBit : 1;
+ UCHAR Reserved2 : 1;
+ UCHAR ActivePartition;
+ UCHAR WriteBufferFullRatio;
+ UCHAR ReadBufferEmptyRatio;
+ UCHAR WriteDelayTime[2];
+ UCHAR REW : 1;
+ UCHAR RBO : 1;
+ UCHAR SOCF : 2;
+ UCHAR AVC : 1;
+ UCHAR RSmk : 1;
+ UCHAR BIS : 1;
+ UCHAR DBR : 1;
+ UCHAR GapSize;
+ UCHAR Reserved3 : 3;
+ UCHAR SEW : 1;
+ UCHAR EEG : 1;
+ UCHAR EODdefined : 3;
+ UCHAR BufferSize[3];
+ UCHAR DCAlgorithm;
+ UCHAR Reserved4;
+
+} MODE_DEVICE_CONFIGURATION_PAGE, *PMODE_DEVICE_CONFIGURATION_PAGE;
+
+//
+// Define Medium Partition Page
+//
+
+typedef struct _MODE_MEDIUM_PARTITION_PAGE {
+
+ UCHAR PageCode : 6;
+ UCHAR Reserved1 : 1;
+ UCHAR PSBit : 1;
+ UCHAR PageLength;
+ UCHAR MaximumAdditionalPartitions;
+ UCHAR AdditionalPartitionDefined;
+ UCHAR Reserved2 : 3;
+ UCHAR PSUMBit : 2;
+ UCHAR IDPBit : 1;
+ UCHAR SDPBit : 1;
+ UCHAR FDPBit : 1;
+ UCHAR MediumFormatRecognition;
+ UCHAR Reserved3[2];
+ UCHAR Partition0Size[2];
+ UCHAR Partition1Size[2];
+
+} MODE_MEDIUM_PARTITION_PAGE, *PMODE_MEDIUM_PARTITION_PAGE;
+
+//
+// Define Data Compression Page
+//
+
+typedef struct _MODE_DATA_COMPRESSION_PAGE {
+
+ UCHAR PageCode : 6;
+ UCHAR Reserved1 : 2;
+ UCHAR PageLength;
+ UCHAR Reserved2 : 6;
+ UCHAR DCC : 1;
+ UCHAR DCE : 1;
+ UCHAR Reserved3 : 5;
+ UCHAR RED : 2;
+ UCHAR DDE : 1;
+ UCHAR CompressionAlgorithm[4];
+ UCHAR DecompressionAlgorithm[4];
+ UCHAR Reserved4[4];
+
+} MODE_DATA_COMPRESSION_PAGE, *PMODE_DATA_COMPRESSION_PAGE;
+
+//
+// Mode parameter list header and medium partition page -
+// used in creating partitions
+//
+
+typedef struct _MODE_MEDIUM_PART_PAGE {
+
+ MODE_PARAMETER_HEADER ParameterListHeader;
+ MODE_MEDIUM_PARTITION_PAGE MediumPartPage;
+
+} MODE_MEDIUM_PART_PAGE, *PMODE_MEDIUM_PART_PAGE;
+
+
+//
+// Mode parameters for retrieving tape or media information
+//
+
+typedef struct _MODE_TAPE_MEDIA_INFORMATION {
+
+ MODE_PARAMETER_HEADER ParameterListHeader;
+ MODE_PARAMETER_BLOCK ParameterListBlock;
+ MODE_MEDIUM_PARTITION_PAGE MediumPartPage;
+
+} MODE_TAPE_MEDIA_INFORMATION, *PMODE_TAPE_MEDIA_INFORMATION;
+
+//
+// Mode parameter list header and device configuration page -
+// used in retrieving device configuration information
+//
+
+typedef struct _MODE_DEVICE_CONFIG_PAGE {
+
+ MODE_PARAMETER_HEADER ParameterListHeader;
+ MODE_DEVICE_CONFIGURATION_PAGE DeviceConfigPage;
+
+} MODE_DEVICE_CONFIG_PAGE, *PMODE_DEVICE_CONFIG_PAGE;
+
+
+//
+// Mode parameter list header and data compression page -
+// used in retrieving data compression information
+//
+
+typedef struct _MODE_DATA_COMPRESS_PAGE {
+
+ MODE_PARAMETER_HEADER ParameterListHeader;
+ MODE_DATA_COMPRESSION_PAGE DataCompressPage;
+
+} MODE_DATA_COMPRESS_PAGE, *PMODE_DATA_COMPRESS_PAGE;
+
+
+
+//
+// The following routines are the exported entry points for
+// all tape class drivers. Note all these routines name start
+// with 'ScsiTape.'
+//
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+NTSTATUS
+ScsiTapeInitialize(
+ IN PDRIVER_OBJECT DriverObject
+ );
+
+NTSTATUS
+ScsiTapeCreate (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ScsiTapeReadWrite (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ScsiTapeDeviceControl(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+
+
+//
+// The following routines are provided by the tape
+// device-specific module. Each routine name is
+// prefixed with 'Tape.'
+
+NTSTATUS
+TapeCreatePartition(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeErase(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+TapeError(
+ PDEVICE_OBJECT DeviceObject,
+ PSCSI_REQUEST_BLOCK Srb,
+ NTSTATUS *Status,
+ BOOLEAN *Retry
+ );
+
+NTSTATUS
+TapeGetDriveParameters(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeGetMediaParameters(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeGetPosition(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeGetStatus(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapePrepare(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeReadWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeSetDriveParameters(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeSetMediaParameters(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+TapeSetPosition(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+BOOLEAN
+TapeVerifyInquiry(
+ IN PSCSI_INQUIRY_DATA LunInfo
+ );
+
+NTSTATUS
+TapeWriteMarks(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+
+