3981 lines
100 KiB
C
3981 lines
100 KiB
C
/*++
|
|
|
|
Copyright (c) 2000-2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cdrom.c
|
|
|
|
Abstract:
|
|
|
|
This module implements routines that apply to the CD-ROM object.
|
|
|
|
--*/
|
|
|
|
#include "idex.h"
|
|
|
|
#if !defined(ARCADE) || defined(DEVKIT)
|
|
|
|
//
|
|
// Single instance of the CD-ROM device object.
|
|
//
|
|
PDEVICE_OBJECT IdexCdRomDeviceObject;
|
|
|
|
//
|
|
// Name of the CD-ROM device object and its DOS devices symbolic link.
|
|
//
|
|
INITIALIZED_OBJECT_STRING_RDATA(IdexCdRomDeviceName, "\\Device\\CdRom0");
|
|
INITIALIZED_OBJECT_STRING_RDATA(IdexCdRomDosDevicesName, "\\??\\CdRom0:");
|
|
|
|
//
|
|
// Static transfer buffer used for IOCTLs that need to transfer a small number
|
|
// of bytes. Used to avoid adding pool allocation and free code to several code
|
|
// paths. The buffer is already locked due to being part of the kernel image.
|
|
//
|
|
UCHAR IdexCdRomStaticTransferBuffer[32];
|
|
|
|
//
|
|
// Number of bytes that are available on the single "partition" of the device
|
|
// media. The actual number is filled in when IOCTL_CDROM_GET_DRIVE_GEOMETRY is
|
|
// invoked.
|
|
//
|
|
LONGLONG IdexCdRomPartitionLength = _I64_MAX;
|
|
|
|
//
|
|
// Stores whether or not the disc has passed DVD-X2 authentication.
|
|
//
|
|
DECLSPEC_STICKY BOOLEAN IdexCdRomDVDX2Authenticated;
|
|
|
|
#if DBG
|
|
//
|
|
// Stores the last SCSI error read from the drive.
|
|
//
|
|
SENSE_DATA IdexCdRomDebugSenseData;
|
|
|
|
//
|
|
// Stores the number of errors that have occurred at the various spindle speeds.
|
|
//
|
|
ULONG IdexCdRomDebugErrors[DVDX2_SPINDLE_SPEED_MAXIMUM];
|
|
|
|
//
|
|
// Stores the number of sectors that have been transferred.
|
|
//
|
|
ULONG IdexCdRomDebugReadsFinished;
|
|
#endif
|
|
|
|
//
|
|
// Stores the maximum spindle speed requested by the title.
|
|
//
|
|
ULONG IdexCdRomMaximumSpindleSpeed = DVDX2_SPINDLE_SPEED_MAXIMUM;
|
|
|
|
//
|
|
// Stores the current spindle speed; this should be less than or equal to the
|
|
// maximum spindle speed. Note that this is sticky across boots so that if the
|
|
// spindle speed is reduced in one instance of the kernel, the next instance of
|
|
// the kernel can correct it.
|
|
//
|
|
DECLSPEC_STICKY ULONG IdexCdRomCurrentSpindleSpeed = DVDX2_SPINDLE_SPEED_MAXIMUM;
|
|
|
|
//
|
|
// Stores the last sector that was read from while the spindle was slowed down.
|
|
//
|
|
DECLSPEC_STICKY ULONG IdexCdRomSpindleSlowdownSectorNumber;
|
|
|
|
//
|
|
// Stores the number of sectors that must be transfered before allowing the
|
|
// spindle to speed back up.
|
|
//
|
|
DECLSPEC_STICKY ULONG IdexCdRomSpindleSlowdownSectorsRemaining;
|
|
|
|
//
|
|
// Stores the number of sectors that need to be crossed before we'll attempt to
|
|
// restore the spindle speed to the next faster spindle speed.
|
|
//
|
|
const ULONG IdexCdRomSpindleSpeedupTable[DVDX2_SPINDLE_SPEED_MAXIMUM + 1] = {
|
|
4096, // DVDX2_SPINDLE_SPEED_MINIMUM
|
|
8192, // DVDX2_SPINDLE_SPEED_MEDIUM
|
|
MAXULONG, // DVDX2_SPINDLE_SPEED_MAXIMUM
|
|
};
|
|
|
|
//
|
|
// Local support.
|
|
//
|
|
|
|
DECLSPEC_NORETURN
|
|
VOID
|
|
IdexCdRomFatalError(
|
|
IN ULONG ErrorCode
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT, IdexCdRomFatalError)
|
|
#pragma alloc_text(INIT, IdexCdRomCreate)
|
|
#endif
|
|
|
|
BOOLEAN
|
|
IdexCdRomPollResetComplete(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine polls the CD-ROM device to check for completion of a reset
|
|
sequence.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the device has completed its reset sequence.
|
|
|
|
--*/
|
|
{
|
|
UCHAR IdeStatus;
|
|
|
|
//
|
|
// Select the IDE device.
|
|
//
|
|
|
|
IdexProgramTargetDevice(IDE_CDROM_DEVICE_NUMBER);
|
|
|
|
//
|
|
// Check if the device is busy.
|
|
//
|
|
|
|
IdeStatus = IdexReadStatusPort();
|
|
|
|
if (IdexIsFlagClear(IdeStatus, IDE_STATUS_BSY)) {
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomResetDevice(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine software resets the CD-ROM device.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
IdexAssertIrqlAtChannelDIRQL();
|
|
|
|
//
|
|
// If the console is preparing to reset or shutdown, there's no reason to
|
|
// continue processing this request.
|
|
//
|
|
|
|
if (HalIsResetOrShutdownPending()) {
|
|
IdexLowerIrqlFromChannelDIRQL(DISPATCH_LEVEL);
|
|
IdexChannelAbortCurrentPacket();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Select the IDE device.
|
|
//
|
|
|
|
IdexProgramTargetDevice(IDE_CDROM_DEVICE_NUMBER);
|
|
|
|
//
|
|
// Issue the IDE command.
|
|
//
|
|
|
|
IdexWriteCommandPort(IDE_COMMAND_DEVICE_RESET);
|
|
|
|
//
|
|
// Lower the IRQL back to DISPATCH_LEVEL.
|
|
//
|
|
|
|
IdexLowerIrqlFromChannelDIRQL(DISPATCH_LEVEL);
|
|
|
|
//
|
|
// Speed up the timer to check for completion of the reset sequence.
|
|
//
|
|
|
|
IdexChannelSetTimerPeriod(IDE_FAST_TIMER_PERIOD);
|
|
|
|
//
|
|
// Delay for up to twenty-five seconds (100 milliseconds per unit).
|
|
//
|
|
|
|
IdexChannelObject.PollResetCompleteRoutine = IdexCdRomPollResetComplete;
|
|
IdexChannelObject.Timeout = 250;
|
|
}
|
|
|
|
BOOLEAN
|
|
IdexCdRomSelectDeviceAndSpinWhileBusy(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine selects the CD-ROM device and spins until the the IDE status
|
|
register's BSY bit is clear, which indicates that the device is ready to
|
|
accept a command.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the BSY bit was clear before timing out, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
ULONG Retries;
|
|
UCHAR IdeStatus;
|
|
|
|
IdexAssertIrqlAtChannelDIRQL();
|
|
|
|
//
|
|
// Select the IDE device.
|
|
//
|
|
|
|
IdexProgramTargetDevice(IDE_CDROM_DEVICE_NUMBER);
|
|
|
|
//
|
|
// Spin for up to a tenth second.
|
|
//
|
|
|
|
for (Retries = 1000; Retries > 0; Retries--) {
|
|
|
|
IdeStatus = IdexReadStatusPort();
|
|
|
|
if (IdexIsFlagClear(IdeStatus, IDE_STATUS_BSY)) {
|
|
return TRUE;
|
|
}
|
|
|
|
KeStallExecutionProcessor(100);
|
|
}
|
|
|
|
//
|
|
// Lower the IRQL back to DISPATCH_LEVEL.
|
|
//
|
|
|
|
IdexLowerIrqlFromChannelDIRQL(DISPATCH_LEVEL);
|
|
|
|
//
|
|
// Mark the IRP as timed out. The caller will pass this IRP to the generic
|
|
// error handler which will reset the device and retry the IRP if allowed.
|
|
//
|
|
|
|
IdexChannelObject.CurrentIrp->IoStatus.Status = STATUS_IO_TIMEOUT;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
IdexCdRomWritePacket(
|
|
PCDB Cdb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine selects the CD-ROM device and spins until the the IDE status
|
|
register's BSY bit is clear, which indicates that the device is ready to
|
|
accept a command.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the BSY bit was clear before timing out, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
ULONG Retries;
|
|
UCHAR IdeStatus;
|
|
|
|
IdexAssertIrqlAtChannelDIRQL();
|
|
|
|
//
|
|
// Issue the IDE command.
|
|
//
|
|
|
|
IdexWriteCommandPort(IDE_COMMAND_PACKET);
|
|
|
|
//
|
|
// Spin until the device indicates that it's ready to receive the SCSI CDB.
|
|
//
|
|
|
|
for (Retries = 1000; Retries > 0; Retries--) {
|
|
|
|
IdeStatus = IdexReadStatusPort();
|
|
|
|
if (IdexIsFlagClear(IdeStatus, IDE_STATUS_BSY) &&
|
|
IdexIsFlagSet(IdeStatus, IDE_STATUS_DRQ)) {
|
|
|
|
//
|
|
// Issue the SCSI CDB.
|
|
//
|
|
|
|
IdexWriteDataPortCdb(Cdb);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
KeStallExecutionProcessor(100);
|
|
}
|
|
|
|
//
|
|
// Lower the IRQL back to DISPATCH_LEVEL.
|
|
//
|
|
|
|
IdexLowerIrqlFromChannelDIRQL(DISPATCH_LEVEL);
|
|
|
|
//
|
|
// Mark the IRP as timed out. The caller will pass this IRP to the generic
|
|
// error handler which will reset the device and retry the IRP if allowed.
|
|
//
|
|
|
|
IdexChannelObject.CurrentIrp->IoStatus.Status = STATUS_IO_TIMEOUT;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomNoTransferInterrupt(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when a hardware interrupt occurs on the channel's
|
|
IRQ and the pending interrupt IRP is for an ATA request that transfers data.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
UCHAR IdeStatus;
|
|
UCHAR InterruptReason;
|
|
PIRP Irp;
|
|
|
|
//
|
|
// Read the status register to dismiss the interrupt.
|
|
//
|
|
|
|
IdeStatus = IdexReadStatusPort();
|
|
|
|
//
|
|
// Verify that the interrupt reason indicates that the command is complete.
|
|
//
|
|
|
|
InterruptReason = IdexReadInterruptReasonPort();
|
|
|
|
if ((InterruptReason & (IDE_INTERRUPT_REASON_IO | IDE_INTERRUPT_REASON_CD)) !=
|
|
(IDE_INTERRUPT_REASON_IO | IDE_INTERRUPT_REASON_CD)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Clear the interrupt routine and grab the current IRP.
|
|
//
|
|
|
|
IdexChannelObject.InterruptRoutine = NULL;
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
|
|
//
|
|
// Set the status code as appropriate.
|
|
//
|
|
|
|
if (IdexIsFlagSet(IdeStatus, IDE_STATUS_ERR)) {
|
|
Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
|
|
} else {
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Finish processing the IRP at DPC level.
|
|
//
|
|
|
|
KeInsertQueueDpc(&IdexChannelObject.FinishDpc, NULL, NULL);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomTransferInterrupt(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when a hardware interrupt occurs on the channel's
|
|
IRQ and the pending interrupt IRP is for an ATA request that transfers data.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
UCHAR BusMasterStatus;
|
|
UCHAR IdeStatus;
|
|
UCHAR InterruptReason;
|
|
PIRP Irp;
|
|
|
|
//
|
|
// Read the bus master status register and verify that the interrupt bit is
|
|
// set if we haven't already seen the bus master interrupt.
|
|
//
|
|
|
|
BusMasterStatus = IdexReadBusMasterStatusPort();
|
|
|
|
//
|
|
// Read the status register to dismiss the interrupt.
|
|
//
|
|
|
|
IdeStatus = IdexReadStatusPort();
|
|
|
|
//
|
|
// Verify that the interrupt reason indicates that the command is complete.
|
|
//
|
|
|
|
InterruptReason = IdexReadInterruptReasonPort();
|
|
|
|
if (IdexChannelObject.ExpectingBusMasterInterrupt) {
|
|
|
|
if (IdexIsFlagClear(BusMasterStatus, IDE_BUS_MASTER_STATUS_INTERRUPT)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Deactivate the bus master interface and dismiss the interrupt.
|
|
//
|
|
|
|
IdexWriteBusMasterCommandPort(0);
|
|
IdexWriteBusMasterStatusPort(IDE_BUS_MASTER_STATUS_INTERRUPT);
|
|
|
|
IdexChannelObject.ExpectingBusMasterInterrupt = FALSE;
|
|
}
|
|
|
|
if ((InterruptReason & (IDE_INTERRUPT_REASON_IO | IDE_INTERRUPT_REASON_CD)) !=
|
|
(IDE_INTERRUPT_REASON_IO | IDE_INTERRUPT_REASON_CD)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Clear the interrupt routine and grab the current IRP.
|
|
//
|
|
|
|
IdexChannelObject.InterruptRoutine = NULL;
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
|
|
//
|
|
// Set the status code as appropriate.
|
|
//
|
|
|
|
if (IdexIsFlagSet(IdeStatus, IDE_STATUS_ERR) ||
|
|
IdexIsFlagSet(BusMasterStatus, IDE_BUS_MASTER_STATUS_ERROR)) {
|
|
Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
|
|
} else if (IdexIsFlagSet(BusMasterStatus, IDE_BUS_MASTER_STATUS_ACTIVE)) {
|
|
Irp->IoStatus.Status = STATUS_DATA_OVERRUN;
|
|
} else {
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Finish processing the IRP at DPC level.
|
|
//
|
|
|
|
KeInsertQueueDpc(&IdexChannelObject.FinishDpc, NULL, NULL);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomIssueAtapiRequest(
|
|
IN PCDB Cdb,
|
|
IN PVOID TransferBuffer,
|
|
IN ULONG TransferLength,
|
|
IN BOOLEAN DataOut,
|
|
IN PIDE_FINISHIO_ROUTINE FinishIoRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine issues the supplied ATAPI request.
|
|
|
|
Arguments:
|
|
|
|
Cdb - Specifies the SCSI CDB to issue.
|
|
|
|
TransferBuffer - Specifies the pointer to the input or output buffer.
|
|
|
|
TransferLength - Specifies the number of bytes to transfer.
|
|
|
|
DataOut - Specifies the direction of the data transfer.
|
|
|
|
FinishIoRoutine - Specifies the routine to invoke on completion of the ATAPI
|
|
request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Synchronize execution with the interrupt service routine.
|
|
//
|
|
|
|
IdexRaiseIrqlToChannelDIRQLFromDPCLevel();
|
|
|
|
//
|
|
// Select the IDE device and spin until the device is not busy.
|
|
//
|
|
|
|
if (!IdexCdRomSelectDeviceAndSpinWhileBusy()) {
|
|
FinishIoRoutine();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Prepare the bus master interface for the DMA transfer.
|
|
//
|
|
|
|
if (TransferLength != 0) {
|
|
IdexChannelPrepareBufferTransfer(TransferBuffer, TransferLength);
|
|
IdexWriteFeaturesPort(IDE_FEATURE_DMA);
|
|
} else {
|
|
IdexWriteFeaturesPort(0);
|
|
}
|
|
|
|
//
|
|
// Write out the IDE command and the SCSI CDB.
|
|
//
|
|
|
|
if (!IdexCdRomWritePacket(Cdb)) {
|
|
FinishIoRoutine();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Activate the bus master interface.
|
|
//
|
|
|
|
if (TransferLength != 0) {
|
|
if (DataOut) {
|
|
IdexWriteBusMasterCommandPort(IDE_BUS_MASTER_COMMAND_START);
|
|
} else {
|
|
IdexWriteBusMasterCommandPort(IDE_BUS_MASTER_COMMAND_START |
|
|
IDE_BUS_MASTER_COMMAND_READ);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Indicate that we're expecting an interrupt for this IRP.
|
|
//
|
|
|
|
ASSERT(IdexChannelObject.InterruptRoutine == NULL);
|
|
|
|
IdexChannelObject.InterruptRoutine = (TransferLength != 0) ?
|
|
IdexCdRomTransferInterrupt : IdexCdRomNoTransferInterrupt;
|
|
IdexChannelObject.FinishIoRoutine = FinishIoRoutine;
|
|
IdexChannelObject.ExpectingBusMasterInterrupt = TRUE;
|
|
IdexChannelObject.Timeout = IDE_ATAPI_DEFAULT_TIMEOUT;
|
|
|
|
IdexLowerIrqlFromChannelDIRQL(DISPATCH_LEVEL);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomSetSpindleSpeed(
|
|
IN ULONG SpindleSpeedControl,
|
|
IN PIDE_FINISHIO_ROUTINE FinishIoRoutine
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prepares a spindle speed ATAPI request and issues the request.
|
|
|
|
Arguments:
|
|
|
|
SpindleSpeedControl - Specifies the desired speed of the spindle.
|
|
|
|
FinishIoRoutine - Specifies the routine to invoke on completion of the ATAPI
|
|
request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDVDX2_ADVANCED_DRIVE_CONTROL AdvancedDriveControl;
|
|
CDB Cdb;
|
|
|
|
ASSERT(SpindleSpeedControl <= IdexCdRomMaximumSpindleSpeed);
|
|
|
|
IdexDbgPrint(("IDEX: setting spindle speed to %d.\n", SpindleSpeedControl));
|
|
|
|
//
|
|
// Change the current spindle speed to the requested spindle speed. Note
|
|
// that if the drive fails to handle the below ATAPI request, the software
|
|
// state won't match the hardware state, but that's not likely to occur and
|
|
// won't cause any problems for the drive or driver, so we'll ignore that
|
|
// possibility.
|
|
//
|
|
|
|
IdexCdRomCurrentSpindleSpeed = SpindleSpeedControl;
|
|
|
|
//
|
|
// If spindle speed change is part of the error recovery path in this driver
|
|
// and not an explicit call to IOCTL_CDROM_SET_SPINDLE_SPEED, then we need
|
|
// to keep track of the number of sectors where we'll apply the new spindle
|
|
// speed before we increase to the new spindle speed.
|
|
//
|
|
|
|
IdexCdRomSpindleSlowdownSectorsRemaining =
|
|
IdexCdRomSpindleSpeedupTable[SpindleSpeedControl];
|
|
|
|
//
|
|
// Prepare the advanced drive control page.
|
|
//
|
|
|
|
ASSERT(sizeof(IdexCdRomStaticTransferBuffer) >= sizeof(DVDX2_ADVANCED_DRIVE_CONTROL));
|
|
|
|
AdvancedDriveControl = (PDVDX2_ADVANCED_DRIVE_CONTROL)IdexCdRomStaticTransferBuffer;
|
|
|
|
RtlZeroMemory(AdvancedDriveControl, sizeof(DVDX2_ADVANCED_DRIVE_CONTROL));
|
|
|
|
*((PUSHORT)&AdvancedDriveControl->Header.ModeDataLength) =
|
|
IdexConstantUshortByteSwap(sizeof(DVDX2_ADVANCED_DRIVE_CONTROL) -
|
|
FIELD_OFFSET(DVDX2_ADVANCED_DRIVE_CONTROL, Header.MediumType));
|
|
AdvancedDriveControl->AdvancedDriveControlPage.PageCode =
|
|
DVDX2_MODE_PAGE_ADVANCED_DRIVE_CONTROL;
|
|
AdvancedDriveControl->AdvancedDriveControlPage.PageLength =
|
|
sizeof(DVDX2_ADVANCED_DRIVE_CONTROL_PAGE) -
|
|
FIELD_OFFSET(DVDX2_ADVANCED_DRIVE_CONTROL_PAGE, SpindleSpeedControl);
|
|
AdvancedDriveControl->AdvancedDriveControlPage.SpindleSpeedControl =
|
|
(UCHAR)SpindleSpeedControl;
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.MODE_SENSE10.OperationCode = SCSIOP_MODE_SELECT10;
|
|
Cdb.MODE_SENSE10.PageCode = DVDX2_MODE_PAGE_ADVANCED_DRIVE_CONTROL;
|
|
*((PUSHORT)&Cdb.MODE_SENSE10.AllocationLength) =
|
|
(USHORT)IdexConstantUshortByteSwap(sizeof(DVDX2_ADVANCED_DRIVE_CONTROL));
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, IdexCdRomStaticTransferBuffer,
|
|
sizeof(DVDX2_ADVANCED_DRIVE_CONTROL), TRUE, FinishIoRoutine);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishSpeedReduction(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing after reducing
|
|
the spindle speed as a result of an error from the drive.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Restart the current packet in around 300ms.
|
|
//
|
|
|
|
IdexChannelSetTimerPeriod(IDE_FAST_TIMER_PERIOD);
|
|
|
|
IdexChannelObject.TimeoutExpiredRoutine = IdexChannelRestartCurrentPacket;
|
|
IdexChannelObject.Timeout = 3;
|
|
}
|
|
|
|
BOOLEAN
|
|
IdexCdRomSpeedReductionRequested(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when the drive has returned a sense code indicating
|
|
that cache errors have been detected and a speed reduction is requested.
|
|
|
|
If a speed reduction is initiated, then the current IRP will be retried at
|
|
the lower spindle speed.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if a speed reduction ATAPI request has been issued, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
//
|
|
// If the current spindle speed is already at the minimum value, then do
|
|
// nothing.
|
|
//
|
|
|
|
if (IdexCdRomCurrentSpindleSpeed != DVDX2_SPINDLE_SPEED_MINIMUM) {
|
|
|
|
//
|
|
// If this is not a read request, then do nothing.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(IdexChannelObject.CurrentIrp);
|
|
|
|
if (IrpSp->MajorFunction == IRP_MJ_READ) {
|
|
|
|
//
|
|
// Remember the sector number where we last slowed down the drive.
|
|
//
|
|
|
|
IdexCdRomSpindleSlowdownSectorNumber =
|
|
IrpSp->Parameters.IdexReadWrite.SectorNumber;
|
|
|
|
//
|
|
// Reduce the spindle speed by another notch and then restart the
|
|
// current packet.
|
|
//
|
|
|
|
IdexCdRomSetSpindleSpeed(IdexCdRomCurrentSpindleSpeed - 1,
|
|
IdexCdRomFinishSpeedReduction);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishSpindleSpeedRestore(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing after restoring
|
|
the spindle speed to the maximum value.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
//
|
|
// Don't use IdexChannelRestartCurrentPacket in order to avoid incrementing
|
|
// the number of retries for this packet.
|
|
//
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Invoke the driver's StartIo routine to start the IRP.
|
|
//
|
|
|
|
IrpSp->DeviceObject->DriverObject->DriverStartIo(IrpSp->DeviceObject, Irp);
|
|
}
|
|
|
|
BOOLEAN
|
|
IdexCdRomCheckForRestoreSpindleSpeed(
|
|
IN ULONG StartingSectorNumber,
|
|
IN ULONG NumberOfSectors
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the supplied sector number is outside of the range of
|
|
sectors that caused the spindle speed of the drive to be reduced.
|
|
|
|
Arguments:
|
|
|
|
StartingSectorNumber - Specifies the starting sector number of the current
|
|
read request.
|
|
|
|
NumberOfSectors - Specifies the number of sectors for the current read
|
|
request.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the spindle speed is being restored, else FALSE.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(IdexCdRomCurrentSpindleSpeed < IdexCdRomMaximumSpindleSpeed);
|
|
|
|
//
|
|
// Reduce the number of sectors that must be read before attempting to
|
|
// increase the spindle speed. This check is to prevent scenerios where
|
|
// reading the same block of data over and over keeps the spindle speed from
|
|
// ever being restored.
|
|
//
|
|
|
|
if (NumberOfSectors < IdexCdRomSpindleSlowdownSectorsRemaining) {
|
|
IdexCdRomSpindleSlowdownSectorsRemaining -= NumberOfSectors;
|
|
} else {
|
|
IdexCdRomSpindleSlowdownSectorsRemaining = 0;
|
|
}
|
|
|
|
//
|
|
// Check if we're still inside the sector range that caused us to last
|
|
// slowdown and that we haven't run the count of transfered sectors down to
|
|
// zero.
|
|
//
|
|
|
|
if ((IdexCdRomSpindleSlowdownSectorsRemaining != 0) &&
|
|
(StartingSectorNumber >= IdexCdRomSpindleSlowdownSectorNumber) &&
|
|
(StartingSectorNumber < IdexCdRomSpindleSlowdownSectorNumber +
|
|
IdexCdRomSpindleSpeedupTable[IdexCdRomCurrentSpindleSpeed])) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Remember the sector number where we last speed up the drive.
|
|
//
|
|
|
|
IdexCdRomSpindleSlowdownSectorNumber = StartingSectorNumber;
|
|
|
|
//
|
|
// We're outside of the slowdown sector range. Attempt to restore the
|
|
// spindle speed back to the maximum value.
|
|
//
|
|
|
|
IdexCdRomSetSpindleSpeed(IdexCdRomCurrentSpindleSpeed + 1,
|
|
IdexCdRomFinishSpindleSpeedRestore);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishRequestSense(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing a request to read
|
|
the sense data from the drive.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PSENSE_DATA SenseData;
|
|
NTSTATUS status;
|
|
BOOLEAN Retry;
|
|
BOOLEAN DelayedRetry;
|
|
BOOLEAN AdjustSpindleSpeed;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
|
|
#if DBG
|
|
//
|
|
// Keep track of the number of errors that have occurred at the various
|
|
// spindle speeds.
|
|
//
|
|
|
|
IdexCdRomDebugErrors[IdexCdRomCurrentSpindleSpeed]++;
|
|
#endif
|
|
|
|
//
|
|
// If we're unable to get the correct sense information, give up and just
|
|
// complete the IRP with STATUS_IO_DEVICE_ERROR.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
Irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
|
|
goto CompleteRequest;
|
|
}
|
|
|
|
//
|
|
// Interpret and process the sense data.
|
|
//
|
|
|
|
SenseData = (PSENSE_DATA)IdexCdRomStaticTransferBuffer;
|
|
status = STATUS_IO_DEVICE_ERROR;
|
|
Retry = FALSE;
|
|
DelayedRetry = FALSE;
|
|
AdjustSpindleSpeed = FALSE;
|
|
|
|
switch (SenseData->SenseKey) {
|
|
|
|
case SCSI_SENSE_NO_SENSE:
|
|
if (SenseData->IncorrectLength) {
|
|
status = STATUS_INVALID_BLOCK_LENGTH;
|
|
} else {
|
|
Retry = TRUE;
|
|
}
|
|
break;
|
|
|
|
case SCSI_SENSE_RECOVERED_ERROR:
|
|
if (SenseData->IncorrectLength) {
|
|
status = STATUS_INVALID_BLOCK_LENGTH;
|
|
} else if (SenseData->AdditionalSenseCode ==
|
|
DVDX2_ADSENSE_SPEED_REDUCTION_REQUESTED) {
|
|
Retry = TRUE;
|
|
AdjustSpindleSpeed = TRUE;
|
|
} else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case SCSI_SENSE_NOT_READY:
|
|
status = STATUS_DEVICE_NOT_READY;
|
|
|
|
switch (SenseData->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_LUN_NOT_READY:
|
|
Retry = TRUE;
|
|
DelayedRetry = TRUE;
|
|
break;
|
|
|
|
case SCSI_ADSENSE_INVALID_MEDIA:
|
|
status = STATUS_UNRECOGNIZED_MEDIA;
|
|
break;
|
|
|
|
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE:
|
|
status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SCSI_SENSE_MEDIUM_ERROR:
|
|
status = STATUS_DEVICE_DATA_ERROR;
|
|
|
|
switch (SenseData->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_INVALID_MEDIA:
|
|
status = STATUS_UNRECOGNIZED_MEDIA;
|
|
break;
|
|
|
|
case DVDX2_ADSENSE_GENERAL_READ_ERROR:
|
|
Retry = TRUE;
|
|
|
|
if (IdexChannelObject.IoRetries != 0) {
|
|
AdjustSpindleSpeed = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
switch (SenseData->AdditionalSenseCode) {
|
|
|
|
case SCSI_ADSENSE_ILLEGAL_BLOCK:
|
|
status = STATUS_NONEXISTENT_SECTOR;
|
|
break;
|
|
|
|
case SCSI_ADSENSE_INVALID_MEDIA:
|
|
status = STATUS_UNRECOGNIZED_MEDIA;
|
|
break;
|
|
|
|
case SCSI_ADSENSE_COPY_PROTECTION_FAILURE:
|
|
case DVDX2_ADSENSE_COPY_PROTECTION_FAILURE:
|
|
status = STATUS_TOO_MANY_SECRETS;
|
|
break;
|
|
|
|
case DVDX2_ADSENSE_COMMAND_ERROR:
|
|
Retry = TRUE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
Retry = TRUE;
|
|
|
|
if (SenseData->AdditionalSenseCode == DVDX2_ADSENSE_INSUFFICIENT_TIME) {
|
|
AdjustSpindleSpeed = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
Retry = TRUE;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
#if DBG
|
|
//
|
|
// Keep a copy of the the last sense data for debugging purposes.
|
|
//
|
|
|
|
RtlCopyMemory(&IdexCdRomDebugSenseData, SenseData, sizeof(SENSE_DATA));
|
|
#endif
|
|
|
|
//
|
|
// Retry the request if possible.
|
|
//
|
|
|
|
if (Retry &&
|
|
(IdexChannelObject.IoRetries < IdexChannelObject.MaximumIoRetries)) {
|
|
|
|
//
|
|
// If this isn't a retry associated with a spindle speed reduction, then
|
|
// restart the current packet and bail out now.
|
|
//
|
|
|
|
if (!AdjustSpindleSpeed) {
|
|
|
|
if (!DelayedRetry) {
|
|
IdexChannelRestartCurrentPacket();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Restart the current packet in around 800ms.
|
|
//
|
|
|
|
IdexChannelSetTimerPeriod(IDE_FAST_TIMER_PERIOD);
|
|
|
|
IdexChannelObject.TimeoutExpiredRoutine = IdexChannelRestartCurrentPacket;
|
|
IdexChannelObject.Timeout = 8;
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Otherwise, try to slow down the spindle. If the spindle cannot be
|
|
// slowed down, then abandon the retry and fall into the non-retry path.
|
|
//
|
|
|
|
if (IdexCdRomSpeedReductionRequested()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the request is a SCSI pass through request and the caller has supplied
|
|
// an output buffer, then copy the sense data to the caller's output buffer
|
|
// and return success. The caller checks the number of bytes written to the
|
|
// output buffer to know whether or not an error has actually occurred.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
if ((IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) &&
|
|
(IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SCSI_PASS_THROUGH_DIRECT) &&
|
|
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
|
|
|
|
RtlCopyMemory(Irp->UserBuffer, SenseData, sizeof(SENSE_DATA));
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(SENSE_DATA);
|
|
}
|
|
|
|
//
|
|
// The request cannot be retried or the retry count has exceeded the limit, so
|
|
// complete the IRP and start the next request.
|
|
//
|
|
|
|
CompleteRequest:
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
IdexChannelStartNextPacket();
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishGeneric(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to generically finish processing a
|
|
request after its hardware interrupt has fired or after the IDE command has
|
|
timed out.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
CDB Cdb;
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
|
|
if (Irp->IoStatus.Status == STATUS_IO_DEVICE_ERROR) {
|
|
|
|
//
|
|
// The command completed with an error. Request the sense data so that
|
|
// we can return a more useful error to the caller.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE;
|
|
Cdb.CDB6INQUIRY.AllocationLength = sizeof(SENSE_DATA);
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, IdexCdRomStaticTransferBuffer,
|
|
sizeof(SENSE_DATA), FALSE, IdexCdRomFinishRequestSense);
|
|
|
|
return;
|
|
|
|
} else if (Irp->IoStatus.Status == STATUS_IO_TIMEOUT) {
|
|
|
|
//
|
|
// Attempt to reset the device. If the reset completes successfully and
|
|
// the retry count has not been exceeded the maximum retry count, then
|
|
// the IRP will be restarted.
|
|
//
|
|
|
|
IdexRaiseIrqlToChannelDIRQLFromDPCLevel();
|
|
|
|
IdexCdRomResetDevice();
|
|
|
|
return;
|
|
}
|
|
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
IdexChannelStartNextPacket();
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishGenericWithOverrun(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to generically finish processing a
|
|
request after its hardware interrupt has fired or after the IDE command has
|
|
timed out.
|
|
|
|
If the request completed with STATUS_DATA_OVERRUN, then this is converted
|
|
to STATUS_SUCCESS.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
|
|
//
|
|
// The media may not have enough data to fill the output buffer, but this
|
|
// isn't an error.
|
|
//
|
|
|
|
if (Irp->IoStatus.Status == STATUS_DATA_OVERRUN) {
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Jump to the generic handler to process errors.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
IdexCdRomFinishGeneric();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Complete the request and start the next packet.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
IdexChannelStartNextPacket();
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartReadTOC(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_CDROM_READ_TOC and
|
|
IOCTL_CDROM_GET_LAST_SESSION requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
ULONG TransferLength;
|
|
CDB Cdb;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
TransferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
//
|
|
// Verify that the output buffer and length is non-zero and has the proper
|
|
// alignment requirement.
|
|
//
|
|
|
|
if ((TransferLength == 0) ||
|
|
((TransferLength & IDE_ALIGNMENT_REQUIREMENT) != 0) ||
|
|
((PtrToUlong(Irp->UserBuffer) & IDE_ALIGNMENT_REQUIREMENT) != 0)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The maximum transfer length for the TOC is sizeof(CDROM_TOC).
|
|
//
|
|
|
|
if (TransferLength > sizeof(CDROM_TOC)) {
|
|
TransferLength = sizeof(CDROM_TOC);
|
|
}
|
|
|
|
//
|
|
// Lock the user's buffer into memory if necessary.
|
|
//
|
|
|
|
IoLockUserBuffer(Irp, TransferLength);
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = TransferLength;
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.READ_TOC.OperationCode = SCSIOP_READ_TOC;
|
|
*((PUSHORT)Cdb.READ_TOC.AllocationLength) = RtlUshortByteSwap((USHORT)TransferLength);
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) {
|
|
Cdb.READ_TOC.Msf = CDB_USE_MSF;
|
|
} else {
|
|
Cdb.READ_TOC.Format = GET_LAST_SESSION;
|
|
}
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, Irp->UserBuffer, TransferLength, FALSE,
|
|
IdexCdRomFinishGenericWithOverrun);
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartCheckVerify(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_CDROM_CHECK_VERIFY requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
CDB Cdb;
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
|
|
|
|
//
|
|
// No retries are allowed for this IRP.
|
|
//
|
|
|
|
IdexChannelObject.MaximumIoRetries = IDE_NO_RETRY_COUNT;
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, NULL, 0, FALSE, IdexCdRomFinishGeneric);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishGetDriveGeometry(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing a
|
|
IOCTL_CDROM_GET_DRIVE_GEOMETRY request.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PREAD_CAPACITY_DATA ReadCapacity;
|
|
ULONG NumberOfSectors;
|
|
PDISK_GEOMETRY DiskGeometry;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
|
|
//
|
|
// Jump to the generic handler to process errors.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
IdexCdRomFinishGeneric();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Compute the drive's geometry.
|
|
//
|
|
|
|
ReadCapacity = (PREAD_CAPACITY_DATA)IdexCdRomStaticTransferBuffer;
|
|
NumberOfSectors = RtlUlongByteSwap(ReadCapacity->LogicalBlockAddress) + 1;
|
|
IdexCdRomPartitionLength = (ULONGLONG)NumberOfSectors * IDE_ATAPI_CD_SECTOR_SIZE;
|
|
|
|
DiskGeometry = (PDISK_GEOMETRY)Irp->UserBuffer;
|
|
DiskGeometry->Cylinders.QuadPart = NumberOfSectors;
|
|
DiskGeometry->MediaType = RemovableMedia;
|
|
DiskGeometry->TracksPerCylinder = 1;
|
|
DiskGeometry->SectorsPerTrack = 1;
|
|
DiskGeometry->BytesPerSector = IDE_ATAPI_CD_SECTOR_SIZE;
|
|
|
|
//
|
|
// Complete the request and start the next packet.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
IdexChannelStartNextPacket();
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartGetDriveGeometry(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_CDROM_GET_DRIVE_GEOMETRY requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
CDB Cdb;
|
|
|
|
ASSERT(sizeof(IdexCdRomStaticTransferBuffer) >= sizeof(READ_CAPACITY_DATA));
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = sizeof(DISK_GEOMETRY);
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.CDB6GENERIC.OperationCode = SCSIOP_READ_CAPACITY;
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, IdexCdRomStaticTransferBuffer,
|
|
sizeof(READ_CAPACITY_DATA), FALSE, IdexCdRomFinishGetDriveGeometry);
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartRawRead(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_CDROM_RAW_READ requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PRAW_READ_INFO RawReadInfo;
|
|
ULONG StartingSector;
|
|
ULONG TransferLength;
|
|
CDB Cdb;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the input buffer is large enough.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(RAW_READ_INFO)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
RawReadInfo = (PRAW_READ_INFO)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Compute the logical block address and transfer length for the request.
|
|
//
|
|
|
|
StartingSector = (ULONG)(RawReadInfo->DiskOffset.QuadPart >>
|
|
IDE_ATAPI_CD_SECTOR_SHIFT);
|
|
TransferLength = RawReadInfo->SectorCount * IDE_ATAPI_RAW_CD_SECTOR_SIZE;
|
|
|
|
//
|
|
// Verify that the parameters are valid. The requested read length must be
|
|
// less than the maximum transfer size, the ending offset must be in within
|
|
// the bounds of the disk, the output buffer must have enough bytes to
|
|
// contain the read length, and the output buffer must have the proper
|
|
// alignment requirement.
|
|
//
|
|
|
|
if ((RawReadInfo->SectorCount > (IDE_ATAPI_MAXIMUM_TRANSFER_BYTES / IDE_ATAPI_RAW_CD_SECTOR_SIZE) + 1) ||
|
|
(TransferLength > IDE_ATAPI_MAXIMUM_TRANSFER_BYTES) ||
|
|
((RawReadInfo->DiskOffset.QuadPart + TransferLength) > IdexCdRomPartitionLength) ||
|
|
((PtrToUlong(Irp->UserBuffer) & IDE_ALIGNMENT_REQUIREMENT) != 0)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = TransferLength;
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.READ_CD.OperationCode = SCSIOP_READ_CD;
|
|
*((PULONG)Cdb.READ_CD.StartingLBA) = RtlUlongByteSwap(StartingSector);
|
|
Cdb.READ_CD.TransferBlocks[2] = (UCHAR)(RawReadInfo->SectorCount);
|
|
Cdb.READ_CD.TransferBlocks[1] = (UCHAR)(RawReadInfo->SectorCount >> 8);
|
|
Cdb.READ_CD.TransferBlocks[0] = (UCHAR)(RawReadInfo->SectorCount >> 16);
|
|
|
|
switch (RawReadInfo->TrackMode) {
|
|
|
|
case CDDA:
|
|
Cdb.READ_CD.ExpectedSectorType = 1;
|
|
Cdb.READ_CD.IncludeUserData = 1;
|
|
Cdb.READ_CD.HeaderCode = 3;
|
|
Cdb.READ_CD.IncludeSyncData = 1;
|
|
break;
|
|
|
|
case YellowMode2:
|
|
Cdb.READ_CD.ExpectedSectorType = 3;
|
|
Cdb.READ_CD.IncludeUserData = 1;
|
|
Cdb.READ_CD.HeaderCode = 1;
|
|
Cdb.READ_CD.IncludeSyncData = 1;
|
|
break;
|
|
|
|
case XAForm2:
|
|
Cdb.READ_CD.ExpectedSectorType = 5;
|
|
Cdb.READ_CD.IncludeUserData = 1;
|
|
Cdb.READ_CD.HeaderCode = 3;
|
|
Cdb.READ_CD.IncludeSyncData = 1;
|
|
break;
|
|
|
|
default:
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// No retries are allowed for this IRP.
|
|
//
|
|
|
|
IdexChannelObject.MaximumIoRetries = IDE_NO_RETRY_COUNT;
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, Irp->UserBuffer, TransferLength, FALSE,
|
|
IdexCdRomFinishGeneric);
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartSetSpindleSpeed(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_CDROM_SET_SPINDLE_SPEED requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PULONG SpindleSpeedControl;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the input buffer is large enough.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(ULONG)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
SpindleSpeedControl = (PULONG)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Verify that this is a valid spindle speed.
|
|
//
|
|
|
|
if (*SpindleSpeedControl > DVDX2_SPINDLE_SPEED_MAXIMUM) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Change the maximum spindle speed to the requested spindle speed. Note
|
|
// that if the drive fails to handle the below ATAPI request, the software
|
|
// state won't match the hardware state, but that's not likely to occur and
|
|
// won't cause any problems for the drive or driver, so we'll ignore that
|
|
// possibility.
|
|
//
|
|
|
|
IdexCdRomMaximumSpindleSpeed = *SpindleSpeedControl;
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Issue the ATAPI spindle speed control request.
|
|
//
|
|
|
|
IdexCdRomSetSpindleSpeed(*SpindleSpeedControl, IdexCdRomFinishGeneric);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishStartSession(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing a
|
|
IOCTL_DVD_START_SESSION request.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PCDVD_KEY_HEADER KeyHeader;
|
|
PCDVD_REPORT_AGID_DATA ReportAGIDData;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
|
|
//
|
|
// Jump to the generic handler to process errors.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
IdexCdRomFinishGeneric();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Copy the authentication grant ID (AGID) to the user's buffer.
|
|
//
|
|
|
|
KeyHeader = (PCDVD_KEY_HEADER)IdexCdRomStaticTransferBuffer;
|
|
ReportAGIDData = (PCDVD_REPORT_AGID_DATA)KeyHeader->Data;
|
|
*((PDVD_SESSION_ID)Irp->UserBuffer) = ReportAGIDData->AGID;
|
|
|
|
//
|
|
// Complete the request and start the next packet.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
IdexChannelStartNextPacket();
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartStartSession(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_DVD_START_SESSION requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
CDB Cdb;
|
|
|
|
ASSERT(sizeof(IdexCdRomStaticTransferBuffer) >= sizeof(CDVD_KEY_HEADER) +
|
|
sizeof(CDVD_REPORT_AGID_DATA));
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the output buffer is large enough to receive the data.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DVD_SESSION_ID)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = sizeof(DVD_SESSION_ID);
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
|
|
*((PUSHORT)Cdb.REPORT_KEY.AllocationLength) =
|
|
IdexConstantUshortByteSwap(sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA));
|
|
|
|
ASSERT(Cdb.REPORT_KEY.KeyFormat == DVD_REPORT_AGID);
|
|
ASSERT(Cdb.REPORT_KEY.AGID == 0);
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, IdexCdRomStaticTransferBuffer,
|
|
sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA), FALSE,
|
|
IdexCdRomFinishStartSession);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishReadKey(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing a
|
|
IOCTL_DVD_READ_KEY request.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PCDVD_KEY_HEADER KeyHeader;
|
|
PDVD_COPY_PROTECT_KEY CopyProtectKey;
|
|
ULONG KeyDataLength;
|
|
ULONG TransferLength;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// The media may not have enough data to fill the output buffer, but this
|
|
// isn't an error.
|
|
//
|
|
|
|
if (Irp->IoStatus.Status == STATUS_DATA_OVERRUN) {
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Jump to the generic handler to process errors.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
IdexCdRomFinishGeneric();
|
|
return;
|
|
}
|
|
|
|
KeyHeader = (PCDVD_KEY_HEADER)IdexCdRomStaticTransferBuffer;
|
|
CopyProtectKey = (PDVD_COPY_PROTECT_KEY)Irp->UserBuffer;
|
|
|
|
//
|
|
// Compute the number of bytes that the device should have returned for the
|
|
// given key format and the number of bytes that were actually transferred.
|
|
// We're not returning the Reserved bytes in the CDVD_KEY_HEADER to the
|
|
// caller, so subtract these from the length of the key.
|
|
//
|
|
|
|
KeyDataLength = RtlUshortByteSwap(*((PUSHORT)&KeyHeader->DataLength));
|
|
|
|
if (KeyDataLength > sizeof(KeyHeader->Reserved)) {
|
|
KeyDataLength -= sizeof(KeyHeader->Reserved);
|
|
}
|
|
|
|
TransferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength -
|
|
sizeof(DVD_COPY_PROTECT_KEY);
|
|
|
|
if (KeyDataLength < TransferLength) {
|
|
TransferLength = KeyDataLength;
|
|
}
|
|
|
|
//
|
|
// Copy the input buffer's DVD_COPY_PROTECT_KEY structure to the output
|
|
// buffer. The I/O manager doesn't handle buffered I/O by using the same
|
|
// input and output buffer, so we need to do this copy ourselves.
|
|
//
|
|
|
|
RtlCopyMemory(CopyProtectKey, IrpSp->Parameters.DeviceIoControl.InputBuffer,
|
|
sizeof(DVD_COPY_PROTECT_KEY));
|
|
|
|
//
|
|
// Copy the key data to the output buffer as appropriate.
|
|
//
|
|
|
|
if (CopyProtectKey->KeyType == DvdTitleKey) {
|
|
RtlCopyMemory(CopyProtectKey->KeyData, KeyHeader->Data + 1,
|
|
TransferLength - 1);
|
|
CopyProtectKey->KeyData[TransferLength - 1] = 0;
|
|
CopyProtectKey->KeyFlags = KeyHeader->Data[0];
|
|
} else {
|
|
RtlCopyMemory(CopyProtectKey->KeyData, KeyHeader->Data, TransferLength);
|
|
}
|
|
|
|
CopyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY) + TransferLength;
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = CopyProtectKey->KeyLength;
|
|
|
|
//
|
|
// Complete the request and start the next packet.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
IdexChannelStartNextPacket();
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartReadKey(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_DVD_READ_KEY requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDVD_COPY_PROTECT_KEY CopyProtectKey;
|
|
ULONG TransferLength;
|
|
ULONG LogicalBlockAddress;
|
|
CDB Cdb;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the input buffer is large enough.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(DVD_COPY_PROTECT_KEY)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
CopyProtectKey = (PDVD_COPY_PROTECT_KEY)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Verify that the output buffer is large enough to receive at least the
|
|
// DVD_COPY_PROTECT_KEY header.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DVD_COPY_PROTECT_KEY)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Compute the transfer length for the request. The transfer length is
|
|
// restricted to the static tranfer buffer length.
|
|
//
|
|
|
|
TransferLength = sizeof(CDVD_KEY_HEADER) +
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength -
|
|
sizeof(DVD_COPY_PROTECT_KEY);
|
|
|
|
if (TransferLength > sizeof(IdexCdRomStaticTransferBuffer)) {
|
|
IdexDbgPrint(("IDEX: transfer length exceeds static buffer length.\n"));
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
|
|
*((PUSHORT)Cdb.REPORT_KEY.AllocationLength) =
|
|
RtlUshortByteSwap((USHORT)TransferLength);
|
|
Cdb.REPORT_KEY.KeyFormat = (UCHAR)CopyProtectKey->KeyType;
|
|
Cdb.REPORT_KEY.AGID = (UCHAR)CopyProtectKey->SessionId;
|
|
|
|
if (CopyProtectKey->KeyType == DvdTitleKey) {
|
|
LogicalBlockAddress = (ULONG)(CopyProtectKey->Parameters.TitleOffset.QuadPart >>
|
|
IDE_ATAPI_CD_SECTOR_SHIFT);
|
|
*((PULONG)Cdb.REPORT_KEY.LogicalBlockAddress) =
|
|
RtlUlongByteSwap(LogicalBlockAddress);
|
|
}
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, IdexCdRomStaticTransferBuffer,
|
|
TransferLength, FALSE, IdexCdRomFinishReadKey);
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartSendKey(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_DVD_SEND_KEY requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDVD_COPY_PROTECT_KEY CopyProtectKey;
|
|
ULONG TransferLength;
|
|
PCDVD_KEY_HEADER KeyHeader;
|
|
CDB Cdb;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the input buffer is large enough.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(DVD_COPY_PROTECT_KEY)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
CopyProtectKey = (PDVD_COPY_PROTECT_KEY)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Compute the transfer length for the request. The transfer length is
|
|
// restricted to the static tranfer buffer length.
|
|
//
|
|
|
|
TransferLength = CopyProtectKey->KeyLength - sizeof(DVD_COPY_PROTECT_KEY) +
|
|
sizeof(CDVD_KEY_HEADER);
|
|
|
|
if (TransferLength > sizeof(IdexCdRomStaticTransferBuffer)) {
|
|
IdexDbgPrint(("IDEX: transfer length exceeds static buffer length.\n"));
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Prepare the transfer buffer.
|
|
//
|
|
|
|
KeyHeader = (PCDVD_KEY_HEADER)IdexCdRomStaticTransferBuffer;
|
|
*((PUSHORT)KeyHeader->DataLength) = RtlUshortByteSwap((USHORT)(TransferLength -
|
|
FIELD_OFFSET(CDVD_KEY_HEADER, Reserved)));
|
|
RtlZeroMemory(KeyHeader->Reserved, sizeof(KeyHeader->Reserved));
|
|
RtlCopyMemory(KeyHeader->Data, CopyProtectKey->KeyData, TransferLength -
|
|
sizeof(CDVD_KEY_HEADER));
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
|
|
*((PUSHORT)Cdb.SEND_KEY.ParameterListLength) =
|
|
RtlUshortByteSwap((USHORT)TransferLength);
|
|
Cdb.SEND_KEY.KeyFormat = (UCHAR)CopyProtectKey->KeyType;
|
|
Cdb.SEND_KEY.AGID = (UCHAR)CopyProtectKey->SessionId;
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, IdexCdRomStaticTransferBuffer,
|
|
TransferLength, TRUE, IdexCdRomFinishGeneric);
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartEndSession(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_DVD_END_SESSION requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDVD_SESSION_ID SessionId;
|
|
CDB Cdb;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the input buffer is large enough.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(DVD_SESSION_ID)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
SessionId = (PDVD_SESSION_ID)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Verify that the output buffer is large enough to receive the data.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(DVD_SESSION_ID)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
|
|
Cdb.SEND_KEY.AGID = (UCHAR)*SessionId;
|
|
Cdb.SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, NULL, 0, FALSE, IdexCdRomFinishGeneric);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomStartReadStructure(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_DVD_READ_STRUCTURE requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDVD_READ_STRUCTURE ReadStructure;
|
|
ULONG TransferLength;
|
|
ULONG MinimumTransferLength;
|
|
ULONG LogicalBlockAddress;
|
|
CDB Cdb;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the input buffer is large enough.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(DVD_READ_STRUCTURE)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
ReadStructure = (PDVD_READ_STRUCTURE)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Verify that the output buffer is large enough to receive the data, but
|
|
// isn't too large to overflow READ_DVD_STRUCTURES_HEADER.Length. The read
|
|
// length must have the proper alignment.
|
|
//
|
|
|
|
TransferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
|
|
|
if (ReadStructure->Format == DvdPhysicalDescriptor) {
|
|
MinimumTransferLength = sizeof(DVD_DESCRIPTOR_HEADER) +
|
|
sizeof(DVD_LAYER_DESCRIPTOR);
|
|
} else {
|
|
MinimumTransferLength = sizeof(DVD_DESCRIPTOR_HEADER);
|
|
}
|
|
|
|
if ((TransferLength < MinimumTransferLength) ||
|
|
(TransferLength > MAXUSHORT) ||
|
|
((TransferLength & IDE_ALIGNMENT_REQUIREMENT) != 0)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Compute the logical block address.
|
|
//
|
|
|
|
LogicalBlockAddress = (ULONG)(ReadStructure->BlockByteOffset.QuadPart >>
|
|
IDE_ATAPI_CD_SECTOR_SHIFT);
|
|
|
|
//
|
|
// Lock the user's buffer into memory if necessary.
|
|
//
|
|
|
|
IoLockUserBuffer(Irp, TransferLength);
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = TransferLength;
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
Cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
|
|
*((PUSHORT)Cdb.READ_DVD_STRUCTURE.AllocationLength) =
|
|
RtlUshortByteSwap((USHORT)TransferLength);
|
|
*((PULONG)Cdb.READ_DVD_STRUCTURE.RMDBlockNumber) =
|
|
RtlUlongByteSwap(LogicalBlockAddress);
|
|
Cdb.READ_DVD_STRUCTURE.LayerNumber = ReadStructure->LayerNumber;
|
|
Cdb.READ_DVD_STRUCTURE.Format = (UCHAR)ReadStructure->Format;
|
|
|
|
if (IdexIsFlagSet(ReadStructure->LayerNumber, 0x80)) {
|
|
Cdb.READ_DVD_STRUCTURE.Control = 0xC0;
|
|
}
|
|
|
|
if (ReadStructure->Format == DvdDiskKeyDescriptor) {
|
|
Cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)ReadStructure->SessionId;
|
|
}
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest(&Cdb, Irp->UserBuffer, TransferLength, FALSE,
|
|
IdexCdRomFinishGenericWithOverrun);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishScsiPassThrough(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing a
|
|
IOCTL_SCSI_PASS_THROUGH_DIRECT request.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PSCSI_PASS_THROUGH_DIRECT PassThrough;
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PassThrough =
|
|
(PSCSI_PASS_THROUGH_DIRECT)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Manually unlock the caller's data buffer if IdexCdRomStartScsiPassThrough
|
|
// locked down the buffer.
|
|
//
|
|
|
|
if (PassThrough->DataTransferLength != 0) {
|
|
MmLockUnlockBufferPages(PassThrough->DataBuffer,
|
|
PassThrough->DataTransferLength, TRUE);
|
|
}
|
|
|
|
//
|
|
// Jump to the generic handler.
|
|
//
|
|
|
|
IdexCdRomFinishGeneric();
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
IdexCdRomStartScsiPassThrough(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IOCTL_SCSI_PASS_THROUGH_DIRECT requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PSCSI_PASS_THROUGH_DIRECT PassThrough;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the output buffer is either empty or large enough to receive
|
|
// the sense data.
|
|
//
|
|
|
|
if ((IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0) &&
|
|
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SENSE_DATA))) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Verify that the input buffer is large enough.
|
|
//
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SCSI_PASS_THROUGH_DIRECT)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
PassThrough =
|
|
(PSCSI_PASS_THROUGH_DIRECT)IrpSp->Parameters.DeviceIoControl.InputBuffer;
|
|
|
|
//
|
|
// Perform minimal verification of the contents of the pass through data
|
|
// structure and the data transfer buffer.
|
|
//
|
|
|
|
if ((PassThrough->Length != sizeof(SCSI_PASS_THROUGH_DIRECT)) ||
|
|
(PassThrough->DataTransferLength > IDE_ATAPI_MAXIMUM_TRANSFER_BYTES) ||
|
|
(((ULONG_PTR)PassThrough->DataBuffer & IDE_ALIGNMENT_REQUIREMENT) != 0) ||
|
|
((PassThrough->DataTransferLength & IDE_ALIGNMENT_REQUIREMENT) != 0) ||
|
|
(PassThrough->DataTransferLength > IDE_ATAPI_MAXIMUM_TRANSFER_BYTES)) {
|
|
IdexChannelInvalidParameterRequest(Irp);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the caller is expecting data to be transferred, then manually lock
|
|
// down the pages.
|
|
//
|
|
|
|
if (PassThrough->DataTransferLength != 0) {
|
|
MmLockUnlockBufferPages(PassThrough->DataBuffer,
|
|
PassThrough->DataTransferLength, FALSE);
|
|
}
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Issue the ATAPI request.
|
|
//
|
|
|
|
IdexCdRomIssueAtapiRequest((PCDB)&PassThrough->Cdb, PassThrough->DataBuffer,
|
|
PassThrough->DataTransferLength, (BOOLEAN)(PassThrough->DataIn ==
|
|
SCSI_IOCTL_DATA_OUT), IdexCdRomFinishScsiPassThrough);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFinishRead(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked at DPC level to finish processing a IRP_MJ_READ
|
|
request.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
Irp = IdexChannelObject.CurrentIrp;
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
#if DBG
|
|
//
|
|
// Keep track of the number of reads that have been completed.
|
|
//
|
|
|
|
IdexCdRomDebugReadsFinished++;
|
|
#endif
|
|
|
|
//
|
|
// Jump to the generic handler to process errors.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
IdexCdRomFinishGeneric();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there are still more bytes to transfer in this request, then adjust
|
|
// the transfer counts and requeue the packet.
|
|
//
|
|
|
|
if (IrpSp->Parameters.IdexReadWrite.Length > IDE_ATAPI_MAXIMUM_TRANSFER_BYTES) {
|
|
|
|
IrpSp->Parameters.IdexReadWrite.Length -= IDE_ATAPI_MAXIMUM_TRANSFER_BYTES;
|
|
IrpSp->Parameters.IdexReadWrite.BufferOffset += IDE_ATAPI_MAXIMUM_TRANSFER_BYTES;
|
|
IrpSp->Parameters.IdexReadWrite.SectorNumber += IDE_ATAPI_CD_MAXIMUM_TRANSFER_SECTORS;
|
|
|
|
IdexChannelStartPacket(IrpSp->DeviceObject, Irp);
|
|
IdexChannelStartNextPacket();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Complete the request and start the next packet.
|
|
//
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(Irp, IO_CD_ROM_INCREMENT);
|
|
IdexChannelStartNextPacket();
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomStartRead(
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles queued IRP_MJ_READ and IRP_MJ_WRITE requests.
|
|
|
|
Arguments:
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
ULONG TransferLength;
|
|
ULONG NumberOfSectors;
|
|
CDB Cdb;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// If the number of bytes remaining is greater than the maximum transfer
|
|
// size allowed by the hardware, then clip the number of bytes to the
|
|
// maximum.
|
|
//
|
|
|
|
TransferLength = IrpSp->Parameters.IdexReadWrite.Length;
|
|
|
|
if (TransferLength > IDE_ATAPI_MAXIMUM_TRANSFER_BYTES) {
|
|
TransferLength = IDE_ATAPI_MAXIMUM_TRANSFER_BYTES;
|
|
}
|
|
|
|
NumberOfSectors = (TransferLength >> IDE_ATAPI_CD_SECTOR_SHIFT);
|
|
|
|
//
|
|
// Check if the current spindle speed is less than the maximum speed and if
|
|
// the spindle speed can be restored to normal. If so, then this will have
|
|
// issued an ATAPI request that on completion, will cause this routine to be
|
|
// called again.
|
|
//
|
|
|
|
if ((IdexCdRomCurrentSpindleSpeed < IdexCdRomMaximumSpindleSpeed) &&
|
|
IdexCdRomCheckForRestoreSpindleSpeed(IrpSp->Parameters.IdexReadWrite.SectorNumber,
|
|
NumberOfSectors)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Prepare the SCSI CDB.
|
|
//
|
|
|
|
RtlZeroMemory(&Cdb, sizeof(CDB));
|
|
|
|
Cdb.CDB10.OperationCode = SCSIOP_READ;
|
|
Cdb.CDB10.LogicalBlock = RtlUlongByteSwap(IrpSp->Parameters.IdexReadWrite.SectorNumber);
|
|
Cdb.CDB10.TransferBlocks = RtlUshortByteSwap((USHORT)NumberOfSectors);
|
|
|
|
//
|
|
// Synchronize execution with the interrupt service routine.
|
|
//
|
|
|
|
IdexRaiseIrqlToChannelDIRQLFromDPCLevel();
|
|
|
|
//
|
|
// Select the IDE device and spin until the device is not busy.
|
|
//
|
|
|
|
if (!IdexCdRomSelectDeviceAndSpinWhileBusy()) {
|
|
IdexCdRomFinishRead();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Prepare the bus master interface for the DMA transfer.
|
|
//
|
|
|
|
if (IdexIsFlagClear(Irp->Flags, IRP_SCATTER_GATHER_OPERATION) ||
|
|
IdexIsFlagSet(IrpSp->Flags, SL_FSCACHE_REQUEST)) {
|
|
IdexChannelPrepareBufferTransfer(IrpSp->Parameters.IdexReadWrite.Buffer +
|
|
IrpSp->Parameters.IdexReadWrite.BufferOffset, TransferLength);
|
|
} else {
|
|
IdexChannelPrepareScatterGatherTransfer(Irp->SegmentArray,
|
|
IrpSp->Parameters.IdexReadWrite.BufferOffset, TransferLength);
|
|
}
|
|
|
|
IdexWriteFeaturesPort(IDE_FEATURE_DMA);
|
|
|
|
//
|
|
// Write out the IDE command and the SCSI CDB.
|
|
//
|
|
|
|
if (!IdexCdRomWritePacket(&Cdb)) {
|
|
IdexCdRomFinishRead();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Activate the bus master interface.
|
|
//
|
|
|
|
IdexWriteBusMasterCommandPort(IDE_BUS_MASTER_COMMAND_START |
|
|
IDE_BUS_MASTER_COMMAND_READ);
|
|
|
|
//
|
|
// Indicate that we're expecting an interrupt for this IRP.
|
|
//
|
|
|
|
ASSERT(IdexChannelObject.InterruptRoutine == NULL);
|
|
|
|
IdexChannelObject.InterruptRoutine = IdexCdRomTransferInterrupt;
|
|
IdexChannelObject.FinishIoRoutine = IdexCdRomFinishRead;
|
|
IdexChannelObject.ExpectingBusMasterInterrupt = TRUE;
|
|
IdexChannelObject.Timeout = IDE_ATAPI_DEFAULT_TIMEOUT;
|
|
|
|
IdexLowerIrqlFromChannelDIRQL(DISPATCH_LEVEL);
|
|
}
|
|
|
|
NTSTATUS
|
|
IdexCdRomRead(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is indirectly called by the I/O manager to handle IRP_MJ_READ
|
|
requests.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Specifies the device object that the I/O request is for.
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
LONGLONG StartingByteOffset;
|
|
LONGLONG EndingByteOffset;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
//
|
|
// Verify that the parameters are valid. The buffer length must be an
|
|
// integral number of sectors, the starting offset must be sector aligned,
|
|
// and the ending offset must be within the bounds of the disk. The I/O
|
|
// buffer must have the proper device alignment.
|
|
//
|
|
|
|
StartingByteOffset = IrpSp->Parameters.Read.ByteOffset.QuadPart;
|
|
EndingByteOffset = StartingByteOffset + IrpSp->Parameters.Read.Length;
|
|
|
|
if (IdexIsFlagClear(IrpSp->Flags, SL_FSCACHE_REQUEST)) {
|
|
|
|
if (((PtrToUlong(Irp->UserBuffer) & IDE_ALIGNMENT_REQUIREMENT) != 0) ||
|
|
((IrpSp->Parameters.Read.BufferOffset & IDE_ALIGNMENT_REQUIREMENT) != 0) ||
|
|
((IrpSp->Parameters.Read.Length & IDE_ATAPI_CD_SECTOR_MASK) != 0) ||
|
|
((IrpSp->Parameters.Read.ByteOffset.LowPart & IDE_ATAPI_CD_SECTOR_MASK) != 0) ||
|
|
(EndingByteOffset > IdexCdRomPartitionLength)) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Bypass parameter checking if this request is coming from the cache.
|
|
// We'll trust that the file system cache is submitting a valid request,
|
|
// but we'll double check in debug.
|
|
//
|
|
|
|
ASSERT((PtrToUlong(IrpSp->Parameters.Read.CacheBuffer) &
|
|
IDE_ATAPI_CD_SECTOR_MASK) == 0);
|
|
ASSERT((IrpSp->Parameters.Read.Length & IDE_ATAPI_CD_SECTOR_MASK) == 0);
|
|
ASSERT((IrpSp->Parameters.Read.ByteOffset.LowPart & IDE_ATAPI_CD_SECTOR_MASK) == 0);
|
|
ASSERT(EndingByteOffset <= IdexCdRomPartitionLength);
|
|
}
|
|
|
|
//
|
|
// If this is a zero length request, then we can complete the IRP now.
|
|
//
|
|
|
|
if (IrpSp->Parameters.Read.Length == 0) {
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Convert the I/O request parameters to the internal IDE format. The
|
|
// internal format unpacks the cache buffer and buffer offset fields to
|
|
// different fields by collapsing the byte offset to a sector number.
|
|
//
|
|
|
|
IrpSp->Parameters.IdexReadWrite.SectorNumber = (ULONG)(StartingByteOffset >>
|
|
IDE_ATAPI_CD_SECTOR_SHIFT);
|
|
|
|
ASSERT(FIELD_OFFSET(IO_STACK_LOCATION, Parameters.Read.Length) ==
|
|
FIELD_OFFSET(IO_STACK_LOCATION, Parameters.IdexReadWrite.Length));
|
|
|
|
//
|
|
// Move the buffer pointer and the buffer starting offset around depending
|
|
// on whether or not this is a file system cache request.
|
|
//
|
|
|
|
if (IdexIsFlagSet(IrpSp->Flags, SL_FSCACHE_REQUEST)) {
|
|
IrpSp->Parameters.IdexReadWrite.BufferOffset = 0;
|
|
ASSERT(FIELD_OFFSET(IO_STACK_LOCATION, Parameters.Read.CacheBuffer) ==
|
|
FIELD_OFFSET(IO_STACK_LOCATION, Parameters.IdexReadWrite.Buffer));
|
|
} else {
|
|
IrpSp->Parameters.IdexReadWrite.BufferOffset =
|
|
IrpSp->Parameters.Read.BufferOffset;
|
|
IrpSp->Parameters.IdexReadWrite.Buffer = (PUCHAR)Irp->UserBuffer;
|
|
}
|
|
|
|
//
|
|
// Initialize the IRP's information result to the number of bytes
|
|
// transferred.
|
|
//
|
|
|
|
Irp->IoStatus.Information = IrpSp->Parameters.IdexReadWrite.Length;
|
|
|
|
//
|
|
// Queue the I/O reqeust.
|
|
//
|
|
|
|
IoMarkIrpPending(Irp);
|
|
IdexChannelStartPacket(DeviceObject, Irp);
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdexCdRomRequestDVDX2AuthenticationPage(
|
|
OUT PDVDX2_AUTHENTICATION Authentication
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine requests the DVD-X2 authentication page from the CD-ROM device.
|
|
|
|
Arguments:
|
|
|
|
Authentication - Specifies the buffer to receive the authentication page.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
SCSI_PASS_THROUGH_DIRECT PassThrough;
|
|
PCDB Cdb = (PCDB)&PassThrough.Cdb;
|
|
|
|
//
|
|
// Prepare the SCSI pass through structure.
|
|
//
|
|
|
|
RtlZeroMemory(&PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT));
|
|
|
|
PassThrough.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
|
|
|
|
//
|
|
// Request the authentication page from the DVD-X2 drive.
|
|
//
|
|
|
|
PassThrough.DataIn = SCSI_IOCTL_DATA_IN;
|
|
PassThrough.DataBuffer = Authentication;
|
|
PassThrough.DataTransferLength = sizeof(DVDX2_AUTHENTICATION);
|
|
|
|
RtlZeroMemory(Cdb, sizeof(CDB));
|
|
|
|
Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|
Cdb->MODE_SENSE10.PageCode = DVDX2_MODE_PAGE_AUTHENTICATION;
|
|
*((PUSHORT)&Cdb->MODE_SENSE10.AllocationLength) =
|
|
(USHORT)IdexConstantUshortByteSwap(sizeof(DVDX2_AUTHENTICATION));
|
|
|
|
RtlZeroMemory(Authentication, sizeof(DVDX2_AUTHENTICATION));
|
|
|
|
status = IoSynchronousDeviceIoControlRequest(IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
|
IdexCdRomDeviceObject, &PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT),
|
|
NULL, 0, NULL, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// If the drive reported back an authentication failure at this point,
|
|
// then change the error to STATUS_UNRECOGNIZED_MEDIA to help
|
|
// distinguish between a disc that doesn't contain any anti-piracy
|
|
// protection from a disc that failed the anti-piracy checks below.
|
|
//
|
|
|
|
if (status == STATUS_TOO_MANY_SECRETS) {
|
|
return STATUS_UNRECOGNIZED_MEDIA;
|
|
} else {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if the DVD-X2 drive thinks that this is a valid CDF header.
|
|
//
|
|
|
|
if (Authentication->AuthenticationPage.CDFValid != DVDX2_CDF_VALID) {
|
|
return STATUS_UNRECOGNIZED_MEDIA;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdexCdRomRequestDVDX2ControlData(
|
|
OUT PDVDX2_CONTROL_DATA ControlData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine requests the DVD-X2 control data structure from the CD-ROM
|
|
device.
|
|
|
|
Arguments:
|
|
|
|
ControlData - Specifies the buffer to receive the control data structure.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
SCSI_PASS_THROUGH_DIRECT PassThrough;
|
|
PCDB Cdb = (PCDB)&PassThrough.Cdb;
|
|
|
|
//
|
|
// Prepare the SCSI pass through structure.
|
|
//
|
|
|
|
RtlZeroMemory(&PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT));
|
|
|
|
PassThrough.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
|
|
|
|
//
|
|
// Request the control data structure from the DVD-X2 drive.
|
|
//
|
|
|
|
PassThrough.DataIn = SCSI_IOCTL_DATA_IN;
|
|
PassThrough.DataBuffer = ControlData;
|
|
PassThrough.DataTransferLength = sizeof(DVDX2_CONTROL_DATA);
|
|
|
|
RtlZeroMemory(Cdb, sizeof(CDB));
|
|
|
|
Cdb->READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
|
|
*((PUSHORT)&Cdb->READ_DVD_STRUCTURE.AllocationLength) =
|
|
(USHORT)IdexConstantUshortByteSwap(sizeof(DVDX2_CONTROL_DATA));
|
|
*((PULONG)Cdb->READ_DVD_STRUCTURE.RMDBlockNumber) =
|
|
IdexConstantUlongByteSwap((ULONG)(~DVDX2_CONTROL_DATA_BLOCK_NUMBER));
|
|
Cdb->READ_DVD_STRUCTURE.LayerNumber = (UCHAR)(~DVDX2_CONTROL_DATA_LAYER);
|
|
Cdb->READ_DVD_STRUCTURE.Format = DvdPhysicalDescriptor;
|
|
Cdb->READ_DVD_STRUCTURE.Control = 0xC0;
|
|
|
|
status = IoSynchronousDeviceIoControlRequest(IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
|
IdexCdRomDeviceObject, &PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT),
|
|
NULL, 0, NULL, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexDbgPrint(("IDEX: control data read failed (status=%08x).\n", status));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*##### -- not necessary
|
|
NTSTATUS
|
|
IdexCdRomVerifyDVDX2AuthoringSignature(
|
|
IN PDVDX2_CONTROL_DATA ControlData
|
|
)
|
|
*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies the authoring signature of the supplied DVD-X2 control
|
|
data structure.
|
|
|
|
Arguments:
|
|
|
|
ControlData - Specifies the control data structure to be verified.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*
|
|
{
|
|
PUCHAR PublicKeyData;
|
|
UCHAR AuthoringDigest[XC_DIGEST_LEN];
|
|
PUCHAR Workspace;
|
|
BOOLEAN Verified;
|
|
|
|
PublicKeyData = XePublicKeyData;
|
|
|
|
//
|
|
// Calculate the digest for bytes 0 to 1226 of the control data structure
|
|
// and verify that this matches the authoring hash stored in the control
|
|
// data structure.
|
|
//
|
|
|
|
XCCalcDigest((PUCHAR)&ControlData->LayerDescriptor,
|
|
FIELD_OFFSET(DVDX2_CONTROL_DATA, AuthoringHash) -
|
|
FIELD_OFFSET(DVDX2_CONTROL_DATA, LayerDescriptor), AuthoringDigest);
|
|
|
|
if (!RtlEqualMemory(AuthoringDigest, ControlData->AuthoringHash,
|
|
XC_DIGEST_LEN)) {
|
|
return STATUS_TOO_MANY_SECRETS;
|
|
}
|
|
|
|
//
|
|
// Allocate a workspace to do the digest verification.
|
|
//
|
|
|
|
Workspace = (PUCHAR)ExAllocatePoolWithTag(XCCalcKeyLen(PublicKeyData) * 2,
|
|
'sWxI');
|
|
|
|
if (Workspace == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Verify that the SHA1 digest matches the encrypted header digest.
|
|
//
|
|
|
|
Verified = XCVerifyDigest(ControlData->AuthoringSignature, PublicKeyData,
|
|
Workspace, AuthoringDigest);
|
|
|
|
ExFreePool(Workspace);
|
|
|
|
if (Verified) {
|
|
return STATUS_SUCCESS;
|
|
} else {
|
|
return STATUS_TOO_MANY_SECRETS;
|
|
}
|
|
}*/
|
|
|
|
VOID
|
|
IdexCdRomDecryptHostChallengeResponseTable(
|
|
IN PDVDX2_CONTROL_DATA ControlData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrypts the host challenge response table of the supplied
|
|
DVD-X2 control data structure.
|
|
|
|
Arguments:
|
|
|
|
ControlData - Specifies the control data structure that contains the host
|
|
challenge response table to be decrypted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
A_SHA_CTX SHAHash;
|
|
UCHAR SHADigest[A_SHA_DIGEST_LEN];
|
|
struct RC4_KEYSTRUCT RC4KeyStruct;
|
|
|
|
//
|
|
// Compute the SHA-1 hash of the data between bytes 1183 to 1226 of the
|
|
// control data structure.
|
|
//
|
|
|
|
A_SHAInit(&SHAHash);
|
|
A_SHAUpdate(&SHAHash, (PUCHAR)&ControlData->AuthoringTimeStamp,
|
|
FIELD_OFFSET(DVDX2_CONTROL_DATA, AuthoringHash) -
|
|
FIELD_OFFSET(DVDX2_CONTROL_DATA, AuthoringTimeStamp));
|
|
A_SHAFinal(&SHAHash, SHADigest);
|
|
|
|
//
|
|
// Compute a 56-bit RC4 session key from the SHA-1 hash.
|
|
//
|
|
|
|
rc4_key(&RC4KeyStruct, 56 / 8, SHADigest);
|
|
|
|
//
|
|
// Decrypt the host challenge response table in place using the RC4 session
|
|
// key.
|
|
//
|
|
|
|
rc4(&RC4KeyStruct, sizeof(ControlData->HostChallengeResponseTable.Entries),
|
|
(PUCHAR)&ControlData->HostChallengeResponseTable.Entries);
|
|
}
|
|
|
|
NTSTATUS
|
|
IdexCdRomAuthenticationChallenge(
|
|
IN PDVDX2_CONTROL_DATA ControlData,
|
|
IN PDVDX2_HOST_CHALLENGE_RESPONSE_ENTRY HostChallengeResponseEntry,
|
|
IN BOOLEAN FirstChallenge,
|
|
IN BOOLEAN FinalChallenge
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine issues the supplied challenge to the drive and checks the
|
|
response from the drive.
|
|
|
|
Arguments:
|
|
|
|
ControlData - Specifies the control data structure.
|
|
|
|
HostChallengeResponseEntry - Specifies the challenge to issue.
|
|
|
|
FirstChallenge - Specifies TRUE if this is the first challenge being made to
|
|
the drive.
|
|
|
|
FinalChallenge - Specifies TRUE if this is the final challenge being made to
|
|
the drive.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
SCSI_PASS_THROUGH_DIRECT PassThrough;
|
|
PCDB Cdb = (PCDB)&PassThrough.Cdb;
|
|
DVDX2_AUTHENTICATION Authentication;
|
|
|
|
ASSERT(HostChallengeResponseEntry->ChallengeLevel == 1);
|
|
|
|
//
|
|
// Prepare the SCSI pass through structure.
|
|
//
|
|
|
|
RtlZeroMemory(&PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT));
|
|
|
|
PassThrough.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
|
|
|
|
//
|
|
// Prepare the authentication page.
|
|
//
|
|
|
|
RtlZeroMemory(&Authentication, sizeof(DVDX2_AUTHENTICATION));
|
|
|
|
*((PUSHORT)&Authentication.Header.ModeDataLength) =
|
|
IdexConstantUshortByteSwap(sizeof(DVDX2_AUTHENTICATION) -
|
|
FIELD_OFFSET(DVDX2_AUTHENTICATION, Header.MediumType));
|
|
Authentication.AuthenticationPage.PageCode = DVDX2_MODE_PAGE_AUTHENTICATION;
|
|
Authentication.AuthenticationPage.PageLength = sizeof(DVDX2_AUTHENTICATION_PAGE) -
|
|
FIELD_OFFSET(DVDX2_AUTHENTICATION_PAGE, PartitionArea);
|
|
Authentication.AuthenticationPage.CDFValid = 1;
|
|
Authentication.AuthenticationPage.DiscCategoryAndVersion =
|
|
(ControlData->LayerDescriptor.BookType << 4) +
|
|
ControlData->LayerDescriptor.BookVersion;
|
|
Authentication.AuthenticationPage.DrivePhaseLevel = 1;
|
|
Authentication.AuthenticationPage.ChallengeID = HostChallengeResponseEntry->ChallengeID;
|
|
Authentication.AuthenticationPage.ChallengeValue = HostChallengeResponseEntry->ChallengeValue;
|
|
|
|
if (!FirstChallenge) {
|
|
Authentication.AuthenticationPage.Authentication = 1;
|
|
}
|
|
|
|
if (FinalChallenge) {
|
|
Authentication.AuthenticationPage.PartitionArea = 1;
|
|
}
|
|
|
|
//
|
|
// Issue the challenge to the DVD-X2 drive.
|
|
//
|
|
|
|
PassThrough.DataIn = SCSI_IOCTL_DATA_OUT;
|
|
PassThrough.DataBuffer = &Authentication;
|
|
PassThrough.DataTransferLength = sizeof(DVDX2_AUTHENTICATION);
|
|
|
|
RtlZeroMemory(Cdb, sizeof(CDB));
|
|
|
|
Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SELECT10;
|
|
*((PUSHORT)&Cdb->MODE_SENSE10.AllocationLength) =
|
|
(USHORT)IdexConstantUshortByteSwap(sizeof(DVDX2_AUTHENTICATION));
|
|
|
|
status = IoSynchronousDeviceIoControlRequest(IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
|
IdexCdRomDeviceObject, &PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT),
|
|
NULL, 0, NULL, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexDbgPrint(("IDEX: challenge operation failed (status=%08x).\n", status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Read the response from the DVD-X2 drive.
|
|
//
|
|
|
|
PassThrough.DataIn = SCSI_IOCTL_DATA_IN;
|
|
|
|
Cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
|
|
Cdb->MODE_SENSE10.PageCode = DVDX2_MODE_PAGE_AUTHENTICATION;
|
|
*((PUSHORT)&Cdb->MODE_SENSE10.AllocationLength) =
|
|
(USHORT)IdexConstantUshortByteSwap(sizeof(DVDX2_AUTHENTICATION));
|
|
|
|
status = IoSynchronousDeviceIoControlRequest(IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
|
IdexCdRomDeviceObject, &PassThrough, sizeof(SCSI_PASS_THROUGH_DIRECT),
|
|
NULL, 0, NULL, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexDbgPrint(("IDEX: response operation failed (status=%08x).\n", status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Check that the drive's response matches the expected response.
|
|
//
|
|
|
|
if (!Authentication.AuthenticationPage.Authentication ||
|
|
(Authentication.AuthenticationPage.ResponseValue !=
|
|
HostChallengeResponseEntry->ResponseValue)) {
|
|
IdexDbgPrint(("IDEX: invalid response from drive.\n"));
|
|
return STATUS_TOO_MANY_SECRETS;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdexCdRomAuthenticationSequence(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the IOCTL_CDROM_AUTHENTICATION_SEQUENCE request.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
DVDX2_AUTHENTICATION Authentication;
|
|
DVDX2_CONTROL_DATA ControlData;
|
|
LONG StartingIndex;
|
|
LONG Index;
|
|
LONG EndingIndex;
|
|
BOOLEAN FirstChallenge;
|
|
PDVDX2_HOST_CHALLENGE_RESPONSE_ENTRY HostChallengeResponseEntry;
|
|
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
//
|
|
// If we've already authenticated the DVD-X2 disc, then don't bother doing
|
|
// it again.
|
|
//
|
|
|
|
if (IdexCdRomDVDX2Authenticated) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Request the authentication page from the DVD-X2 drive.
|
|
//
|
|
|
|
status = IdexCdRomRequestDVDX2AuthenticationPage(&Authentication);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Request the control data structure from the DVD-X2 drive.
|
|
//
|
|
|
|
status = IdexCdRomRequestDVDX2ControlData(&ControlData);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Verify that the disc category and version from the authentication page
|
|
// matches the control data structure.
|
|
//
|
|
|
|
if (Authentication.AuthenticationPage.DiscCategoryAndVersion !=
|
|
(ControlData.LayerDescriptor.BookType << 4) + ControlData.LayerDescriptor.BookVersion) {
|
|
IdexDbgPrint(("IDEX: disc category and version mismatch.\n"));
|
|
return STATUS_TOO_MANY_SECRETS;
|
|
}
|
|
|
|
//##### -- we're going to need this on retail systems for shadow boot!
|
|
//#ifdef DEVKIT
|
|
//
|
|
// Check if the DVD-X2 drive already thinks that we're authenticated. This
|
|
// can only happen in a DEVKIT environment where we loaded a ROM from the
|
|
// CD-ROM or hard disk and cold booted into that ROM. The second instance
|
|
// of the ROM doesn't know if the first instance of the ROM already ran the
|
|
// authentication sequence, so we rely on the the drive.
|
|
//
|
|
|
|
if ((Authentication.AuthenticationPage.PartitionArea != 0) &&
|
|
(Authentication.AuthenticationPage.Authentication != 0)) {
|
|
IdexCdRomDVDX2Authenticated = TRUE;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
//#endif
|
|
|
|
//
|
|
// Verify that the encrypted digest stored in the control data structure
|
|
// matches the digest of the structure.
|
|
//
|
|
|
|
//##### -- unnecessary
|
|
/* status = IdexCdRomVerifyDVDX2AuthoringSignature(&ControlData);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexDbgPrint(("IDEX: failed to verify control data structure (status=%08x).\n", status));
|
|
return status;
|
|
}*/
|
|
|
|
//
|
|
// Decrypt the contents of the host challenge response table.
|
|
//
|
|
|
|
IdexCdRomDecryptHostChallengeResponseTable(&ControlData);
|
|
|
|
//
|
|
// Validate the the host challenge response table is the expected version
|
|
// and that there are the expected number of entries in the table.
|
|
//
|
|
|
|
if ((ControlData.HostChallengeResponseTable.Version != 1) ||
|
|
(ControlData.HostChallengeResponseTable.NumberOfEntries == 0) ||
|
|
(ControlData.HostChallengeResponseTable.NumberOfEntries >
|
|
DVDX2_HOST_CHALLENGE_RESPONSE_ENTRY_COUNT)) {
|
|
IdexDbgPrint(("IDEX: invalid host challenge response table.\n"));
|
|
return STATUS_TOO_MANY_SECRETS;
|
|
}
|
|
|
|
//
|
|
// Walk through the host challenge response starting at a random starting
|
|
// index and issue challenge response values.
|
|
//
|
|
|
|
StartingIndex = KeQueryPerformanceCounter().LowPart %
|
|
ControlData.HostChallengeResponseTable.NumberOfEntries;
|
|
Index = StartingIndex;
|
|
EndingIndex = -1;
|
|
FirstChallenge = TRUE;
|
|
|
|
do {
|
|
|
|
HostChallengeResponseEntry = &ControlData.HostChallengeResponseTable.Entries[Index];
|
|
|
|
//
|
|
// Check if this is a supported challenge level. If so, issue the
|
|
// authentication challenge to the DVD-X2 drive.
|
|
//
|
|
|
|
if (HostChallengeResponseEntry->ChallengeLevel == 1) {
|
|
|
|
status = IdexCdRomAuthenticationChallenge(&ControlData,
|
|
HostChallengeResponseEntry, FirstChallenge, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
EndingIndex = Index;
|
|
FirstChallenge = FALSE;
|
|
}
|
|
|
|
//
|
|
// Advance the index and jump back to zero if we reach the end of the
|
|
// table.
|
|
//
|
|
|
|
Index++;
|
|
|
|
if (Index == ControlData.HostChallengeResponseTable.NumberOfEntries) {
|
|
Index = 0;
|
|
}
|
|
|
|
} while (Index != StartingIndex);
|
|
|
|
//
|
|
// If we found a challenge response table with no entries that we can
|
|
// process, then fail authentication. We need at least one entry in order
|
|
// to issue the final request to switch to the Xbox partition.
|
|
//
|
|
|
|
if (EndingIndex == -1) {
|
|
IdexDbgPrint(("IDEX: no usable challenge response entries.\n"));
|
|
return STATUS_TOO_MANY_SECRETS;
|
|
}
|
|
|
|
//
|
|
// Issue the last challenge response entry again, but this time switch to
|
|
// the Xbox partition.
|
|
//
|
|
|
|
HostChallengeResponseEntry = &ControlData.HostChallengeResponseTable.Entries[EndingIndex];
|
|
|
|
status = IdexCdRomAuthenticationChallenge(&ControlData,
|
|
HostChallengeResponseEntry, FALSE, TRUE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Remember that the disc has passed DVD-X2 authentication.
|
|
//
|
|
|
|
IdexCdRomDVDX2Authenticated = TRUE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
IdexCdRomDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is indirectly called by the I/O manager to handle
|
|
IRP_MJ_DEVICE_CONTROL requests.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Specifies the device object that the I/O request is for.
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
case IOCTL_CDROM_GET_LAST_SESSION:
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
|
|
case IOCTL_CDROM_RAW_READ:
|
|
case IOCTL_CDROM_SET_SPINDLE_SPEED:
|
|
case IOCTL_DVD_START_SESSION:
|
|
case IOCTL_DVD_READ_KEY:
|
|
case IOCTL_DVD_SEND_KEY:
|
|
case IOCTL_DVD_END_SESSION:
|
|
case IOCTL_DVD_SEND_KEY2:
|
|
case IOCTL_DVD_READ_STRUCTURE:
|
|
case IOCTL_IDE_PASS_THROUGH:
|
|
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
|
|
IoMarkIrpPending(Irp);
|
|
IdexChannelStartPacket(DeviceObject, Irp);
|
|
status = STATUS_PENDING;
|
|
break;
|
|
|
|
case IOCTL_CDROM_AUTHENTICATION_SEQUENCE:
|
|
status = IdexCdRomAuthenticationSequence();
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the request is no longer pending, then complete the request.
|
|
//
|
|
|
|
if (status != STATUS_PENDING) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomStartIo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the channel to handle the next queued I/O request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Specifies the device object that the I/O request is for.
|
|
|
|
Irp - Specifies the packet that describes the I/O request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
//
|
|
// If the console is preparing to reset or shutdown, there's no reason to
|
|
// continue processing this request.
|
|
//
|
|
|
|
if (HalIsResetOrShutdownPending()) {
|
|
IdexChannelAbortCurrentPacket();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Dispatch the IRP request.
|
|
//
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
switch (IrpSp->MajorFunction) {
|
|
|
|
case IRP_MJ_READ:
|
|
IdexCdRomStartRead(Irp);
|
|
break;
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
case IOCTL_CDROM_GET_LAST_SESSION:
|
|
IdexCdRomStartReadTOC(Irp);
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
IdexCdRomStartCheckVerify(Irp);
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
|
|
IdexCdRomStartGetDriveGeometry(Irp);
|
|
break;
|
|
|
|
case IOCTL_CDROM_RAW_READ:
|
|
IdexCdRomStartRawRead(Irp);
|
|
break;
|
|
|
|
case IOCTL_CDROM_SET_SPINDLE_SPEED:
|
|
IdexCdRomStartSetSpindleSpeed(Irp);
|
|
break;
|
|
|
|
case IOCTL_DVD_START_SESSION:
|
|
IdexCdRomStartStartSession(Irp);
|
|
break;
|
|
|
|
case IOCTL_DVD_READ_KEY:
|
|
IdexCdRomStartReadKey(Irp);
|
|
break;
|
|
|
|
case IOCTL_DVD_SEND_KEY:
|
|
case IOCTL_DVD_SEND_KEY2:
|
|
IdexCdRomStartSendKey(Irp);
|
|
break;
|
|
|
|
case IOCTL_DVD_END_SESSION:
|
|
IdexCdRomStartEndSession(Irp);
|
|
break;
|
|
|
|
case IOCTL_DVD_READ_STRUCTURE:
|
|
IdexCdRomStartReadStructure(Irp);
|
|
break;
|
|
|
|
case IOCTL_IDE_PASS_THROUGH:
|
|
IdexChannelStartIdePassThrough(Irp, IDE_CDROM_DEVICE_NUMBER,
|
|
IdexCdRomResetDevice);
|
|
break;
|
|
|
|
case IOCTL_SCSI_PASS_THROUGH_DIRECT:
|
|
IdexCdRomStartScsiPassThrough(Irp);
|
|
break;
|
|
|
|
default:
|
|
IdexBugCheck(IDE_BUG_CHECK_CDROM, Irp);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
IdexBugCheck(IDE_BUG_CHECK_CDROM, Irp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomTrayOpenNotification(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the SMC interrupt handler when a tray open event
|
|
has occurred.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|
|
|
//
|
|
// Reset any assumptions about the state of the media.
|
|
//
|
|
|
|
IdexCdRomPartitionLength = _I64_MAX;
|
|
IdexCdRomDVDX2Authenticated = FALSE;
|
|
|
|
#if DBG
|
|
//
|
|
// Reset any debug statistics.
|
|
//
|
|
|
|
RtlZeroMemory(IdexCdRomDebugErrors, sizeof(IdexCdRomDebugErrors));
|
|
IdexCdRomDebugReadsFinished = 0;
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomFatalError(
|
|
IN ULONG ErrorCode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when a fatal CD-ROM device related error is detected.
|
|
|
|
Arguments:
|
|
|
|
ErrorCode - Specifies the fatal error code.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
IdexDbgBreakPoint();
|
|
|
|
//
|
|
// Flush the LEDs to indicate that there is CD-ROM related problem.
|
|
//
|
|
|
|
HalWriteSMCLEDStates(SMC_LED_STATES_RED_STATE0 | SMC_LED_STATES_GREEN_STATE1 |
|
|
SMC_LED_STATES_GREEN_STATE2 | SMC_LED_STATES_GREEN_STATE3);
|
|
|
|
//
|
|
// Display the universal error message.
|
|
//
|
|
|
|
ExDisplayFatalError(ErrorCode);
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomCreateQuick(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the CD-ROM device subsystem after a quick reboot.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT CdRomDeviceObject;
|
|
DVDX2_AUTHENTICATION Authentication;
|
|
|
|
//
|
|
// Create the CD-ROM's device object.
|
|
//
|
|
|
|
status = IoCreateDevice(&IdexCdRomDriverObject, 0, &IdexCdRomDeviceName,
|
|
FILE_DEVICE_CD_ROM, FALSE, &CdRomDeviceObject);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexBugCheck(IDE_BUG_CHECK_CDROM, status);
|
|
}
|
|
|
|
//
|
|
// Create the \DosDevices symbolic link.
|
|
//
|
|
|
|
status = IoCreateSymbolicLink(&IdexCdRomDosDevicesName, &IdexCdRomDeviceName);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexBugCheck(IDE_BUG_CHECK_CDROM, status);
|
|
}
|
|
|
|
//
|
|
// Mark the device object as support direct I/O so that user buffers are
|
|
// passed down to us as locked buffers. Also indicate that we can accept
|
|
// scatter/gather buffers.
|
|
//
|
|
|
|
CdRomDeviceObject->Flags |= DO_DIRECT_IO | DO_SCATTER_GATHER_IO;
|
|
|
|
//
|
|
// Set the alignment requirement for the device.
|
|
//
|
|
|
|
CdRomDeviceObject->AlignmentRequirement = IDE_ALIGNMENT_REQUIREMENT;
|
|
|
|
//
|
|
// Set the size of a sector in bytes for the device.
|
|
//
|
|
|
|
CdRomDeviceObject->SectorSize = IDE_ATAPI_CD_SECTOR_SIZE;
|
|
|
|
//
|
|
// The device has finished initializing and is ready to accept requests.
|
|
//
|
|
|
|
CdRomDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
//
|
|
// Store the device object globally so that it can be quickly found later.
|
|
//
|
|
|
|
IdexCdRomDeviceObject = CdRomDeviceObject;
|
|
|
|
//
|
|
// If the kernel is quick booting and we think that the current media is
|
|
// DVD-X2, then verify that assumption by checking the DVD-X2 authentication
|
|
// page. This code is in place to prevent media swap piracy attacks.
|
|
//
|
|
|
|
if (KeHasQuickBooted && IdexCdRomDVDX2Authenticated) {
|
|
|
|
//
|
|
// Assume that the drive has not passed DVD-X2 authentication.
|
|
//
|
|
|
|
IdexCdRomDVDX2Authenticated = FALSE;
|
|
|
|
//
|
|
// Request the authentication page from the DVD-X2 drive.
|
|
//
|
|
|
|
status = IdexCdRomRequestDVDX2AuthenticationPage(&Authentication);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// If the DVD-X2 drive is in the Xbox partition and has successfully
|
|
// completed DVD-X2 authentication, then the media likely hasn't
|
|
// changed from the last time DVD-X2 authentication was performed.
|
|
//
|
|
|
|
if ((Authentication.AuthenticationPage.PartitionArea != 0) &&
|
|
(Authentication.AuthenticationPage.Authentication != 0)) {
|
|
IdexCdRomDVDX2Authenticated = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
IdexCdRomCreate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the CD-ROM device subsystem.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ULONG Retries;
|
|
LARGE_INTEGER Interval;
|
|
KIRQL OldIrql;
|
|
IDE_IDENTIFY_DATA IdentifyData;
|
|
ULONG IOErrors;
|
|
|
|
//
|
|
// Wait for the device to finish its hardware reset sequence. When cold
|
|
// booting, the IDE controller in the south bridge will do a hardware reset
|
|
// of the IDE bus, so we don't need to a software reset here.
|
|
//
|
|
// If we're booting a kernel that was loaded from the hard disk or CD-ROM,
|
|
// then the device should already be ready to go, so we can skip the
|
|
// initialization here.
|
|
//
|
|
|
|
HalPulseHardwareMonitorPin();
|
|
|
|
if (IdexIsFlagClear(XboxBootFlags, XBOX_BOOTFLAG_SHADOW)) {
|
|
|
|
//
|
|
// Spin for up to thirty seconds as required by the ATA specification.
|
|
//
|
|
|
|
for (Retries = 600; Retries > 0; Retries--) {
|
|
|
|
if (IdexCdRomPollResetComplete()) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Delay for 50 milliseconds.
|
|
//
|
|
|
|
Interval.QuadPart = -50 * 10000;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
|
|
}
|
|
|
|
if (Retries == 0) {
|
|
IdexDbgPrint(("IDEX: CD-ROM timed out during reset.\n"));
|
|
IdexCdRomFatalError(FATAL_ERROR_DVD_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The below calls may cause an interrupt to be raised, so while we're
|
|
// initializing the hardware, raise IRQL to synchronize with the interrupt
|
|
// service routine.
|
|
//
|
|
|
|
IdexRaiseIrqlToChannelDIRQL(&OldIrql);
|
|
|
|
//
|
|
// Set the PIO transfer mode for the CD-ROM device.
|
|
//
|
|
|
|
HalPulseHardwareMonitorPin();
|
|
|
|
status = IdexChannelSetTransferMode(IDE_CDROM_DEVICE_NUMBER,
|
|
IDE_TRANSFER_MODE_PIO_MODE_4);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexDbgPrint(("IDEX: CD-ROM not PIO configured (status=%02x).\n",
|
|
IdexReadStatusPort()));
|
|
KeLowerIrql(OldIrql);
|
|
IdexCdRomFatalError(FATAL_ERROR_DVD_BAD_CONFIG);
|
|
}
|
|
|
|
//
|
|
// Set the DMA transfer mode for the CD-ROM device.
|
|
//
|
|
|
|
HalPulseHardwareMonitorPin();
|
|
|
|
status = IdexChannelSetTransferMode(IDE_CDROM_DEVICE_NUMBER,
|
|
IDE_TRANSFER_MODE_UDMA_MODE_2);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexDbgPrint(("IDEX: CD-ROM not DMA configured (status=%02x).\n",
|
|
IdexReadStatusPort()));
|
|
KeLowerIrql(OldIrql);
|
|
IdexCdRomFatalError(FATAL_ERROR_DVD_BAD_CONFIG);
|
|
}
|
|
|
|
//
|
|
// Issue an ATAPI identify device command in order to verify that the device
|
|
// is present.
|
|
//
|
|
|
|
HalPulseHardwareMonitorPin();
|
|
|
|
status = IdexChannelIdentifyDevice(IDE_CDROM_DEVICE_NUMBER,
|
|
IDE_COMMAND_IDENTIFY_PACKET_DEVICE, &IdentifyData);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IdexDbgPrint(("IDEX: CD-ROM not found (status=%02x).\n",
|
|
IdexReadStatusPort()));
|
|
KeLowerIrql(OldIrql);
|
|
IdexCdRomFatalError(FATAL_ERROR_DVD_NOT_FOUND);
|
|
}
|
|
|
|
KeLowerIrql(OldIrql);
|
|
|
|
//
|
|
// At this point, we can use the quick boot creation code.
|
|
//
|
|
|
|
IdexCdRomCreateQuick();
|
|
|
|
//
|
|
// If we're cold-booting and we're ejecting the tray, then don't bother
|
|
// waiting for the CD-ROM device to become ready because the tray is about
|
|
// to be ejected and we're going to force a launch into the dashboard
|
|
// anyway.
|
|
//
|
|
|
|
HalPulseHardwareMonitorPin();
|
|
|
|
if (!KeHasQuickBooted &&
|
|
IdexIsFlagClear(XboxBootFlags, XBOX_BOOTFLAG_TRAYEJECT)) {
|
|
|
|
//
|
|
// Delay until the CD-ROM device reports that it's ready. We also handle
|
|
// a 6-29 error here which is returned as STATUS_IO_DEVICE_ERROR. We only
|
|
// want to handle a certain number of IO errors here.
|
|
//
|
|
|
|
IOErrors = 0;
|
|
|
|
while (IOErrors < 5) {
|
|
|
|
status = IoSynchronousDeviceIoControlRequest(IOCTL_CDROM_CHECK_VERIFY,
|
|
IdexCdRomDeviceObject, NULL, 0, NULL, 0, NULL, FALSE);
|
|
|
|
if (status == STATUS_IO_DEVICE_ERROR) {
|
|
IOErrors++;
|
|
} else if (status != STATUS_DEVICE_NOT_READY) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Delay for 200 milliseconds.
|
|
//
|
|
|
|
Interval.QuadPart = -200 * 10000;
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Driver object for the CD-ROM object.
|
|
//
|
|
DECLSPEC_RDATA DRIVER_OBJECT IdexCdRomDriverObject = {
|
|
IdexCdRomStartIo, // DriverStartIo
|
|
NULL, // DriverDeleteDevice
|
|
NULL, // DriverDismountVolume
|
|
{
|
|
IdexDriverIrpReturnSuccess, // IRP_MJ_CREATE
|
|
IdexDriverIrpReturnSuccess, // IRP_MJ_CLOSE
|
|
IdexCdRomRead, // IRP_MJ_READ
|
|
IoInvalidDeviceRequest, // IRP_MJ_WRITE
|
|
IoInvalidDeviceRequest, // IRP_MJ_QUERY_INFORMATION
|
|
IoInvalidDeviceRequest, // IRP_MJ_SET_INFORMATION
|
|
IoInvalidDeviceRequest, // IRP_MJ_FLUSH_BUFFERS
|
|
IoInvalidDeviceRequest, // IRP_MJ_QUERY_VOLUME_INFORMATION
|
|
IoInvalidDeviceRequest, // IRP_MJ_DIRECTORY_CONTROL
|
|
IoInvalidDeviceRequest, // IRP_MJ_FILE_SYSTEM_CONTROL
|
|
IdexCdRomDeviceControl, // IRP_MJ_DEVICE_CONTROL
|
|
IoInvalidDeviceRequest, // IRP_MJ_INTERNAL_DEVICE_CONTROL
|
|
IoInvalidDeviceRequest, // IRP_MJ_SHUTDOWN
|
|
IoInvalidDeviceRequest, // IRP_MJ_CLEANUP
|
|
}
|
|
};
|
|
|
|
#else
|
|
|
|
//
|
|
// Single instance of the CD-ROM device object.
|
|
//
|
|
PDEVICE_OBJECT IdexCdRomDeviceObject;
|
|
|
|
VOID
|
|
IdexCdRomTrayOpenNotification(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the SMC interrupt handler when a tray open event
|
|
has occurred.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
#endif
|