mirror of
https://github.com/reactos/reactos.git
synced 2024-08-25 04:38:43 +00:00
7360 lines
218 KiB
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(¶mStr, L"\\Parameters");
|
|
|
|
RtlInitUnicodeString(¶mPath, 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(¶mPath, RegistryPath->Buffer);
|
|
RtlAppendUnicodeToString(¶mPath, 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(¶mNum, buf);
|
|
|
|
//
|
|
// Next convert this into a unicode string
|
|
//
|
|
|
|
status = RtlAnsiStringToUnicodeString(¶mSuffix, ¶mNum, TRUE);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((1,"CdRomCheckRegAP: couldn't convert paramNum to paramSuffix\n"));
|
|
ExFreePool(paramPath.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlInitUnicodeString(¶mDevPath, 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(¶mSuffix);
|
|
ExFreePool(paramPath.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(paramDevPath.Buffer, paramDevPath.MaximumLength);
|
|
RtlAppendUnicodeToString(¶mDevPath, paramPath.Buffer);
|
|
RtlAppendUnicodeToString(¶mDevPath, 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(¶mSuffix);
|
|
|
|
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;
|
|
}
|