|
|
/*--
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"
#include "mmc.tmh"
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 && ((ULONG)(ULONG_PTR)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(mmcData->NumDelayedIrps != 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 ) { LIST_ENTRY irpList; PLIST_ENTRY listEntry; KIRQL oldIrql;
// 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));
}
/*
* Get all the delayed IRPs into a private list first to avoid an infinite loop * where irps are added to the DelayedIrpsList while we are siphoning them off. */ InitializeListHead(&irpList); KeAcquireSpinLock(&MmcData->DelayedIrpsLock, &oldIrql); while (!IsListEmpty(&MmcData->DelayedIrpsList)){ listEntry = RemoveHeadList(&MmcData->DelayedIrpsList); InsertTailList(&irpList, listEntry); ASSERT(MmcData->NumDelayedIrps > 0); MmcData->NumDelayedIrps--; } ASSERT(MmcData->NumDelayedIrps == 0); KeReleaseSpinLock(&MmcData->DelayedIrpsLock, oldIrql); // 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(!IsListEmpty(&irpList));
//
// now either succeed or fail all the delayed irps, according
// to the update status.
//
while (!IsListEmpty(&irpList)){ PIRP irp; listEntry = RemoveHeadList(&irpList); irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry); 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); InitializeListHead(&mmcData->DelayedIrpsList); KeInitializeSpinLock(&mmcData->DelayedIrpsLock); mmcData->NumDelayedIrps = 0;
//
// 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;
}
|