windows-nt/Source/XPSP1/NT/drivers/storage/cdrom/mmc.c
2020-09-26 16:20:57 +08:00

1437 lines
44 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*--
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;
}