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 + ); + + +