reactos/drivers/storage/class/cdrom/cdrom.c

7360 lines
218 KiB
C

/*
* PROJECT: ReactOS Storage Stack
* LICENSE: DDK - see license.txt in the root dir
* FILE: drivers/storage/cdrom/cdrom.c
* PURPOSE: CDROM driver
* PROGRAMMERS: Based on a source code sample from Microsoft NT4 DDK
*/
#include "precomp.h"
#include <ntddk.h>
#include <scsi.h>
#include <ntdddisk.h>
#include <ntddcdrm.h>
#include <include/class2.h>
#include <stdio.h>
//#define NDEBUG
#include <debug.h>
#define CDB12GENERIC_LENGTH 12
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_DATA {
//
// Indicates whether an audio play operation
// is currently being performed.
//
BOOLEAN PlayActive;
//
// Indicates whether the blocksize used for user data
// is 2048 or 2352.
//
BOOLEAN RawAccess;
//
// Indicates whether 6 or 10 byte mode sense/select
// should be used.
//
USHORT XAFlags;
//
// Storage for the error recovery page. This is used
// as an easy method to switch block sizes.
//
union {
ERROR_RECOVERY_DATA u1;
ERROR_RECOVERY_DATA10 u2;
};
//
// Pointer to the original irp for the raw read.
//
PIRP SavedReadIrp;
//
// Used to protect accesses to the RawAccess flag.
//
KSPIN_LOCK FormSpinLock;
//
// Even if media change support is requested, there are some devices
// that are not supported. This flag will indicate that such a device
// is present when it is FALSE.
//
BOOLEAN MediaChangeSupported;
//
// The media change event is being supported. The media change timer
// should be running whenever this is true.
//
BOOLEAN MediaChange;
//
// 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.
//
UCHAR MediaChangeCountDown;
#if DBG
//
// 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
//
SHORT MediaChangeIrpTimeInUse;
//
// Set by CdRomTickHandler when we determine that the media change irp has
// been lost
//
BOOLEAN MediaChangeIrpLost;
#endif
UCHAR PadReserve; // use this for new flags.
//
// An IRP is allocated and kept for the duration that media change
// detection is in effect. If this is NULL and MediaChange is TRUE,
// the detection is in progress. This should always be NULL when
// MediaChange is FALSE.
//
PIRP MediaChangeIrp;
//
// The timer work list is a collection of IRPS that are prepared for
// submission, but need to allow some time to pass before they are
// run.
//
LIST_ENTRY TimerIrpList;
KSPIN_LOCK TimerIrpSpinLock;
} CDROM_DATA, *PCDROM_DATA;
#define DEVICE_EXTENSION_SIZE sizeof(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 MEDIA_CHANGE_DEFAULT_TIME 4
#define CDROM_SRB_LIST_SIZE 4
#if DBG
//
// Used to detect the loss of the autorun irp. The driver prints out a message
// (debug level 0) if this timeout ever occurs
//
#define MEDIA_CHANGE_TIMEOUT_TIME 300
#endif
#define PLAY_ACTIVE(DeviceExtension) (((PCDROM_DATA)(DeviceExtension + 1))->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_USE_READ_CD 0x04
#define XA_NOT_SUPPORTED 0x08
#define PLEXTOR_CDDA 0x10
#define NEC_CDDA 0x20
//
// 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
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'CscS')
#endif
NTSTATUS
NTAPI
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
BOOLEAN
NTAPI
ScsiCdRomFindDevices(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath,
IN PCLASS_INIT_DATA InitializationData,
IN PDEVICE_OBJECT PortDeviceObject,
IN ULONG PortNumber
);
NTSTATUS
NTAPI
ScsiCdRomOpenClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
NTAPI
ScsiCdRomReadVerification(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
NTSTATUS
NTAPI
ScsiCdRomSwitchMode(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN PIRP OriginalRequest
);
NTSTATUS
NTAPI
CdRomDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
IO_COMPLETION_ROUTINE CdRomDeviceControlCompletion;
NTSTATUS
NTAPI
CdRomDeviceControlCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
IO_COMPLETION_ROUTINE CdRomSetVolumeIntermediateCompletion;
NTSTATUS
NTAPI
CdRomSetVolumeIntermediateCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
IO_COMPLETION_ROUTINE CdRomSwitchModeCompletion;
NTSTATUS
NTAPI
CdRomSwitchModeCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
IO_COMPLETION_ROUTINE CdRomXACompletion;
NTSTATUS
NTAPI
CdRomXACompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
IO_COMPLETION_ROUTINE CdRomClassIoctlCompletion;
NTSTATUS
NTAPI
CdRomClassIoctlCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
);
VOID
NTAPI
ScsiCdRomStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
);
VOID
NTAPI
CdRomTickHandler(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
);
BOOLEAN
NTAPI
CdRomCheckRegistryForMediaChangeValue(
IN PUNICODE_STRING RegistryPath,
IN ULONG DeviceNumber
);
NTSTATUS
NTAPI
CdRomUpdateCapacity(
IN PDEVICE_EXTENSION DeviceExtension,
IN PIRP IrpToComplete,
IN OPTIONAL PKEVENT IoctlEvent
);
NTSTATUS
NTAPI
CreateCdRomDeviceObject(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PortDeviceObject,
IN ULONG PortNumber,
IN PULONG DeviceCount,
PIO_SCSI_CAPABILITIES PortCapabilities,
IN PSCSI_INQUIRY_DATA LunInfo,
IN PCLASS_INIT_DATA InitializationData,
IN PUNICODE_STRING RegistryPath
);
VOID
NTAPI
ScanForSpecial(
PDEVICE_OBJECT DeviceObject,
PINQUIRYDATA InquiryData,
PIO_SCSI_CAPABILITIES PortCapabilities
);
BOOLEAN
NTAPI
CdRomIsPlayActive(
IN PDEVICE_OBJECT DeviceObject
);
VOID
NTAPI
HitachProcessError(
PDEVICE_OBJECT DeviceObject,
PSCSI_REQUEST_BLOCK Srb,
NTSTATUS *Status,
BOOLEAN *Retry
);
IO_COMPLETION_ROUTINE ToshibaProcessErrorCompletion;
VOID
NTAPI
ToshibaProcessError(
PDEVICE_OBJECT DeviceObject,
PSCSI_REQUEST_BLOCK Srb,
NTSTATUS *Status,
BOOLEAN *Retry
);
BOOLEAN
NTAPI
IsThisAnAtapiChanger(
IN PDEVICE_OBJECT DeviceObject,
OUT PULONG DiscsPresent
);
BOOLEAN
NTAPI
IsThisASanyo(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR PathId,
IN UCHAR TargetId
);
BOOLEAN
NTAPI
IsThisAMultiLunDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PDEVICE_OBJECT PortDeviceObject
);
VOID
NTAPI
CdRomCreateNamedEvent(
IN PDEVICE_EXTENSION DeviceExtension,
IN ULONG DeviceNumber
);
#ifdef _PPC_
NTSTATUS
FindScsiAdapter (
IN HANDLE KeyHandle,
IN UNICODE_STRING ScsiUnicodeString[],
OUT PUCHAR IntermediateController
);
#endif
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, DriverEntry)
#pragma alloc_text(PAGE, ScsiCdRomFindDevices)
#pragma alloc_text(PAGE, CreateCdRomDeviceObject)
#pragma alloc_text(PAGE, ScanForSpecial)
//#pragma alloc_text(PAGE, CdRomDeviceControl)
#pragma alloc_text(PAGE, HitachProcessError)
#pragma alloc_text(PAGE, CdRomIsPlayActive)
#pragma alloc_text(PAGE, ScsiCdRomReadVerification)
#pragma alloc_text(INIT, CdRomCheckRegistryForMediaChangeValue)
#pragma alloc_text(INIT, IsThisAnAtapiChanger)
#pragma alloc_text(INIT, IsThisASanyo)
#pragma alloc_text(INIT, IsThisAMultiLunDevice)
#pragma alloc_text(INIT, CdRomCreateNamedEvent)
#ifdef _PPC_
#pragma alloc_text(PAGE, FindScsiAdapter)
#endif
#endif
ULONG NoLoad = 0;
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;
if(NoLoad) {
return STATUS_NO_SUCH_DEVICE;
}
//
// Zero InitData
//
RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
//
// Set sizes
//
InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
InitializationData.DeviceExtensionSize = DEVICE_EXTENSION_SIZE;
InitializationData.DeviceType = FILE_DEVICE_CD_ROM;
InitializationData.DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE;
//
// Set entry points
//
InitializationData.ClassReadWriteVerification = ScsiCdRomReadVerification;
InitializationData.ClassDeviceControl = CdRomDeviceControl;
InitializationData.ClassFindDevices = ScsiCdRomFindDevices;
InitializationData.ClassShutdownFlush = NULL;
InitializationData.ClassCreateClose = NULL;
InitializationData.ClassStartIo = ScsiCdRomStartIo;
//
// Call the class init routine
//
return ScsiClassInitialize( DriverObject, RegistryPath, &InitializationData);
} // end DriverEntry()
BOOLEAN
NTAPI
ScsiCdRomFindDevices(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath,
IN PCLASS_INIT_DATA InitializationData,
IN PDEVICE_OBJECT PortDeviceObject,
IN ULONG PortNumber
)
/*++
Routine Description:
Connect to SCSI port driver. Get adapter capabilities and
SCSI bus configuration information. Search inquiry data
for CDROM devices to process.
Arguments:
DriverObject - CDROM class driver object.
PortDeviceObject - SCSI port driver device object.
PortNumber - The system ordinal for this scsi adapter.
Return Value:
TRUE if CDROM device present on this SCSI adapter.
--*/
{
PIO_SCSI_CAPABILITIES portCapabilities;
PULONG cdRomCount;
PCHAR buffer;
PSCSI_INQUIRY_DATA lunInfo;
PSCSI_ADAPTER_BUS_INFO adapterInfo;
PINQUIRYDATA inquiryData;
ULONG scsiBus;
NTSTATUS status;
BOOLEAN foundDevice = FALSE;
//
// Call port driver to get adapter capabilities.
//
status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n"));
return foundDevice;
}
//
// Call port driver to get inquiry information to find cdroms.
//
status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n"));
return foundDevice;
}
//
// Get the address of the count of the number of cdroms already initialized.
//
cdRomCount = &IoGetConfigurationInformation()->CdRomCount;
adapterInfo = (PVOID) buffer;
//
// For each SCSI bus this adapter supports ...
//
for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) {
//
// Get the SCSI bus scan data for this bus.
//
lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
//
// Search list for unclaimed disk devices.
//
while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
inquiryData = (PVOID)lunInfo->InquiryData;
if ((inquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE) &&
(inquiryData->DeviceTypeQualifier == 0) &&
(!lunInfo->DeviceClaimed)) {
DebugPrint((1,"FindScsiDevices: Vendor string is %.24s\n",
inquiryData->VendorId));
//
// Create device objects for cdrom
//
status = CreateCdRomDeviceObject(DriverObject,
PortDeviceObject,
PortNumber,
cdRomCount,
portCapabilities,
lunInfo,
InitializationData,
RegistryPath);
if (NT_SUCCESS(status)) {
//
// Increment system cdrom device count.
//
(*cdRomCount)++;
//
// Indicate that a cdrom device was found.
//
foundDevice = TRUE;
}
}
//
// Get next LunInfo.
//
if (lunInfo->NextInquiryDataOffset == 0) {
break;
}
lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
}
}
ExFreePool(buffer);
return foundDevice;
} // end FindScsiCdRoms()
VOID
NTAPI
CdRomCreateNamedEvent(
IN PDEVICE_EXTENSION DeviceExtension,
IN ULONG DeviceNumber
)
/*++
Routine Description:
Create the named synchronization event for notification of media change
events to the system. The event is reset before this function returns.
Arguments:
DeviceExtension - the device extension pointer for storage of the event pointer.
Return Value:
None.
--*/
{
UNICODE_STRING unicodeString;
OBJECT_ATTRIBUTES objectAttributes;
CCHAR eventNameBuffer[MAXIMUM_FILENAME_LENGTH];
STRING eventNameString;
HANDLE handle;
NTSTATUS status;
sprintf(eventNameBuffer,"\\Device\\MediaChangeEvent%ld",
DeviceNumber);
RtlInitString(&eventNameString,
eventNameBuffer);
status = RtlAnsiStringToUnicodeString(&unicodeString,
&eventNameString,
TRUE);
if (!NT_SUCCESS(status)) {
return;
}
InitializeObjectAttributes(&objectAttributes,
&unicodeString,
OBJ_PERMANENT | OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
NULL,
NULL);
DeviceExtension->MediaChangeEvent = IoCreateSynchronizationEvent(&unicodeString,
&handle);
DeviceExtension->MediaChangeEventHandle = handle;
KeClearEvent(DeviceExtension->MediaChangeEvent);
RtlFreeUnicodeString(&unicodeString);
}
VOID
NTAPI
ReportToMountMgr(
IN PDEVICE_OBJECT CdDeviceObject
)
/*++
Routine Description:
This routine reports the creation of a cdrom device object to the
MountMgr to fake PnP.
Arguments:
CdDeviceObject - Pointer to the created cdrom device.
Return Value:
VOID
--*/
{
NTSTATUS status;
UNICODE_STRING mountMgrDevice;
PDEVICE_OBJECT deviceObject;
PFILE_OBJECT fileObject;
PMOUNTMGR_TARGET_NAME mountTarget;
ULONG cdLen;
PDEVICE_EXTENSION deviceExtension;
PIRP irp;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
//
// First, get MountMgr DeviceObject.
//
RtlInitUnicodeString(&mountMgrDevice, MOUNTMGR_DEVICE_NAME);
status = IoGetDeviceObjectPointer(&mountMgrDevice, FILE_READ_ATTRIBUTES,
&fileObject, &deviceObject);
if (!NT_SUCCESS(status)) {
DebugPrint((1,
"ReportToMountMgr: Can't get MountMgr pointers %lx\n",
status));
return;
}
deviceExtension = CdDeviceObject->DeviceExtension;
cdLen = deviceExtension->DeviceName.Length;
//
// Allocate input buffer to report our partition device.
//
mountTarget = ExAllocatePool(NonPagedPool,
sizeof(MOUNTMGR_TARGET_NAME) + cdLen);
if (!mountTarget) {
DebugPrint((1,
"ReportToMountMgr: Allocation of mountTarget failed\n"));
ObDereferenceObject(fileObject);
return;
}
mountTarget->DeviceNameLength = cdLen;
RtlCopyMemory(mountTarget->DeviceName, deviceExtension->DeviceName.Buffer, cdLen);
KeInitializeEvent(&event, NotificationEvent, FALSE);
//
// Build the IRP used to communicate with the MountMgr.
//
irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION,
deviceObject,
mountTarget,
sizeof(MOUNTMGR_TARGET_NAME) + cdLen,
NULL,
0,
FALSE,
&event,
&ioStatus);
if (!irp) {
DebugPrint((1,
"ReportToMountMgr: Allocation of irp failed\n"));
ExFreePool(mountTarget);
ObDereferenceObject(fileObject);
return;
}
//
// Call the MountMgr.
//
status = IoCallDriver(deviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
//
// We're done.
//
DPRINT1("Reported to the MountMgr: %lx\n", status);
ExFreePool(mountTarget);
ObDereferenceObject(fileObject);
return;
}
NTSTATUS
NTAPI
CreateCdRomDeviceObject(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PortDeviceObject,
IN ULONG PortNumber,
IN PULONG DeviceCount,
IN PIO_SCSI_CAPABILITIES PortCapabilities,
IN PSCSI_INQUIRY_DATA LunInfo,
IN PCLASS_INIT_DATA InitializationData,
IN PUNICODE_STRING RegistryPath
)
/*++
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 capabilities (and limitations).
LunInfo - Pointer to configuration information for this device.
Return Value:
NTSTATUS
--*/
{
CHAR ntNameBuffer[64];
NTSTATUS status;
BOOLEAN changerDevice;
SCSI_REQUEST_BLOCK srb;
ULONG length;
PCDROM_DATA cddata;
PCDB cdb;
PVOID senseData = NULL;
PDEVICE_OBJECT deviceObject = NULL;
PDEVICE_EXTENSION deviceExtension = NULL;
PUCHAR buffer;
ULONG bps;
ULONG lastBit;
ULONG timeOut;
BOOLEAN srbListInitialized = FALSE;
//
// Claim the device. Note that any errors after this
// will goto the generic handler, where the device will
// be released.
//
status = ScsiClassClaimDevice(PortDeviceObject,
LunInfo,
FALSE,
&PortDeviceObject);
if (!NT_SUCCESS(status)) {
return(status);
}
//
// Create device object for this device.
//
sprintf(ntNameBuffer,
"\\Device\\CdRom%lu",
*DeviceCount);
status = ScsiClassCreateDeviceObject(DriverObject,
ntNameBuffer,
NULL,
&deviceObject,
InitializationData);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"CreateCdRomDeviceObjects: Can not create device %s\n",
ntNameBuffer));
goto CreateCdRomDeviceObjectExit;
}
//
// Indicate that IRPs should include MDLs.
//
deviceObject->Flags |= DO_DIRECT_IO;
//
// Set up required stack size in device object.
//
deviceObject->StackSize = PortDeviceObject->StackSize + 2;
deviceExtension = deviceObject->DeviceExtension;
//
// Allocate spinlock for split request completion.
//
KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
//
// This is the physical device.
//
deviceExtension->PhysicalDevice = deviceObject;
//
// Initialize lock count to zero. The lock count is used to
// disable the ejection mechanism when media is mounted.
//
deviceExtension->LockCount = 0;
//
// Save system cdrom number
//
deviceExtension->DeviceNumber = *DeviceCount;
//
// Copy port device object to device extension.
//
deviceExtension->PortDeviceObject = PortDeviceObject;
//
// Set the alignment requirements for the device based on the
// host adapter requirements
//
if (PortDeviceObject->AlignmentRequirement > deviceObject->AlignmentRequirement) {
deviceObject->AlignmentRequirement = PortDeviceObject->AlignmentRequirement;
}
//
// Save address of port driver capabilities.
//
deviceExtension->PortCapabilities = PortCapabilities;
//
// Clear SRB flags.
//
deviceExtension->SrbFlags = 0;
deviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
//
// Allocate request sense buffer.
//
senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
if (senseData == NULL) {
//
// The buffer cannot be allocated.
//
status = STATUS_INSUFFICIENT_RESOURCES;
goto CreateCdRomDeviceObjectExit;
}
//
// Set the sense data pointer in the device extension.
//
deviceExtension->SenseData = senseData;
//
// CDROMs are not partitionable so starting offset is 0.
//
deviceExtension->StartingOffset.LowPart = 0;
deviceExtension->StartingOffset.HighPart = 0;
//
// Path/TargetId/LUN describes a device location on the SCSI bus.
// This information comes from the LunInfo buffer.
//
deviceExtension->PortNumber = (UCHAR)PortNumber;
deviceExtension->PathId = LunInfo->PathId;
deviceExtension->TargetId = LunInfo->TargetId;
deviceExtension->Lun = LunInfo->Lun;
//
// Set timeout value in seconds.
//
timeOut = ScsiClassQueryTimeOutRegistryValue(RegistryPath);
if (timeOut) {
deviceExtension->TimeOutValue = timeOut;
} else {
deviceExtension->TimeOutValue = SCSI_CDROM_TIMEOUT;
}
//
// Build the lookaside list for srb's for the physical disk. Should only
// need a couple.
//
ScsiClassInitializeSrbLookasideList(deviceExtension,
CDROM_SRB_LIST_SIZE);
srbListInitialized = TRUE;
//
// Back pointer to device object.
//
deviceExtension->DeviceObject = deviceObject;
//
// Allocate buffer for drive geometry.
//
deviceExtension->DiskGeometry =
ExAllocatePool(NonPagedPool, sizeof(DISK_GEOMETRY_EX));
if (deviceExtension->DiskGeometry == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto CreateCdRomDeviceObjectExit;
}
//
// Set up media change support defaults.
//
cddata = (PCDROM_DATA)(deviceExtension + 1);
KeInitializeSpinLock(&cddata->FormSpinLock);
KeInitializeSpinLock(&cddata->TimerIrpSpinLock);
InitializeListHead(&cddata->TimerIrpList);
cddata->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
cddata->MediaChangeSupported = FALSE;
cddata->MediaChange = FALSE;
//
// Assume that there is initially no media in the device
// only notify upper layers if there is something there
//
deviceExtension->MediaChangeNoMedia = TRUE;
cddata->MediaChangeIrp = NULL;
#if DBG
cddata->MediaChangeIrpTimeInUse = 0;
cddata->MediaChangeIrpLost = FALSE;
#endif
//
// Scan for Scsi controllers that require special processing.
//
ScanForSpecial(deviceObject,
(PINQUIRYDATA) LunInfo->InquiryData,
PortCapabilities);
//
// 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.
//
status = ScsiClassReadDriveCapacity(deviceObject);
bps = deviceExtension->DiskGeometry->Geometry.BytesPerSector;
if (!NT_SUCCESS(status) || !bps) {
DebugPrint((1,
"CreateCdRomDeviceObjects: Can't read capacity for device %s\n",
ntNameBuffer));
//
// Set disk geometry to default values (per ISO 9660).
//
bps = 2048;
deviceExtension->SectorShift = 11;
deviceExtension->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;
}
deviceExtension->DiskGeometry->Geometry.BytesPerSector = bps;
DebugPrint((2, "CreateCdRomDeviceObject: Calc'd bps = %x\n", bps));
//
// Check to see if this is some sort of changer device
//
changerDevice = FALSE;
//
// Search for devices that have special requirements for media
// change support.
//
if (deviceExtension->Lun > 0) {
changerDevice = TRUE;
}
if (!changerDevice) {
changerDevice = IsThisASanyo(deviceObject, deviceExtension->PathId,
deviceExtension->TargetId);
}
if (!changerDevice) {
ULONG tmp;
changerDevice = IsThisAnAtapiChanger(deviceObject, &tmp);
}
if (!changerDevice) {
changerDevice = IsThisAMultiLunDevice(deviceObject, PortDeviceObject);
}
//
// If it is a changer device, increment the timeout to take platter-swapping
// time into account
//
if(changerDevice) {
deviceExtension->TimeOutValue += SCSI_CHANGER_BONUS_TIMEOUT;
}
//
// Create the media change named event. If this succeeds then continue
// initializing the media change support data items.
//
CdRomCreateNamedEvent(deviceExtension,*DeviceCount);
if (deviceExtension->MediaChangeEvent) {
//
// If this is not a changer, get an IRP for the timer request
// and initialize the timer.
//
if (!changerDevice) {
//
// Not a changer device - continue with media change initialization.
// Determine if the user actually wants media change events.
//
if (CdRomCheckRegistryForMediaChangeValue(RegistryPath, *DeviceCount)) {
PIO_STACK_LOCATION irpStack;
PSCSI_REQUEST_BLOCK srb;
PIRP irp;
//
// User wants it - preallocate IRP and SRB.
//
irp = IoAllocateIrp((CCHAR)(deviceObject->StackSize+1),
FALSE);
if (irp) {
PVOID buffer;
srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
buffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
if (srb && buffer) {
PCDB cdb;
//
// All resources have been allocated set up the IRP.
//
IoSetNextIrpStackLocation(irp);
irpStack = IoGetCurrentIrpStackLocation(irp);
irpStack->DeviceObject = deviceObject;
irpStack = IoGetNextIrpStackLocation(irp);
cddata->MediaChangeIrp = irp;
irpStack->Parameters.Scsi.Srb = srb;
//
// Initialize the SRB
//
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
srb->CdbLength = 6;
srb->TimeOutValue = deviceExtension->TimeOutValue * 2;
srb->QueueTag = SP_UNTAGGED;
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->PathId = deviceExtension->PathId;
srb->TargetId = deviceExtension->TargetId;
srb->Lun = deviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
//
// Initialize and set up the sense information buffer
//
RtlZeroMemory(buffer, SENSE_BUFFER_SIZE);
srb->SenseInfoBuffer = buffer;
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
//
// Initialize the CDB
//
cdb = (PCDB)&srb->Cdb[0];
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
cdb->CDB6GENERIC.LogicalUnitNumber = deviceExtension->Lun;
//
// It is ok to support media change events on this device.
//
cddata->MediaChangeSupported = TRUE;
cddata->MediaChange = TRUE;
} else {
if (srb) {
ExFreePool(srb);
}
if (buffer) {
ExFreePool(buffer);
}
IoFreeIrp(irp);
}
}
} else {
deviceExtension->MediaChangeEvent = NULL;
}
} else {
deviceExtension->MediaChangeEvent = NULL;
}
}
//
// Assume use of 6-byte mode sense/select for now.
//
cddata->XAFlags |= XA_USE_6_BYTE;
//
// Build and issue mode sense with Read error recovery page. This will be used to change
// block size in case of any raw reads (Mode 2, Form 2).
//
length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH);
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
srb.CdbLength = 6;
cdb = (PCDB)srb.Cdb;
//
// Set timeout value from device extension.
//
srb.TimeOutValue = deviceExtension->TimeOutValue;
//
// Build the MODE SENSE CDB. The data returned will be kept in the device extension
// and used to set block size.
//
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
cdb->MODE_SENSE.PageCode = 0x1;
cdb->MODE_SENSE.AllocationLength = (UCHAR)length;
buffer = ExAllocatePool(NonPagedPoolCacheAligned, (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH10));
if (!buffer) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto CreateCdRomDeviceObjectExit;
}
status = ScsiClassSendSrbSynchronous(deviceObject,
&srb,
buffer,
length,
FALSE);
if (!NT_SUCCESS(status)) {
//
// May be Atapi, try 10-byte.
//
length = (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH10);
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
//
// Build the MODE SENSE CDB.
//
srb.CdbLength = 10;
cdb = (PCDB)srb.Cdb;
//
// Set timeout value from device extension.
//
srb.TimeOutValue = deviceExtension->TimeOutValue;
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
cdb->MODE_SENSE10.PageCode = 0x1;
cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR)(length >> 8);
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(length & 0xFF);
status = ScsiClassSendSrbSynchronous(deviceObject,
&srb,
buffer,
length,
FALSE);
if (status == STATUS_DATA_OVERRUN) {
//
// Build and issue the ReadCd command to ensure that this device supports it.
//
RtlZeroMemory(cdb, 12);
cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
status = ScsiClassSendSrbSynchronous(deviceObject,
&srb,
NULL,
0,
FALSE);
//
// If the command wasn't rejected then support the READ_CD.
//
if (NT_SUCCESS(status) || (status == STATUS_NO_MEDIA_IN_DEVICE)) {
//
// Using Read CD precludes issuing a mode select to
// set the user data size. So, no buffer copy is
// necessary.
//
cddata->XAFlags &= ~XA_USE_6_BYTE;
cddata->XAFlags |= XA_USE_READ_CD | XA_USE_10_BYTE;
} else {
RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA10));
cddata->u1.Header.ModeDataLength = 0;
cddata->XAFlags &= ~XA_USE_6_BYTE;
cddata->XAFlags |= XA_USE_10_BYTE;
}
} else if (NT_SUCCESS(status)) {
RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA10));
cddata->u1.Header.ModeDataLength = 0;
cddata->XAFlags &= ~XA_USE_6_BYTE;
cddata->XAFlags |= XA_USE_10_BYTE;
} else {
cddata->XAFlags |= XA_NOT_SUPPORTED;
}
} else {
RtlCopyMemory(&cddata->u1.Header, buffer, sizeof(ERROR_RECOVERY_DATA));
cddata->u1.Header.ModeDataLength = 0;
}
ExFreePool(buffer);
ReportToMountMgr(deviceObject);
//
// Start the timer now regardless of if Autorun is enabled.
// The timer must run forever since IoStopTimer faults.
//
IoInitializeTimer(deviceObject, CdRomTickHandler, NULL);
IoStartTimer(deviceObject);
return(STATUS_SUCCESS);
CreateCdRomDeviceObjectExit:
//
// Release the device since an error occured.
//
ScsiClassClaimDevice(PortDeviceObject,
LunInfo,
TRUE,
NULL);
if (senseData != NULL) {
ExFreePool(senseData);
}
if (deviceExtension->DiskGeometry != NULL) {
ExFreePool(deviceExtension->DiskGeometry);
}
if (deviceObject != NULL) {
if (srbListInitialized) {
ExDeleteNPagedLookasideList(&deviceExtension->SrbLookasideListHead);
}
IoDeleteDevice(deviceObject);
}
return status;
} // end CreateCdRomDeviceObject()
VOID
NTAPI
ScsiCdRomStartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->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;
ULONG maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength;
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);
//
// If the flag is set in the device object, force a verify.
//
if (DeviceObject->Flags & DO_VERIFY_VOLUME) {
DebugPrint((2, "ScsiCdRomStartIo: [%lx] Volume needs verified\n", Irp));
if (!(currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME)) {
if (Irp->Tail.Overlay.Thread) {
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
}
Irp->IoStatus.Status = STATUS_VERIFY_REQUIRED;
DebugPrint((2, "ScsiCdRomStartIo: [%lx] Calling UpdateCapacity - "
"ioctl event = %lx\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(deviceExtension,
Irp,
nextIrpStack->Parameters.Others.Argument1
);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] UpdateCapacity returned %lx\n", Irp, status));
ASSERT(status == STATUS_PENDING);
return;
}
}
cdData = (PCDROM_DATA)(deviceExtension + 1);
use6Byte = cdData->XAFlags & XA_USE_6_BYTE;
if (currentIrpStack->MajorFunction == IRP_MJ_READ) {
//
// Add partition byte offset to make starting byte relative to
// beginning of disk. In addition, add in skew for DM Driver, if any.
//
currentIrpStack->Parameters.Read.ByteOffset.QuadPart += (deviceExtension->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) {
ASSERT(!(cdData->XAFlags & XA_USE_READ_CD));
//
// Fire off a mode select to switch back to cooked sectors.
//
irp2 = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1),
FALSE);
if (!irp2) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
if (!srb) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
cdb = (PCDB)srb->Cdb;
//
// Allocate sense buffer.
//
senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
if (!senseBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
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 = deviceExtension->DeviceObject;
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->PathId = deviceExtension->PathId;
srb->TargetId = deviceExtension->TargetId;
srb->Lun = deviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->Cdb[1] |= deviceExtension->Lun << 5;
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 = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount );
if (!dataBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
transferByteCount,
FALSE,
FALSE,
(PIRP) NULL);
if (!irp2->MdlAddress) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
//
// Prepare the MDL
//
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
srb->DataBuffer = dataBuffer;
//
// Set the new block size in the descriptor.
//
cdData->u1.BlockDescriptor.BlockLength[0] = (UCHAR)(COOKED_SECTOR_SIZE >> 16) & 0xFF;
cdData->u1.BlockDescriptor.BlockLength[1] = (UCHAR)(COOKED_SECTOR_SIZE >> 8) & 0xFF;
cdData->u1.BlockDescriptor.BlockLength[2] = (UCHAR)(COOKED_SECTOR_SIZE & 0xFF);
//
// Move error page into dataBuffer.
//
RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, transferByteCount);
//
// Build and send a mode select to switch into raw mode.
//
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT);
srb->DataTransferLength = transferByteCount;
srb->TimeOutValue = deviceExtension->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(deviceExtension->PortDeviceObject, irp2);
return;
}
if ((currentIrpStack->Parameters.Read.Length > maximumTransferLength) ||
(transferPages >
deviceExtension->PortCapabilities->MaximumPhysicalPages)) {
//
// 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 =
deviceExtension->PortCapabilities->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;
}
ScsiClassSplitRequest(DeviceObject, Irp, maximumTransferLength);
return;
} else {
//
// Build SRB and CDB for this IRP.
//
ScsiClassBuildRequest(DeviceObject, Irp);
}
} else if (currentIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
//
// Allocate an irp, srb and associated structures.
//
irp2 = IoAllocateIrp((CCHAR)(deviceExtension->DeviceObject->StackSize+1),
FALSE);
if (!irp2) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
return;
}
srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
if (!srb) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
return;
}
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
cdb = (PCDB)srb->Cdb;
//
// Allocate sense buffer.
//
senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
if (!senseBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
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 = deviceExtension->DeviceObject;
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;
IoSetCompletionRoutine(irp2,
CdRomDeviceControlCompletion,
srb,
TRUE,
TRUE,
TRUE);
//
// Setup those fields that are generic to all requests.
//
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->PathId = deviceExtension->PathId;
srb->TargetId = deviceExtension->TargetId;
srb->Lun = deviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->Cdb[1] |= deviceExtension->Lun << 5;
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 (!(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;
//
// Free the recently allocated irp, as we don't need it.
//
IoFreeIrp(irp2);
cdb = (PCDB)srb->Cdb;
RtlZeroMemory(cdb, 12);
//
// Calculate starting offset.
//
startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift);
transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
maximumTransferLength = deviceExtension->PortCapabilities->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 > deviceExtension->PortCapabilities->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.
//
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
srb->OriginalRequest = Irp;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
srb->DataTransferLength = transferByteCount;
srb->TimeOutValue = deviceExtension->TimeOutValue;
srb->CdbLength = 10;
srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
if (rawReadInfo->TrackMode == CDDA) {
if (cdData->XAFlags & PLEXTOR_CDDA) {
srb->CdbLength = 12;
cdb->PLXTR_READ_CDDA.LogicalUnitNumber = deviceExtension->Lun;
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 (cdData->XAFlags & 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.LogicalUnitNumber = deviceExtension->Lun;
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;
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;
}
//
// Set up IoCompletion routine address.
//
IoSetCompletionRoutine(Irp,
CdRomXACompletion,
srb,
TRUE,
TRUE,
TRUE);
IoCallDriver(deviceExtension->PortDeviceObject, Irp);
return;
} else {
transferByteCount = (use6Byte) ? sizeof(ERROR_RECOVERY_DATA) : sizeof(ERROR_RECOVERY_DATA10);
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount );
if (!dataBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
transferByteCount,
FALSE,
FALSE,
(PIRP) NULL);
if (!irp2->MdlAddress) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
//
// Prepare the MDL
//
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
srb->DataBuffer = dataBuffer;
//
// Set the new block size in the descriptor.
//
cdData->u1.BlockDescriptor.BlockLength[0] = (UCHAR)(RAW_SECTOR_SIZE >> 16) & 0xFF;
cdData->u1.BlockDescriptor.BlockLength[1] = (UCHAR)(RAW_SECTOR_SIZE >> 8) & 0xFF;
cdData->u1.BlockDescriptor.BlockLength[2] = (UCHAR)(RAW_SECTOR_SIZE & 0xFF);
//
// TODO: Set density code, based on operation
//
cdData->u1.BlockDescriptor.DensityCode = 0;
//
// Move error page into dataBuffer.
//
RtlCopyMemory(srb->DataBuffer, &cdData->u1.Header, transferByteCount);
//
// Build and send a mode select to switch into raw mode.
//
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_OUT);
srb->DataTransferLength = transferByteCount;
srb->TimeOutValue = deviceExtension->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, 12);
//
// Calculate starting offset.
//
startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift);
transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
srb->OriginalRequest = Irp;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
srb->DataTransferLength = transferByteCount;
srb->TimeOutValue = deviceExtension->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:
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoStartNextPacket(DeviceObject, FALSE);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
return;
}
cdb->READ_CD.OperationCode = SCSIOP_READ_CD;
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
nextIrpStack->Parameters.Scsi.Srb = srb;
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;
}
//
// Set up IoCompletion routine address.
//
IoSetCompletionRoutine(Irp,
CdRomXACompletion,
srb,
TRUE,
TRUE,
TRUE);
IoCallDriver(deviceExtension->PortDeviceObject, Irp);
return;
}
IoCallDriver(deviceExtension->PortDeviceObject, irp2);
return;
}
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.
//
DebugPrint((3,
"CdRomStartIo: Get drive capacity\n"));
//
// setup remaining srb and cdb parameters.
//
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
srb->CdbLength = 10;
srb->TimeOutValue = deviceExtension->TimeOutValue;
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(READ_CAPACITY_DATA));
if (!dataBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
return;
}
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
sizeof(READ_CAPACITY_DATA),
FALSE,
FALSE,
(PIRP) NULL);
if (!irp2->MdlAddress) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] bailing with status %lx at line %s\n", Irp, Irp->IoStatus.Status, __LINE__));
return;
}
//
// Prepare the MDL
//
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
srb->DataBuffer = dataBuffer;
cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
IoCallDriver(deviceExtension->PortDeviceObject, irp2);
return;
}
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.
//
cdData->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
//
// Set up the SRB/CDB
//
srb->CdbLength = 6;
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
srb->TimeOutValue = deviceExtension->TimeOutValue * 2;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
DebugPrint((2, "ScsiCdRomStartIo: [%lx] Sending CHECK_VERIFY irp %lx\n", Irp, irp2));
IoCallDriver(deviceExtension->PortDeviceObject, irp2);
return;
}
case IOCTL_CDROM_GET_LAST_SESSION:
//
// Set format to return first and last session numbers.
//
cdb->READ_TOC.Format = GET_LAST_SESSION;
//
// Fall through to READ TOC code.
//
case IOCTL_CDROM_READ_TOC: {
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) {
//
// Use MSF addressing if not request for session information.
//
cdb->READ_TOC.Msf = CDB_USE_MSF;
}
//
// Set size of TOC structure.
//
transferByteCount =
currentIrpStack->Parameters.Read.Length >
sizeof(CDROM_TOC) ? sizeof(CDROM_TOC):
currentIrpStack->Parameters.Read.Length;
cdb->READ_TOC.AllocationLength[0] = (UCHAR) (transferByteCount >> 8);
cdb->READ_TOC.AllocationLength[1] = (UCHAR) (transferByteCount & 0xFF);
cdb->READ_TOC.Control = 0;
//
// Start at beginning of disc.
//
cdb->READ_TOC.StartingTrack = 0;
//
// setup remaining srb and cdb parameters.
//
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
srb->DataTransferLength = transferByteCount;
srb->CdbLength = 10;
srb->TimeOutValue = deviceExtension->TimeOutValue;
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, transferByteCount);
if (!dataBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
transferByteCount,
FALSE,
FALSE,
(PIRP) NULL);
if (!irp2->MdlAddress) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
//
// Prepare the MDL
//
MmBuildMdlForNonPagedPool(irp2->MdlAddress);
srb->DataBuffer = dataBuffer;
cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
IoCallDriver(deviceExtension->PortDeviceObject, 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 = deviceExtension->TimeOutValue;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
IoCallDriver(deviceExtension->PortDeviceObject, irp2);
return;
}
case IOCTL_CDROM_READ_Q_CHANNEL: {
PCDROM_SUB_Q_DATA_FORMAT inputBuffer =
Irp->AssociatedIrp.SystemBuffer;
//
// Allocate buffer for subq channel information.
//
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
sizeof(SUB_Q_CHANNEL_DATA));
if (!dataBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
transferByteCount,
FALSE,
FALSE,
(PIRP) NULL);
if (!irp2->MdlAddress) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
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 information 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 = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
srb->DataTransferLength = transferByteCount;
srb->CdbLength = 10;
srb->TimeOutValue = deviceExtension->TimeOutValue;
IoCallDriver(deviceExtension->PortDeviceObject, 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 = deviceExtension->TimeOutValue;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
IoCallDriver(deviceExtension->PortDeviceObject, 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 = deviceExtension->TimeOutValue;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
IoCallDriver(deviceExtension->PortDeviceObject, 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 = deviceExtension->TimeOutValue;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
IoCallDriver(deviceExtension->PortDeviceObject, 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 = deviceExtension->TimeOutValue;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_NO_DATA_TRANSFER);
IoCallDriver(deviceExtension->PortDeviceObject, irp2);
return;
}
case IOCTL_CDROM_GET_CONTROL: {
//
// Allocate buffer for volume control information.
//
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
MODE_DATA_SIZE);
if (!dataBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
MODE_DATA_SIZE,
FALSE,
FALSE,
(PIRP) NULL);
if (!irp2->MdlAddress) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
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 = deviceExtension->TimeOutValue;
srb->DataTransferLength = MODE_DATA_SIZE;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
IoCallDriver(deviceExtension->PortDeviceObject, irp2);
return;
}
case IOCTL_CDROM_GET_VOLUME:
case IOCTL_CDROM_SET_VOLUME: {
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
MODE_DATA_SIZE);
if (!dataBuffer) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
return;
}
irp2->MdlAddress = IoAllocateMdl(dataBuffer,
MODE_DATA_SIZE,
FALSE,
FALSE,
(PIRP) NULL);
if (!irp2->MdlAddress) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(irp2);
IoStartNextPacket(DeviceObject, FALSE);
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 = deviceExtension->TimeOutValue;
srb->DataTransferLength = MODE_DATA_SIZE;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | 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(deviceExtension->PortDeviceObject, irp2);
return;
}
default:
//
// Just complete the request - CdRomClassIoctlCompletion will take
// care of it for us
//
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ExFreePool(senseBuffer);
ExFreePool(srb);
IoFreeIrp(irp2);
return;
} // end switch()
}
//
// 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(deviceExtension->PortDeviceObject, Irp);
return;
}
NTSTATUS
NTAPI
ScsiCdRomReadVerification(
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
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
//
// If the cd is playing music then reject this request.
//
if (PLAY_ACTIVE(deviceExtension)) {
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 (!deviceExtension->DiskGeometry->Geometry.BytesPerSector) {
deviceExtension->DiskGeometry->Geometry.BytesPerSector = 2048;
}
if ((startingOffset.QuadPart > deviceExtension->PartitionLength.QuadPart) ||
(transferByteCount & (deviceExtension->DiskGeometry->Geometry.BytesPerSector - 1))) {
DebugPrint((1,"ScsiCdRomRead: Invalid I/O parameters\n"));
DebugPrint((1, "\toffset %x:%x, Length %x:%x\n",
startingOffset.u.HighPart,
startingOffset.u.LowPart,
deviceExtension->PartitionLength.u.HighPart,
deviceExtension->PartitionLength.u.LowPart));
DebugPrint((1, "\tbps %x\n", deviceExtension->DiskGeometry->Geometry.BytesPerSector));
//
// Fail request with status of invalid parameters.
//
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
} // end ScsiCdRomReadVerification()
NTSTATUS
NTAPI
CdRomDeviceControlCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PDEVICE_EXTENSION physicalExtension = deviceExtension->PhysicalDevice->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension + 1);
BOOLEAN use6Byte = 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) {
DebugPrint((2,
"CdRomDeviceControlCompletion: Irp %lx, Srb %lx Real Irp %lx Status %lx\n",
Irp,
srb,
realIrp,
srb->SrbStatus));
//
// Release the queue if it is frozen.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
DebugPrint((2, "CdRomDeviceControlCompletion: Releasing Queue\n"));
ScsiClassReleaseQueue(DeviceObject);
}
retry = ScsiClassInterpretSenseInfo(DeviceObject,
srb,
irpStack->MajorFunction,
irpStack->Parameters.DeviceIoControl.IoControlCode,
MAXIMUM_RETRIES - ((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1),
&status);
DebugPrint((2, "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_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(deviceExtension) = 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) {
if (((realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) ||
(realIrpStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL)) &&
(realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY)) {
ExFreePool(srb->SenseInfoBuffer);
if (srb->DataBuffer) {
ExFreePool(srb->DataBuffer);
}
ExFreePool(srb);
if (Irp->MdlAddress) {
IoFreeMdl(Irp->MdlAddress);
}
IoFreeIrp(Irp);
//
// 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.
//
status = CdRomUpdateCapacity(deviceExtension,realIrp, NULL);
DebugPrint((2, "CdRomDeviceControlCompletion: [%lx] CdRomUpdateCapacity completed with status %lx\n", realIrp, status));
ASSERT(status == STATUS_PENDING);
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
status = STATUS_IO_DEVICE_ERROR;
retry = TRUE;
}
}
if (retry && (realIrpNextStack->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1-1))) {
if (((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) {
//
// Retry request.
//
DebugPrint((1, "Retry request %lx - Calling StartIo\n", Irp));
ExFreePool(srb->SenseInfoBuffer);
if (srb->DataBuffer) {
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.
//
ScsiCdRomStartIo(DeviceObject, realIrp);
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)) {
switch (realIrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_DISK_GET_LENGTH_INFO: {
PREAD_CAPACITY_DATA readCapacityBuffer = srb->DataBuffer;
ULONG lastSector;
ULONG bps;
ULONG lastBit;
ULONG tmp;
//
// 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;
}
deviceExtension->DiskGeometry->Geometry.BytesPerSector = bps;
DebugPrint((2,
"CdRomDeviceControlCompletion: Calculated bps %#x\n",
deviceExtension->DiskGeometry->Geometry.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, deviceExtension->SectorShift);
DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Sector size is %d\n",
deviceExtension->DiskGeometry->Geometry.BytesPerSector));
DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Number of Sectors is %d\n",
lastSector + 1));
//
// Calculate media capacity in bytes.
//
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
//
// Calculate number of cylinders.
//
deviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64));
deviceExtension->PartitionLength.QuadPart =
(deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
//
// This device supports removable media.
//
deviceExtension->DiskGeometry->Geometry.MediaType = RemovableMedia;
} else {
//
// Assume media type is fixed disk.
//
deviceExtension->DiskGeometry->Geometry.MediaType = FixedMedia;
}
//
// Assume sectors per track are 32;
//
deviceExtension->DiskGeometry->Geometry.SectorsPerTrack = 32;
//
// Assume tracks per cylinder (number of heads) is 64.
//
deviceExtension->DiskGeometry->Geometry.TracksPerCylinder = 64;
//
// Copy the device extension's geometry info into the user buffer.
//
RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
&deviceExtension->PartitionLength,
sizeof(GET_LENGTH_INFORMATION));
//
// update information field.
//
realIrp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION);
break;
}
case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: {
PREAD_CAPACITY_DATA readCapacityBuffer = srb->DataBuffer;
ULONG lastSector;
ULONG bps;
ULONG lastBit;
ULONG tmp;
PDISK_GEOMETRY_EX geometryEx;
//
// 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;
}
deviceExtension->DiskGeometry->Geometry.BytesPerSector = bps;
DebugPrint((2,
"CdRomDeviceControlCompletion: Calculated bps %#x\n",
deviceExtension->DiskGeometry->Geometry.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, deviceExtension->SectorShift);
DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Sector size is %d\n",
deviceExtension->DiskGeometry->Geometry.BytesPerSector));
DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Number of Sectors is %d\n",
lastSector + 1));
//
// Calculate media capacity in bytes.
//
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
//
// Calculate number of cylinders.
//
deviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64));
deviceExtension->PartitionLength.QuadPart =
(deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
//
// This device supports removable media.
//
deviceExtension->DiskGeometry->Geometry.MediaType = RemovableMedia;
} else {
//
// Assume media type is fixed disk.
//
deviceExtension->DiskGeometry->Geometry.MediaType = FixedMedia;
}
//
// Assume sectors per track are 32;
//
deviceExtension->DiskGeometry->Geometry.SectorsPerTrack = 32;
//
// Assume tracks per cylinder (number of heads) is 64.
//
deviceExtension->DiskGeometry->Geometry.TracksPerCylinder = 64;
//
// Copy the device extension's geometry info into the user buffer.
//
geometryEx = realIrp->AssociatedIrp.SystemBuffer;
RtlMoveMemory(&geometryEx->Geometry,
&deviceExtension->DiskGeometry->Geometry,
sizeof(DISK_GEOMETRY));
//
// Copy the extended information
//
geometryEx->DiskSize = deviceExtension->PartitionLength;
//
// update information field.
//
realIrp->IoStatus.Information = FIELD_OFFSET(DISK_GEOMETRY_EX, Data);
break;
}
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
PREAD_CAPACITY_DATA readCapacityBuffer = srb->DataBuffer;
ULONG lastSector;
ULONG bps;
ULONG lastBit;
ULONG tmp;
//
// 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;
}
deviceExtension->DiskGeometry->Geometry.BytesPerSector = bps;
DebugPrint((2,
"CdRomDeviceControlCompletion: Calculated bps %#x\n",
deviceExtension->DiskGeometry->Geometry.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, deviceExtension->SectorShift);
DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Sector size is %d\n",
deviceExtension->DiskGeometry->Geometry.BytesPerSector));
DebugPrint((2,"SCSI ScsiClassReadDriveCapacity: Number of Sectors is %d\n",
lastSector + 1));
//
// Calculate media capacity in bytes.
//
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
//
// Calculate number of cylinders.
//
deviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64));
deviceExtension->PartitionLength.QuadPart =
(deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
//
// This device supports removable media.
//
deviceExtension->DiskGeometry->Geometry.MediaType = RemovableMedia;
} else {
//
// Assume media type is fixed disk.
//
deviceExtension->DiskGeometry->Geometry.MediaType = FixedMedia;
}
//
// Assume sectors per track are 32;
//
deviceExtension->DiskGeometry->Geometry.SectorsPerTrack = 32;
//
// Assume tracks per cylinder (number of heads) is 64.
//
deviceExtension->DiskGeometry->Geometry.TracksPerCylinder = 64;
//
// Copy the device extension's geometry info into the user buffer.
//
RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
deviceExtension->DiskGeometry,
sizeof(DISK_GEOMETRY));
//
// update information field.
//
realIrp->IoStatus.Information = sizeof(DISK_GEOMETRY);
break;
}
case IOCTL_CDROM_CHECK_VERIFY:
if((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY) &&
(realIrpStack->Parameters.DeviceIoControl.OutputBufferLength)) {
*((PULONG)realIrp->AssociatedIrp.SystemBuffer) =
physicalExtension->MediaChangeCount;
realIrp->IoStatus.Information = sizeof(ULONG);
} else {
realIrp->IoStatus.Information = 0;
}
DebugPrint((2, "CdRomDeviceControlCompletion: [%lx] completing CHECK_VERIFY buddy irp %lx\n", realIrp, Irp));
break;
case IOCTL_CDROM_GET_LAST_SESSION:
case IOCTL_CDROM_READ_TOC: {
PCDROM_TOC toc = srb->DataBuffer;
//
// Copy the device extension's geometry info into the user buffer.
//
RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer,
toc,
srb->DataTransferLength);
//
// update information field.
//
realIrp->IoStatus.Information = srb->DataTransferLength;
break;
}
case IOCTL_CDROM_PLAY_AUDIO_MSF:
PLAY_ACTIVE(deviceExtension) = TRUE;
break;
case IOCTL_CDROM_READ_Q_CHANNEL: {
PSUB_Q_CHANNEL_DATA userChannelData = realIrp->AssociatedIrp.SystemBuffer;
#if DBG
PCDROM_SUB_Q_DATA_FORMAT inputBuffer = realIrp->AssociatedIrp.SystemBuffer;
#endif
PSUB_Q_CHANNEL_DATA subQPtr = srb->DataBuffer;
#if DBG
switch( inputBuffer->Format ) {
case IOCTL_CDROM_CURRENT_POSITION:
DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus ));
DebugPrint((2,"CdRomDeviceControlCompletion: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR ));
DebugPrint((2,"CdRomDeviceControlCompletion: Control = 0x%x\n", subQPtr->CurrentPosition.Control ));
DebugPrint((2,"CdRomDeviceControlCompletion: Track = %u\n", subQPtr->CurrentPosition.TrackNumber ));
DebugPrint((2,"CdRomDeviceControlCompletion: Index = %u\n", subQPtr->CurrentPosition.IndexNumber ));
DebugPrint((2,"CdRomDeviceControlCompletion: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) ));
DebugPrint((2,"CdRomDeviceControlCompletion: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) ));
break;
case IOCTL_CDROM_MEDIA_CATALOG:
DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus ));
DebugPrint((2,"CdRomDeviceControlCompletion: Mcval is %u\n", subQPtr->MediaCatalog.Mcval ));
break;
case IOCTL_CDROM_TRACK_ISRC:
DebugPrint((2,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus ));
DebugPrint((2,"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(deviceExtension) = TRUE;
} else {
PLAY_ACTIVE(deviceExtension) = 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(deviceExtension) = 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(deviceExtension) = FALSE;
realIrp->IoStatus.Information = 0;
break;
case IOCTL_CDROM_GET_CONTROL: {
PCDROM_AUDIO_CONTROL audioControl = srb->DataBuffer;
PAUDIO_OUTPUT audioOutput;
ULONG bytesTransferred;
audioOutput = ScsiClassFindModePage((PCHAR)audioControl,
srb->DataTransferLength,
CDROM_AUDIO_CONTROL_PAGE,
use6Byte);
//
// Verify the page is as big as expected.
//
bytesTransferred = (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,bytesTransferred;
audioOutput = ScsiClassFindModePage((PCHAR)volumeControl,
srb->DataTransferLength,
CDROM_AUDIO_CONTROL_PAGE,
use6Byte);
//
// Verify the page is as big as expected.
//
bytesTransferred = (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 = sizeof(VOLUME_CONTROL);
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.
//
DebugPrint((1, "CdRomDeviceCompletion - Setting Hard Error on realIrp %lx\n",
realIrp));
IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
realIrp->IoStatus.Information = 0;
}
IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
NTAPI
CdRomSetVolumeIntermediateCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension + 1);
BOOLEAN use6Byte = 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) {
DebugPrint((2,
"CdRomSetVolumeIntermediateCompletion: Irp %lx, Srb %lx Real Irp\n",
Irp,
srb,
realIrp));
//
// Release the queue if it is frozen.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
ScsiClassReleaseQueue(DeviceObject);
}
retry = ScsiClassInterpretSenseInfo(DeviceObject,
srb,
irpStack->MajorFunction,
irpStack->Parameters.DeviceIoControl.IoControlCode,
MAXIMUM_RETRIES - ((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1),
&status);
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 = (PVOID)((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1-1))) {
if (((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) {
//
// Retry request.
//
DebugPrint((1, "Retry request %lx - 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.
//
ScsiCdRomStartIo(DeviceObject, realIrp);
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 = ScsiClassFindModePage((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) {
DebugPrint((1, "Mode Sense Page %d not found\n",
CDROM_AUDIO_CONTROL_PAGE));
realIrp->IoStatus.Information = 0;
realIrp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
ExFreePool(srb->SenseInfoBuffer);
ExFreePool(srb);
IoFreeMdl(Irp->MdlAddress);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
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 = ExAllocatePool(NonPagedPoolCacheAligned, bytesTransferred);
if (!dataBuffer) {
realIrp->IoStatus.Information = 0;
realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
ExFreePool(srb->SenseInfoBuffer);
ExFreePool(srb);
IoFreeMdl(Irp->MdlAddress);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
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.
//
ExFreePool(srb->DataBuffer);
IoFreeMdl(Irp->MdlAddress);
//
// rebuild the srb.
//
cdb = (PCDB)srb->Cdb;
RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
srb->SrbStatus = srb->ScsiStatus = 0;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | 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;
IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
ExFreePool(srb->SenseInfoBuffer);
ExFreePool(srb);
ExFreePool(dataBuffer);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
MmBuildMdlForNonPagedPool(Irp->MdlAddress);
srb->DataBuffer = dataBuffer;
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(deviceExtension->PortDeviceObject, Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// 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.
//
IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
realIrp->IoStatus.Information = 0;
}
IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
NTAPI
CdRomSwitchModeCompletion(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context
)
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension + 1);
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) {
DebugPrint((2,
"CdRomSetVolumeIntermediateCompletion: Irp %lx, Srb %lx Real Irp\n",
Irp,
srb,
realIrp));
//
// Release the queue if it is frozen.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
ScsiClassReleaseQueue(DeviceObject);
}
retry = ScsiClassInterpretSenseInfo(DeviceObject,
srb,
irpStack->MajorFunction,
irpStack->Parameters.DeviceIoControl.IoControlCode,
MAXIMUM_RETRIES - ((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1),
&status);
//
// 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 = (PVOID)((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1-1))) {
if (((ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) {
//
// Retry request.
//
DebugPrint((1, "Retry request %lx - 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.
//
ScsiCdRomStartIo(DeviceObject, realIrp);
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->u1.BlockDescriptor.BlockLength[0] << 16;
sectorSize |= (cdData->u1.BlockDescriptor.BlockLength[1] << 8);
sectorSize |= (cdData->u1.BlockDescriptor.BlockLength[2]);
cdData->RawAccess = (sectorSize == RAW_SECTOR_SIZE) ? TRUE : FALSE;
//
// Free the old data buffer, mdl.
//
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;
//
// Calculate starting offset.
//
startingSector = (ULONG)(rawReadInfo->DiskOffset.QuadPart >> deviceExtension->SectorShift);
transferByteCount = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
maximumTransferLength = deviceExtension->PortCapabilities->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 > deviceExtension->PortCapabilities->MaximumPhysicalPages) {
realIrp->IoStatus.Information = 0;
realIrp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
ExFreePool(srb->SenseInfoBuffer);
ExFreePool(srb);
IoStartNextPacket(DeviceObject, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
srb->OriginalRequest = realIrp;
srb->SrbFlags = deviceExtension->SrbFlags;
srb->SrbFlags |= (SRB_FLAGS_DISABLE_SYNCH_TRANSFER | SRB_FLAGS_DATA_IN);
srb->DataTransferLength = transferByteCount;
srb->TimeOutValue = deviceExtension->TimeOutValue;
srb->CdbLength = 10;
srb->DataBuffer = MmGetMdlVirtualAddress(realIrp->MdlAddress);
if (rawReadInfo->TrackMode == CDDA) {
if (cdData->XAFlags & PLEXTOR_CDDA) {
srb->CdbLength = 12;
cdb->PLXTR_READ_CDDA.LogicalUnitNumber = deviceExtension->Lun;
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 (cdData->XAFlags & 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.LogicalUnitNumber = deviceExtension->Lun;
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 {
ULONG maximumTransferLength = deviceExtension->PortCapabilities->MaximumTransferLength;
ULONG transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(MmGetMdlVirtualAddress(realIrp->MdlAddress),
realIrpStack->Parameters.Read.Length);
//
// Back to cooked sectors. Build and send a normal read.
// The real work for setting offsets and checking for splitrequests was
// done in startio
//
if ((realIrpStack->Parameters.Read.Length > maximumTransferLength) ||
(transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages)) {
//
// 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.
//
ScsiClassSplitRequest(DeviceObject, realIrp, maximumTransferLength);
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
//
// Build SRB and CDB for this IRP.
//
ScsiClassBuildRequest(DeviceObject, realIrp);
}
}
//
// Call the port driver.
//
IoCallDriver(deviceExtension->PortDeviceObject, realIrp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Update device Extension flags to indicate that XA isn't supported.
//
cdData->XAFlags |= XA_NOT_SUPPORTED;
//
// 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.
//
IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject);
realIrp->IoStatus.Information = 0;
}
IoCompleteRequest(realIrp, IO_DISK_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
NTAPI
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);
PIO_STACK_LOCATION irpNextStack = IoGetNextIrpStackLocation(Irp);
PSCSI_REQUEST_BLOCK srb = Context;
NTSTATUS status;
BOOLEAN retry;
//
// Check SRB status for success of completing request.
//
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
DebugPrint((2,"ScsiClassIoComplete: IRP %lx, SRB %lx\n", Irp, srb));
//
// Release the queue if it is frozen.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
ScsiClassReleaseQueue(DeviceObject);
}
retry = ScsiClassInterpretSenseInfo(
DeviceObject,
srb,
irpStack->MajorFunction,
irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0,
MAXIMUM_RETRIES - ((ULONG_PTR)irpNextStack->Parameters.Others.Argument1),
&status);
//
// 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 && (irpNextStack->Parameters.Others.Argument1 = (PVOID)((ULONG_PTR)irpNextStack->Parameters.Others.Argument1-1))) {
if (((ULONG_PTR)irpNextStack->Parameters.Others.Argument1)) {
//
// Retry request.
//
DebugPrint((1, "CdRomXACompletion: Retry request %lx - Calling StartIo\n", Irp));
ExFreePool(srb->SenseInfoBuffer);
ExFreePool(srb->DataBuffer);
ExFreePool(srb);
//
// Call StartIo directly since IoStartNextPacket hasn't been called,
// the serialisation is still intact.
//
ScsiCdRomStartIo(DeviceObject, Irp);
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;
} // end if (SRB_STATUS(srb->SrbStatus) ...
//
// Return SRB to nonpaged pool.
//
ExFreePool(srb);
//
// Set status in completing IRP.
//
Irp->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.
//
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);
}
//IoCompleteRequest(Irp, IO_DISK_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
return status;
}
NTSTATUS
NTAPI
CdRomDeviceControl(
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
--*/
{
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION nextStack;
PKEVENT deviceControlEvent;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension+1);
SCSI_REQUEST_BLOCK srb;
NTSTATUS status;
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;
DebugPrint((1, "CdRomDeviceControl: Ioctl Code = %#08lx, 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);
DebugPrint((1, "CdRomDeviceControl: Class Code - new ioctl code is %#08lx\n",
ioctlCode));
irpStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
}
switch (ioctlCode) {
case IOCTL_CDROM_RAW_READ: {
LARGE_INTEGER startingOffset;
ULONG transferBytes;
PRAW_READ_INFO rawReadInfo = (PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer;
PUCHAR userData = (PUCHAR)Irp->AssociatedIrp.SystemBuffer;
//
// Ensure that XA reads are supported.
//
if (cdData->XAFlags & XA_NOT_SUPPORTED) {
DebugPrint((1,
"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. Validate the buffers.
//
rawReadInfo = (PRAW_READ_INFO)userData;
irpStack->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)userData;
if (rawReadInfo == NULL) {
DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (No extent info\n"));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
if (irpStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(RAW_READ_INFO)) {
DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Invalid info buffer\n"));
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
}
startingOffset.QuadPart = rawReadInfo->DiskOffset.QuadPart;
transferBytes = rawReadInfo->SectorCount * RAW_SECTOR_SIZE;
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < transferBytes) {
DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Bad buffer size)\n"));
//
// Fail request with status of invalid parameters.
//
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_PARAMETER;
}
if ((startingOffset.QuadPart + transferBytes) > deviceExtension->PartitionLength.QuadPart) {
DebugPrint((1,"CdRomDeviceControl: Invalid I/O parameters for XA Read (Request Out of Bounds)\n"));
//
// Fail request with status of invalid parameters.
//
status = STATUS_INVALID_PARAMETER;
break;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
DebugPrint((2,"CdRomDeviceControl: Get drive geometry\n"));
if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( DISK_GEOMETRY ) ) {
status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,Irp, NULL,NULL);
return STATUS_PENDING;
}
case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX:
case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: {
DebugPrint((2,"CdRomDeviceControl: Get drive geometry ex\n"));
if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( DISK_GEOMETRY_EX ) ) {
status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,Irp, NULL,NULL);
return STATUS_PENDING;
}
case IOCTL_DISK_GET_LENGTH_INFO: {
DebugPrint((2,"CdRomDeviceControl: Get length info\n"));
if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof( GET_LENGTH_INFORMATION ) ) {
status = STATUS_INFO_LENGTH_MISMATCH;
break;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,Irp, NULL,NULL);
return STATUS_PENDING;
}
case IOCTL_CDROM_GET_LAST_SESSION:
case IOCTL_CDROM_READ_TOC: {
//
// If the cd is playing music then reject this request.
//
if (CdRomIsPlayActive(DeviceObject)) {
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_DEVICE_BUSY;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
case IOCTL_CDROM_PLAY_AUDIO_MSF: {
//
// Play Audio MSF
//
DebugPrint((2,"CdRomDeviceControl: Play audio MSF\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CDROM_PLAY_AUDIO_MSF)) {
//
// Indicate unsuccessful status.
//
status = STATUS_BUFFER_TOO_SMALL;
break;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
case IOCTL_CDROM_SEEK_AUDIO_MSF: {
//
// Seek Audio MSF
//
DebugPrint((2,"CdRomDeviceControl: Seek audio MSF\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CDROM_SEEK_AUDIO_MSF)) {
//
// Indicate unsuccessful status.
//
status = STATUS_BUFFER_TOO_SMALL;
break;
} else {
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
}
case IOCTL_CDROM_PAUSE_AUDIO: {
//
// Pause audio
//
DebugPrint((2, "CdRomDeviceControl: Pause audio\n"));
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
break;
}
case IOCTL_CDROM_RESUME_AUDIO: {
//
// Resume audio
//
DebugPrint((2, "CdRomDeviceControl: Resume audio\n"));
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
case IOCTL_CDROM_READ_Q_CHANNEL: {
if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(CDROM_SUB_Q_DATA_FORMAT)) {
status = STATUS_BUFFER_TOO_SMALL;
Irp->IoStatus.Information = 0;
break;
}
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
case IOCTL_CDROM_GET_CONTROL: {
DebugPrint((2, "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 = 0;
break;
} else {
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
}
case IOCTL_CDROM_GET_VOLUME: {
DebugPrint((2, "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 = 0;
break;
} else {
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
}
case IOCTL_CDROM_SET_VOLUME: {
DebugPrint((2, "CdRomDeviceControl: Set volume control\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
sizeof(VOLUME_CONTROL)) {
//
// Indicate unsuccessful status.
//
status = STATUS_BUFFER_TOO_SMALL;
break;
} else {
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
}
case IOCTL_CDROM_STOP_AUDIO: {
//
// Stop play.
//
DebugPrint((2, "CdRomDeviceControl: Stop audio\n"));
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,Irp, NULL,NULL);
return STATUS_PENDING;
}
case IOCTL_CDROM_CHECK_VERIFY: {
DebugPrint((1, "CdRomDeviceControl: [%lx] Check Verify\n", Irp));
IoMarkIrpPending(Irp);
if((irpStack->Parameters.DeviceIoControl.OutputBufferLength) &&
(irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))) {
DebugPrint((1, "CdRomDeviceControl: Check Verify: media count "
"buffer too small\n"));
status = STATUS_BUFFER_TOO_SMALL;
break;
}
IoStartPacket(DeviceObject,Irp, NULL,NULL);
return STATUS_PENDING;
}
default: {
//
// allocate an event and stuff it into our stack location.
//
deviceControlEvent = ExAllocatePool(NonPagedPool, sizeof(KEVENT));
if(!deviceControlEvent) {
status = STATUS_INSUFFICIENT_RESOURCES;
} else {
PIO_STACK_LOCATION currentStack;
KeInitializeEvent(deviceControlEvent, NotificationEvent, FALSE);
currentStack = IoGetCurrentIrpStackLocation(Irp);
nextStack = IoGetNextIrpStackLocation(Irp);
//
// Copy the stack down a notch
//
*nextStack = *currentStack;
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
//
nextStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject, Irp, NULL, NULL);
//
// Wait for CdRomClassIoctlCompletion to set the event. This
// ensures serialization remains intact for these unhandled device
// controls.
//
KeWaitForSingleObject(
deviceControlEvent,
Suspended,
KernelMode,
FALSE,
NULL);
ExFreePool(deviceControlEvent);
DebugPrint((2, "CdRomDeviceControl: irp %#08lx synchronized\n", Irp));
//
// 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(Irp->IoStatus.Status == STATUS_SUCCESS) {
status = ScsiClassDeviceControl(DeviceObject, Irp);
KeRaiseIrql(DISPATCH_LEVEL, &irql);
IoStartNextPacket(DeviceObject, FALSE);
KeLowerIrql(irql);
}
}
return status;
}
} // 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)) {
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject);
}
//
// Update IRP with completion status.
//
Irp->IoStatus.Status = status;
//
// Complete the request.
//
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
DebugPrint((2, "CdRomDeviceControl: Status is %lx\n", status));
return status;
} // end ScsiCdRomDeviceControl()
VOID
NTAPI
ScanForSpecial(
PDEVICE_OBJECT DeviceObject,
PINQUIRYDATA InquiryData,
PIO_SCSI_CAPABILITIES PortCapabilities
)
/*++
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.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension+1);
//
// Look for a Hitachi CDR-1750. Read-ahead must be disabled in order
// to get this cdrom drive to work on scsi adapters that use PIO.
//
if ((strncmp((PCHAR)InquiryData->VendorId, "HITACHI CDR-1750S", strlen("HITACHI CDR-1750S")) == 0 ||
strncmp((PCHAR)InquiryData->VendorId, "HITACHI CDR-3650/1650S", strlen("HITACHI CDR-3650/1650S")) == 0)
&& PortCapabilities->AdapterUsesPio) {
DebugPrint((1, "CdRom ScanForSpecial: Found Hitachi CDR-1750S.\n"));
//
// Setup an error handler to reinitialize the cd rom after it is reset.
//
deviceExtension->ClassError = HitachProcessError;
} else if (( RtlCompareMemory( InquiryData->VendorId,"FUJITSU", 7 ) == 7 ) &&
(( RtlCompareMemory( InquiryData->ProductId,"FMCD-101", 8 ) == 8 ) ||
( RtlCompareMemory( InquiryData->ProductId,"FMCD-102", 8 ) == 8 ))) {
//
// 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.
//
deviceExtension->TimeOutValue = 20;
} else if (( RtlCompareMemory( InquiryData->VendorId,"TOSHIBA", 7) == 7) &&
(( RtlCompareMemory( InquiryData->ProductId,"CD-ROM XM-34", 12) == 12))) {
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 = deviceExtension->TimeOutValue;
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
cdb->MODE_SENSE.PageCode = 0x1;
cdb->MODE_SENSE.AllocationLength = (UCHAR)length;
buffer = ExAllocatePool(NonPagedPoolCacheAligned, (sizeof(MODE_READ_RECOVERY_PAGE) + MODE_BLOCK_DESC_LENGTH + MODE_HEADER_LENGTH));
if (!buffer) {
return;
}
status = ScsiClassSendSrbSynchronous(DeviceObject,
&srb,
buffer,
length,
FALSE);
((PERROR_RECOVERY_DATA)buffer)->BlockDescriptor.DensityCode = 0x83;
((PERROR_RECOVERY_DATA)buffer)->Header.ModeDataLength = 0x0;
RtlCopyMemory(&cdData->u1.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 = deviceExtension->TimeOutValue;
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT;
cdb->MODE_SELECT.PFBit = 1;
cdb->MODE_SELECT.ParameterListLength = (UCHAR)length;
status = ScsiClassSendSrbSynchronous(DeviceObject,
&srb,
buffer,
length,
TRUE);
if (!NT_SUCCESS(status)) {
DebugPrint((1,
"Cdrom.ScanForSpecial: Setting density code on Toshiba failed [%x]\n",
status));
}
deviceExtension->ClassError = ToshibaProcessError;
ExFreePool(buffer);
}
//
// Determine special CD-DA requirements.
//
if (RtlCompareMemory( InquiryData->VendorId,"PLEXTOR",7) == 7) {
cdData->XAFlags |= PLEXTOR_CDDA;
} else if (RtlCompareMemory ( InquiryData->VendorId,"NEC",3) == 3) {
cdData->XAFlags |= NEC_CDDA;
}
return;
}
VOID
NTAPI
HitachProcessError(
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.
--*/
{
PDEVICE_EXTENSION deviceExtension = 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 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 a bad HITACHI cd-rom. These devices do 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) {
DebugPrint((1, "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 = ExAllocatePool(
NonPagedPool,
sizeof(COMPLETION_CONTEXT) + HITACHI_MODE_DATA_SIZE + alignment
);
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->PathId = deviceExtension->PathId;
srb->TargetId = deviceExtension->TargetId;
srb->Lun = deviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->TimeOutValue = deviceExtension->TimeOutValue;
//
// Set the transfer length.
//
srb->DataTransferLength = HITACHI_MODE_DATA_SIZE;
srb->SrbFlags = SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE | 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);
IoSetCompletionRoutine(irp,
(PIO_COMPLETION_ROUTINE)ScsiClassAsynchronousCompletion,
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(deviceExtension->PortDeviceObject, irp);
}
}
NTSTATUS
NTAPI
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;
//
// Check for a frozen queue.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
//
// Unfreeze the queue getting the device object from the context.
//
ScsiClassReleaseQueue(DeviceObject);
}
//
// Free all of the allocations.
//
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
NTAPI
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.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PCDROM_DATA cdData = (PCDROM_DATA)(deviceExtension+1);
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)(deviceExtension->DeviceObject->StackSize+1),
FALSE);
if (!irp) {
return;
}
srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
if (!srb) {
IoFreeIrp(irp);
return;
}
length = sizeof(ERROR_RECOVERY_DATA);
dataBuffer = ExAllocatePool(NonPagedPoolCacheAligned, length);
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 = deviceExtension->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);
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->PathId = deviceExtension->PathId;
srb->TargetId = deviceExtension->TargetId;
srb->Lun = deviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->Cdb[1] |= deviceExtension->Lun << 5;
srb->SrbStatus = srb->ScsiStatus = 0;
srb->NextSrb = 0;
srb->OriginalRequest = irp;
srb->SenseInfoBufferLength = 0;
//
// Set the transfer length.
//
srb->DataTransferLength = length;
srb->SrbFlags = SRB_FLAGS_DATA_OUT | SRB_FLAGS_DISABLE_AUTOSENSE | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
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->u1.Header, length);
//
// Set the density code.
//
((PERROR_RECOVERY_DATA)srb->DataBuffer)->BlockDescriptor.DensityCode = 0x83;
IoCallDriver(deviceExtension->PortDeviceObject, irp);
}
}
BOOLEAN
NTAPI
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.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PIRP irp;
IO_STATUS_BLOCK ioStatus;
KEVENT event;
NTSTATUS status;
PSUB_Q_CURRENT_POSITION currentBuffer;
if (!PLAY_ACTIVE(deviceExtension)) {
return(FALSE);
}
currentBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(SUB_Q_CURRENT_POSITION));
if (currentBuffer == NULL) {
return(FALSE);
}
((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;
//
// Create notification event object to be used to signal the
// request completion.
//
KeInitializeEvent(&event, NotificationEvent, FALSE);
//
// Build the synchronous request to be sent to the port driver
// to perform the request.
//
irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_READ_Q_CHANNEL,
deviceExtension->DeviceObject,
currentBuffer,
sizeof(CDROM_SUB_Q_DATA_FORMAT),
currentBuffer,
sizeof(SUB_Q_CURRENT_POSITION),
FALSE,
&event,
&ioStatus);
if (irp == NULL) {
ExFreePool(currentBuffer);
return FALSE;
}
//
// Pass request to port driver and wait for request to complete.
//
status = IoCallDriver(deviceExtension->DeviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS(status)) {
ExFreePool(currentBuffer);
return FALSE;
}
ExFreePool(currentBuffer);
return(PLAY_ACTIVE(deviceExtension));
}
IO_COMPLETION_ROUTINE CdRomMediaChangeCompletion;
NTSTATUS
NTAPI
CdRomMediaChangeCompletion(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
/*++
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
--*/
{
PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context;
PIO_STACK_LOCATION cdStack = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION irpNextStack = IoGetNextIrpStackLocation(Irp);
PDEVICE_EXTENSION deviceExtension;
PDEVICE_EXTENSION physicalExtension;
PSENSE_DATA senseBuffer;
PCDROM_DATA cddata;
ASSERT(Irp);
ASSERT(cdStack);
DeviceObject = cdStack->DeviceObject;
ASSERT(DeviceObject);
deviceExtension = DeviceObject->DeviceExtension;
physicalExtension = deviceExtension->PhysicalDevice->DeviceExtension;
cddata = (PCDROM_DATA)(deviceExtension + 1);
ASSERT(cddata->MediaChangeIrp == NULL);
//
// If the sense data field is valid, look for a media change.
// otherwise this iteration of the polling will just assume nothing
// changed.
//
DebugPrint((3, "CdRomMediaChangeHandler: Completing Autorun Irp 0x%lx "
"for device %d\n",
Irp, deviceExtension->DeviceNumber));
if (srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
if (srb->SenseInfoBufferLength >= FIELD_OFFSET(SENSE_DATA, CommandSpecificInformation)) {
//
// See if this is a media change.
//
senseBuffer = srb->SenseInfoBuffer;
if ((senseBuffer->SenseKey & 0x0f) == SCSI_SENSE_UNIT_ATTENTION) {
if (senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_MEDIUM_CHANGED) {
DebugPrint((1, "CdRomMediaChangeCompletion: New media inserted "
"into CdRom%d [irp = 0x%lx]\n",
deviceExtension->DeviceNumber, Irp));
//
// Media change event occurred - signal the named event.
//
KeSetEvent(deviceExtension->MediaChangeEvent,
(KPRIORITY) 0,
FALSE);
deviceExtension->MediaChangeNoMedia = FALSE;
}
if (DeviceObject->Vpb->Flags & VPB_MOUNTED) {
//
// Must remember the media changed and force the
// file system to verify on next access
//
DeviceObject->Flags |= DO_VERIFY_VOLUME;
}
physicalExtension->MediaChangeCount++;
} else if(((senseBuffer->SenseKey & 0x0f) == SCSI_SENSE_NOT_READY)&&
(senseBuffer->AdditionalSenseCode == SCSI_ADSENSE_NO_MEDIA_IN_DEVICE)&&
(!deviceExtension->MediaChangeNoMedia)){
//
// If there was no media in the device then signal the waiters if
// we haven't already done so before.
//
DebugPrint((1, "CdRomMediaChangeCompletion: No media in device"
"CdRom%d [irp = 0x%lx]\n",
deviceExtension->DeviceNumber, Irp));
KeSetEvent(deviceExtension->MediaChangeEvent,
(KPRIORITY) 0,
FALSE);
deviceExtension->MediaChangeNoMedia = TRUE;
}
}
} else if((srb->SrbStatus == SRB_STATUS_SUCCESS)&&
(deviceExtension->MediaChangeNoMedia)) {
//
// We didn't have any media before and now the requests are succeeding
// we probably missed the Media change somehow. Signal the change
// anyway
//
DebugPrint((1, "CdRomMediaChangeCompletion: Request completed normally"
"for CdRom%d which was marked w/NoMedia [irp = 0x%lx]\n",
deviceExtension->DeviceNumber, Irp));
KeSetEvent(deviceExtension->MediaChangeEvent,
(KPRIORITY) 0,
FALSE);
deviceExtension->MediaChangeNoMedia = FALSE;
}
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
ScsiClassReleaseQueue(deviceExtension->DeviceObject);
}
//
// Remember the IRP and SRB for use the next time.
//
irpNextStack->Parameters.Scsi.Srb = srb;
cddata->MediaChangeIrp = Irp;
if (deviceExtension->ClassError) {
NTSTATUS status;
BOOLEAN retry;
//
// Throw away the status and retry values. Just give the error routine a chance
// to do what it needs to.
//
deviceExtension->ClassError(DeviceObject,
srb,
&status,
&retry);
}
IoStartNextPacket(DeviceObject, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
NTAPI
CdRomTickHandler(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
)
/*++
Routine Description:
This routine handles the once per second timer provided by the
Io subsystem. It is only used when the cdrom device itself is
a candidate for autoplay support. It should never be called if
the cdrom device is a changer device.
Arguments:
DeviceObject - what to check.
Context - not used.
Return Value:
None.
--*/
{
PIRP irp;
PIRP heldIrpList;
PIRP nextIrp;
PLIST_ENTRY listEntry;
PCDROM_DATA cddata;
PIO_STACK_LOCATION irpStack;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
cddata = (PCDROM_DATA)(deviceExtension + 1);
if (cddata->MediaChange) {
if (cddata->MediaChangeIrp != NULL) {
//
// 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
// and only one instance of it should be running at any
// given time.
//
cddata->MediaChangeCountDown--;
#if DBG
cddata->MediaChangeIrpTimeInUse = 0;
cddata->MediaChangeIrpLost = FALSE;
#endif
if (!cddata->MediaChangeCountDown) {
PSCSI_REQUEST_BLOCK srb;
PIO_STACK_LOCATION irpNextStack;
PCDB cdb;
//
// Reset the timer.
//
cddata->MediaChangeCountDown = MEDIA_CHANGE_DEFAULT_TIME;
//
// Prepare the IRP for the test unit ready
//
irp = cddata->MediaChangeIrp;
cddata->MediaChangeIrp = NULL;
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;
irpNextStack = IoGetNextIrpStackLocation(irp);
irpNextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
irpNextStack->Parameters.DeviceIoControl.IoControlCode =
IOCTL_SCSI_EXECUTE_NONE;
//
// Prepare the SRB for execution.
//
srb = irpNextStack->Parameters.Scsi.Srb;
srb->SrbStatus = srb->ScsiStatus = 0;
srb->NextSrb = 0;
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->PathId = deviceExtension->PathId;
srb->TargetId = deviceExtension->TargetId;
srb->Lun = deviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER |
SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
srb->DataTransferLength = 0;
srb->OriginalRequest = irp;
RtlZeroMemory(srb->SenseInfoBuffer, SENSE_BUFFER_SIZE);
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
cdb = (PCDB) &srb->Cdb[0];
cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
cdb->CDB6GENERIC.LogicalUnitNumber = srb->Lun;
//
// Setup the IRP to perform a test unit ready.
//
IoSetCompletionRoutine(irp,
CdRomMediaChangeCompletion,
srb,
TRUE,
TRUE,
TRUE);
//
// Issue the request.
//
IoStartPacket(DeviceObject, irp, NULL, NULL);
}
} else {
#if DBG
if(cddata->MediaChangeIrpLost == FALSE) {
if(cddata->MediaChangeIrpTimeInUse++ >
MEDIA_CHANGE_TIMEOUT_TIME) {
DebugPrint((0, "CdRom%d: AutoPlay 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",
deviceExtension->DeviceNumber));
cddata->MediaChangeIrpLost = TRUE;
}
}
#endif
}
}
//
// Process all generic timer IRPS in the timer list. As IRPs are pulled
// off of the TimerIrpList they must be remembered in the first loop
// if they are not sent to the lower driver. After all items have
// been pulled off the list, it is possible to put the held IRPs back
// into the TimerIrpList.
//
heldIrpList = NULL;
if (IsListEmpty(&cddata->TimerIrpList)) {
listEntry = NULL;
} else {
listEntry = ExInterlockedRemoveHeadList(&cddata->TimerIrpList,
&cddata->TimerIrpSpinLock);
}
while (listEntry) {
//
// There is something in the timer list. Pick up the IRP and
// see if it is ready to be submitted.
//
irp = CONTAINING_RECORD(listEntry, IRP, Tail.Overlay.ListEntry);
irpStack = IoGetCurrentIrpStackLocation(irp);
if (irpStack->Parameters.Others.Argument3) {
ULONG_PTR count;
//
// Decrement the countdown timer and put the IRP back in the list.
//
count = (ULONG_PTR) irpStack->Parameters.Others.Argument3;
count--;
irpStack->Parameters.Others.Argument3 = (PVOID) count;
ASSERT(irp->AssociatedIrp.MasterIrp == NULL);
if (heldIrpList) {
irp->AssociatedIrp.MasterIrp = (PVOID) heldIrpList;
}
heldIrpList = irp;
} else {
//
// Submit this IRP to the lower driver. This IRP does not
// need to be remembered here. It will be handled again when
// it completes.
//
DebugPrint((1, "CdRomTickHandler: Reissuing request %lx (thread = %lx)\n", irp, irp->Tail.Overlay.Thread));
//
// feed this to the appropriate port driver
//
IoCallDriver (deviceExtension->PortDeviceObject, irp);
}
//
// Pick up the next IRP from the timer list.
//
listEntry = ExInterlockedRemoveHeadList(&cddata->TimerIrpList,
&cddata->TimerIrpSpinLock);
}
//
// Move all held IRPs back onto the timer list.
//
while (heldIrpList) {
//
// Save the single list pointer before queueing this IRP.
//
nextIrp = (PIRP) heldIrpList->AssociatedIrp.MasterIrp;
heldIrpList->AssociatedIrp.MasterIrp = NULL;
//
// Return the held IRP to the timer list.
//
ExInterlockedInsertTailList(&cddata->TimerIrpList,
&heldIrpList->Tail.Overlay.ListEntry,
&cddata->TimerIrpSpinLock);
//
// Continue processing the held IRPs
//
heldIrpList = nextIrp;
}
}
BOOLEAN
NTAPI
CdRomCheckRegistryForMediaChangeValue(
IN PUNICODE_STRING RegistryPath,
IN ULONG DeviceNumber
)
/*++
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\Cdrom\Autorun:REG_DWORD:1.
The user can override the global setting to enable or disable Autorun on a
specific cdrom device by setting the key HKEY_LOCAL_MACHINE\System\
CurrentControlSet\Services\Cdrom\Device<N>\Autorun:REG_DWORD to one or zero.
(CURRENTLY UNIMPLEMENTED)
If this registry value does not exist or contains the value zero then
the timer to check for media change does not run.
Arguments:
RegistryPath - pointer to the unicode string inside
...\CurrentControlSet\Services\Cdrom
DeviceNumber - The number of the device object
Return Value:
TRUE - Autorun is enabled.
FALSE - no autorun.
--*/
{
#define ITEMS_TO_QUERY 2 /* always 1 greater than what is searched */
PRTL_QUERY_REGISTRY_TABLE parameters = NULL;
NTSTATUS status;
LONG zero = 0;
LONG tmp = 0;
LONG doRun = 0;
CHAR buf[32];
ANSI_STRING paramNum;
UNICODE_STRING paramStr;
UNICODE_STRING paramSuffix;
UNICODE_STRING paramPath;
UNICODE_STRING paramDevPath;
//
// First append \Parameters to the passed in registry path
//
RtlInitUnicodeString(&paramStr, L"\\Parameters");
RtlInitUnicodeString(&paramPath, NULL);
paramPath.MaximumLength = RegistryPath->Length +
paramStr.Length +
sizeof(WCHAR);
paramPath.Buffer = ExAllocatePool(PagedPool, paramPath.MaximumLength);
if(!paramPath.Buffer) {
DebugPrint((1,"CdRomCheckRegAP: couldn't allocate paramPath\n"));
return FALSE;
}
RtlZeroMemory(paramPath.Buffer, paramPath.MaximumLength);
RtlAppendUnicodeToString(&paramPath, RegistryPath->Buffer);
RtlAppendUnicodeToString(&paramPath, paramStr.Buffer);
DebugPrint((2, "CdRomCheckRegAP: paramPath [%d] = %ws\n",
paramPath.Length,
paramPath.Buffer));
//
// build a counted ANSI string that contains
// the suffix for the path
//
sprintf(buf, "\\Device%lu", DeviceNumber);
RtlInitAnsiString(&paramNum, buf);
//
// Next convert this into a unicode string
//
status = RtlAnsiStringToUnicodeString(&paramSuffix, &paramNum, TRUE);
if(!NT_SUCCESS(status)) {
DebugPrint((1,"CdRomCheckRegAP: couldn't convert paramNum to paramSuffix\n"));
ExFreePool(paramPath.Buffer);
return FALSE;
}
RtlInitUnicodeString(&paramDevPath, NULL);
//
// now build the device specific path
//
paramDevPath.MaximumLength = paramPath.Length +
paramSuffix.Length +
sizeof(WCHAR);
paramDevPath.Buffer = ExAllocatePool(PagedPool, paramDevPath.MaximumLength);
if(!paramDevPath.Buffer) {
RtlFreeUnicodeString(&paramSuffix);
ExFreePool(paramPath.Buffer);
return FALSE;
}
RtlZeroMemory(paramDevPath.Buffer, paramDevPath.MaximumLength);
RtlAppendUnicodeToString(&paramDevPath, paramPath.Buffer);
RtlAppendUnicodeToString(&paramDevPath, paramSuffix.Buffer);
DebugPrint((2, "CdRomCheckRegAP: paramDevPath [%d] = %ws\n",
paramPath.Length,
paramPath.Buffer));
parameters = ExAllocatePool(NonPagedPool,
sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY);
if (parameters) {
//
// Check for the Autorun value.
//
RtlZeroMemory(parameters,
(sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY));
parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
parameters[0].Name = L"Autorun";
parameters[0].EntryContext = &doRun;
parameters[0].DefaultType = REG_DWORD;
parameters[0].DefaultData = &zero;
parameters[0].DefaultLength = sizeof(ULONG);
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
RegistryPath->Buffer,
parameters,
NULL,
NULL);
DebugPrint((2, "CdRomCheckRegAP: cdrom/Autorun flag = %d\n", doRun));
RtlZeroMemory(parameters,
(sizeof(RTL_QUERY_REGISTRY_TABLE)*ITEMS_TO_QUERY));
parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
parameters[0].Name = L"Autorun";
parameters[0].EntryContext = &tmp;
parameters[0].DefaultType = REG_DWORD;
parameters[0].DefaultData = &doRun;
parameters[0].DefaultLength = sizeof(ULONG);
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
paramPath.Buffer,
parameters,
NULL,
NULL);
DebugPrint((2, "CdRomCheckRegAP: cdrom/parameters/autorun flag = %d\n", tmp));
RtlZeroMemory(parameters,
(sizeof(RTL_QUERY_REGISTRY_TABLE) * ITEMS_TO_QUERY));
parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
parameters[0].Name = L"Autorun";
parameters[0].EntryContext = &doRun;
parameters[0].DefaultType = REG_DWORD;
parameters[0].DefaultData = &tmp;
parameters[0].DefaultLength = sizeof(ULONG);
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
paramDevPath.Buffer,
parameters,
NULL,
NULL);
DebugPrint((1, "CdRomCheckRegAP: cdrom/parameters/device%d/autorun flag = %d\n", DeviceNumber, doRun));
ExFreePool(parameters);
}
ExFreePool(paramPath.Buffer);
ExFreePool(paramDevPath.Buffer);
RtlFreeUnicodeString(&paramSuffix);
DebugPrint((1, "CdRomCheckRegAP: Autoplay for device %d is %s\n",
DeviceNumber,
(doRun ? "on" : "off")));
if(doRun) {
return TRUE;
}
return FALSE;
}
BOOLEAN
NTAPI
IsThisASanyo(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR PathId,
IN UCHAR TargetId
)
/*++
Routine Description:
This routine is called by DriverEntry to determine whether a Sanyo 3-CD
changer device is present.
Arguments:
DeviceObject - Supplies the device object for the 'real' device.
PathId -
Return Value:
TRUE - if an Atapi changer device is found.
--*/
{
KEVENT event;
PIRP irp;
PCHAR inquiryBuffer;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
PSCSI_ADAPTER_BUS_INFO adapterInfo;
ULONG scsiBus;
PINQUIRYDATA inquiryData;
PSCSI_INQUIRY_DATA lunInfo;
inquiryBuffer = ExAllocatePool(NonPagedPool, 2048);
KeInitializeEvent(&event, NotificationEvent, FALSE);
irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_INQUIRY_DATA,
DeviceObject,
NULL,
0,
inquiryBuffer,
2048,
FALSE,
&event,
&ioStatus);
if (!irp) {
return FALSE;
}
status = IoCallDriver(DeviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS(status)) {
return FALSE;
}
adapterInfo = (PVOID) inquiryBuffer;
for (scsiBus=0; scsiBus < (ULONG)adapterInfo->NumberOfBuses; scsiBus++) {
//
// Get the SCSI bus scan data for this bus.
//
lunInfo = (PVOID) (inquiryBuffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
for (;;) {
if (lunInfo->PathId == PathId && lunInfo->TargetId == TargetId) {
inquiryData = (PVOID) lunInfo->InquiryData;
if (RtlCompareMemory(inquiryData->VendorId, "TORiSAN CD-ROM CDR-C", 20) == 20) {
ExFreePool(inquiryBuffer);
return TRUE;
}
ExFreePool(inquiryBuffer);
return FALSE;
}
if (!lunInfo->NextInquiryDataOffset) {
break;
}
lunInfo = (PVOID) (inquiryBuffer + lunInfo->NextInquiryDataOffset);
}
}
ExFreePool(inquiryBuffer);
return FALSE;
}
BOOLEAN
NTAPI
IsThisAnAtapiChanger(
IN PDEVICE_OBJECT DeviceObject,
OUT PULONG DiscsPresent
)
/*++
Routine Description:
This routine is called by DriverEntry to determine whether an Atapi
changer device is present.
Arguments:
DeviceObject - Supplies the device object for the 'real' device.
DiscsPresent - Supplies a pointer to the number of Discs supported by the changer.
Return Value:
TRUE - if an Atapi changer device is found.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
PMECHANICAL_STATUS_INFORMATION_HEADER mechanicalStatusBuffer;
NTSTATUS status;
SCSI_REQUEST_BLOCK srb;
PCDB cdb = (PCDB) &srb.Cdb[0];
BOOLEAN retVal = FALSE;
*DiscsPresent = 0;
//
// Some devices can't handle 12 byte CDB's gracefully
//
if(deviceExtension->DeviceFlags & DEV_NO_12BYTE_CDB) {
return FALSE;
}
//
// Build and issue the mechanical status command.
//
mechanicalStatusBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
sizeof(MECHANICAL_STATUS_INFORMATION_HEADER));
if (!mechanicalStatusBuffer) {
retVal = FALSE;
} else {
//
// Build and send the Mechanism status CDB.
//
RtlZeroMemory(&srb, sizeof(srb));
srb.CdbLength = 12;
srb.TimeOutValue = 20;
cdb->MECH_STATUS.OperationCode = SCSIOP_MECHANISM_STATUS;
cdb->MECH_STATUS.AllocationLength[1] = sizeof(MECHANICAL_STATUS_INFORMATION_HEADER);
status = ScsiClassSendSrbSynchronous(DeviceObject,
&srb,
mechanicalStatusBuffer,
sizeof(MECHANICAL_STATUS_INFORMATION_HEADER),
FALSE);
if (status == STATUS_SUCCESS) {
//
// Indicate number of slots available
//
*DiscsPresent = mechanicalStatusBuffer->NumberAvailableSlots;
if (*DiscsPresent > 1) {
retVal = TRUE;
} else {
//
// If only one disc, no need for this driver.
//
retVal = FALSE;
}
} else {
//
// Device doesn't support this command.
//
retVal = FALSE;
}
ExFreePool(mechanicalStatusBuffer);
}
return retVal;
}
BOOLEAN
NTAPI
IsThisAMultiLunDevice(
IN PDEVICE_OBJECT DeviceObject,
IN PDEVICE_OBJECT PortDeviceObject
)
/*++
Routine Description:
This routine is called to determine whether a multi-lun
device is present.
Arguments:
DeviceObject - Supplies the device object for the 'real' device.
Return Value:
TRUE - if a Multi-lun device is found.
--*/
{
PCHAR buffer;
PSCSI_INQUIRY_DATA lunInfo;
PSCSI_ADAPTER_BUS_INFO adapterInfo;
PDEVICE_EXTENSION deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
PINQUIRYDATA inquiryData;
ULONG scsiBus;
NTSTATUS status;
UCHAR lunCount = 0;
status = ScsiClassGetInquiryData(PortDeviceObject, (PSCSI_ADAPTER_BUS_INFO *) &buffer);
if (!NT_SUCCESS(status)) {
DebugPrint((1,"IsThisAMultiLunDevice: ScsiClassGetInquiryData failed\n"));
return FALSE;
}
adapterInfo = (PVOID) buffer;
//
// For each SCSI bus this adapter supports ...
//
for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) {
//
// Get the SCSI bus scan data for this bus.
//
lunInfo = (PVOID) (buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
inquiryData = (PVOID)lunInfo->InquiryData;
if ((lunInfo->PathId == deviceExtension->PathId) &&
(lunInfo->TargetId == deviceExtension->TargetId) &&
(inquiryData->DeviceType == READ_ONLY_DIRECT_ACCESS_DEVICE)) {
DebugPrint((1,"IsThisAMultiLunDevice: Vendor string is %.24s\n",
inquiryData->VendorId));
//
// If this device has more than one cdrom-type lun then we
// won't support autoplay on it
//
if (lunCount++) {
ExFreePool(buffer);
return TRUE;
}
}
//
// Get next LunInfo.
//
if (lunInfo->NextInquiryDataOffset == 0) {
break;
}
lunInfo = (PVOID) (buffer + lunInfo->NextInquiryDataOffset);
}
}
ExFreePool(buffer);
return FALSE;
}
IO_COMPLETION_ROUTINE CdRomUpdateGeometryCompletion;
NTSTATUS
NTAPI
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
--*/
{
PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK) Context;
PREAD_CAPACITY_DATA readCapacityBuffer;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
NTSTATUS status;
BOOLEAN retry;
ULONG_PTR retryCount;
ULONG lastSector;
PIRP originalIrp;
PCDROM_DATA cddata;
//
// Get items saved in the private IRP stack location.
//
irpStack = IoGetCurrentIrpStackLocation(Irp);
retryCount = (ULONG_PTR) irpStack->Parameters.Others.Argument1;
originalIrp = (PIRP) irpStack->Parameters.Others.Argument2;
if (!DeviceObject) {
DeviceObject = irpStack->DeviceObject;
}
ASSERT(DeviceObject);
deviceExtension = DeviceObject->DeviceExtension;
cddata = (PCDROM_DATA) (deviceExtension + 1);
readCapacityBuffer = srb->DataBuffer;
if ((NT_SUCCESS(Irp->IoStatus.Status)) && (SRB_STATUS(srb->SrbStatus) == SRB_STATUS_SUCCESS)) {
PFOUR_BYTE from;
PFOUR_BYTE to;
DebugPrint((2, "CdRomUpdateCapacityCompletion: [%lx] successful completion of buddy-irp %lx\n", originalIrp, Irp));
//
// Copy sector size from read capacity buffer to device extension
// in reverse byte order.
//
from = (PFOUR_BYTE) &readCapacityBuffer->BytesPerBlock;
to = (PFOUR_BYTE) &deviceExtension->DiskGeometry->Geometry.BytesPerSector;
to->Byte0 = from->Byte3;
to->Byte1 = from->Byte2;
to->Byte2 = from->Byte1;
to->Byte3 = from->Byte0;
//
// Using the new BytesPerBlock, calculate and store the SectorShift.
//
WHICH_BIT(deviceExtension->DiskGeometry->Geometry.BytesPerSector, deviceExtension->SectorShift);
//
// Copy last sector in reverse byte order.
//
from = (PFOUR_BYTE) &readCapacityBuffer->LogicalBlockAddress;
to = (PFOUR_BYTE) &lastSector;
to->Byte0 = from->Byte3;
to->Byte1 = from->Byte2;
to->Byte2 = from->Byte1;
to->Byte3 = from->Byte0;
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
//
// Calculate number of cylinders.
//
deviceExtension->DiskGeometry->Geometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1)/(32 * 64));
deviceExtension->PartitionLength.QuadPart =
(deviceExtension->PartitionLength.QuadPart << deviceExtension->SectorShift);
deviceExtension->DiskGeometry->Geometry.MediaType = RemovableMedia;
//
// Assume sectors per track are 32;
//
deviceExtension->DiskGeometry->Geometry.SectorsPerTrack = 32;
//
// Assume tracks per cylinder (number of heads) is 64.
//
deviceExtension->DiskGeometry->Geometry.TracksPerCylinder = 64;
} else {
DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] unsuccessful completion of buddy-irp %lx (status - %lx)\n", originalIrp, Irp, Irp->IoStatus.Status));
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
ScsiClassReleaseQueue(DeviceObject);
}
retry = ScsiClassInterpretSenseInfo(DeviceObject,
srb,
IRP_MJ_SCSI,
0,
retryCount,
&status);
if (retry) {
retryCount--;
if (retryCount) {
PCDB cdb;
DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] Retrying request %lx .. thread is %lx\n", originalIrp, Irp, Irp->Tail.Overlay.Thread));
//
// set up a one shot timer to get this process started over
//
irpStack->Parameters.Others.Argument1 = (PVOID) 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 = deviceExtension->TimeOutValue;
srb->SrbStatus = srb->ScsiStatus = 0;
srb->NextSrb = 0;
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
srb->PathId = deviceExtension->PathId;
srb->TargetId = deviceExtension->TargetId;
srb->Lun = deviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
srb->DataTransferLength = sizeof(READ_CAPACITY_DATA);
//
// Set up the CDB
//
cdb = (PCDB) &srb->Cdb[0];
cdb->CDB10.OperationCode = SCSIOP_READ_CAPACITY;
cdb->CDB10.LogicalUnitNumber = deviceExtension->Lun;
//
// Requests queued onto this list will be sent to the
// lower level driver during CdRomTickHandler
//
ExInterlockedInsertHeadList(&cddata->TimerIrpList,
&Irp->Tail.Overlay.ListEntry,
&cddata->TimerIrpSpinLock);
return STATUS_MORE_PROCESSING_REQUIRED;
} else {
//
// This has been bounced for a number of times. Error the
// original request.
//
originalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY_EX));
deviceExtension->DiskGeometry->Geometry.BytesPerSector = 2048;
deviceExtension->SectorShift = 11;
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
deviceExtension->DiskGeometry->Geometry.MediaType = RemovableMedia;
}
} else {
//
// Set up reasonable defaults
//
RtlZeroMemory(deviceExtension->DiskGeometry, sizeof(DISK_GEOMETRY_EX));
deviceExtension->DiskGeometry->Geometry.BytesPerSector = 2048;
deviceExtension->SectorShift = 11;
deviceExtension->PartitionLength.QuadPart = (LONGLONG)(0x7fffffff);
deviceExtension->DiskGeometry->Geometry.MediaType = RemovableMedia;
}
}
//
// Free resources held.
//
ExFreePool(srb->SenseInfoBuffer);
ExFreePool(srb->DataBuffer);
ExFreePool(srb);
if (Irp->MdlAddress) {
IoFreeMdl(Irp->MdlAddress);
}
IoFreeIrp(Irp);
if (originalIrp->Tail.Overlay.Thread) {
DebugPrint((2, "CdRomUpdateCapacityCompletion: [%lx] completing original IRP\n", originalIrp));
IoCompleteRequest(originalIrp, IO_DISK_INCREMENT);
} else {
DebugPrint((1, "CdRomUpdateCapacityCompletion: [%lx] original irp has "
"no thread\n",
originalIrp
));
}
//
// It's now safe to either start the next request or let the waiting ioctl
// request continue along it's merry way
//
IoStartNextPacket(DeviceObject, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
NTAPI
CdRomUpdateCapacity(
IN PDEVICE_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
--*/
{
PCDB cdb;
PIRP irp;
PSCSI_REQUEST_BLOCK srb;
PREAD_CAPACITY_DATA capacityBuffer;
PIO_STACK_LOCATION irpStack;
PUCHAR senseBuffer;
irp = IoAllocateIrp((CCHAR)(DeviceExtension->DeviceObject->StackSize+1),
FALSE);
if (irp) {
srb = ExAllocatePool(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK));
if (srb) {
capacityBuffer = ExAllocatePool(NonPagedPoolCacheAligned,
sizeof(READ_CAPACITY_DATA));
if (capacityBuffer) {
senseBuffer = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
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 = DeviceExtension->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->PathId = DeviceExtension->PathId;
srb->TargetId = DeviceExtension->TargetId;
srb->Lun = DeviceExtension->Lun;
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
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;
cdb->CDB10.LogicalUnitNumber = DeviceExtension->Lun;
//
// 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(DeviceExtension->PortDeviceObject, 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);
}
}
return STATUS_INSUFFICIENT_RESOURCES;
}
NTSTATUS
NTAPI
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 CdRomDeviceControl 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;
DebugPrint((2, "CdRomClassIoctlCompletion: setting event for irp %#08lx\n",
Irp
));
KeSetEvent(syncEvent, IO_DISK_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}