xbox-kernel/private/ntos/idex/cdrom.c.bak

3981 lines
100 KiB
C
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*++
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