|
|
/*--
Copyright (C) Microsoft Corporation, 1999 - 1999
Module Name:
ioctl.c
Abstract:
The CDROM class driver tranlates IRPs to SRBs with embedded CDBs and sends them to its devices through the port driver.
Environment:
kernel mode only
Notes:
SCSI Tape, CDRom and Disk class drivers share common routines that can be found in the CLASS directory (..\ntos\dd\class).
Revision History:
--*/
#include "stddef.h"
#include "string.h"
#include "ntddk.h"
#include "ntddcdvd.h"
#include "classpnp.h"
#include "initguid.h"
#include "ntddstor.h"
#include "cdrom.h"
#include "ioctl.tmh"
#if DBG
PUCHAR READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor+1] = { "Physical", "Copyright", "DiskKey", "BCA", "Manufacturer", "Unknown" }; #endif // DBG
#define DEFAULT_CDROM_SECTORS_PER_TRACK 32
#define DEFAULT_TRACKS_PER_CYLINDER 64
NTSTATUS CdRomDeviceControlDispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
This is the NT device control handler for CDROMs.
Arguments:
DeviceObject - for this CDROM
Irp - IO Request packet
Return Value:
NTSTATUS
--*/ { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION nextStack; PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData);
BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE); SCSI_REQUEST_BLOCK srb = {0}; PCDB cdb = (PCDB)srb.Cdb; PVOID outputBuffer; ULONG bytesTransferred = 0; NTSTATUS status; NTSTATUS status2; KIRQL irql;
ULONG ioctlCode; ULONG baseCode; ULONG functionCode;
RetryControl:
//
// Zero the SRB on stack.
//
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
Irp->IoStatus.Information = 0;
//
// if this is a class driver ioctl then we need to change the base code
// to IOCTL_CDROM_BASE so that the switch statement can handle it.
//
// WARNING - currently the scsi class ioctl function codes are between
// 0x200 & 0x300. this routine depends on that fact
//
ioctlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; baseCode = ioctlCode >> 16; functionCode = (ioctlCode & (~0xffffc003)) >> 2;
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Ioctl Code = %lx, Base Code = %lx," " Function Code = %lx\n", ioctlCode, baseCode, functionCode ));
if((functionCode >= 0x200) && (functionCode <= 0x300)) {
ioctlCode = (ioctlCode & 0x0000ffff) | CTL_CODE(IOCTL_CDROM_BASE, 0, 0, 0);
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Class Code - new ioctl code is %lx\n", ioctlCode));
irpStack->Parameters.DeviceIoControl.IoControlCode = ioctlCode;
}
switch (ioctlCode) {
case IOCTL_STORAGE_GET_MEDIA_TYPES_EX: {
PGET_MEDIA_TYPES mediaTypes = Irp->AssociatedIrp.SystemBuffer; PDEVICE_MEDIA_INFO mediaInfo = &mediaTypes->MediaInfo[0]; ULONG sizeNeeded;
sizeNeeded = sizeof(GET_MEDIA_TYPES);
//
// IsMmc is static...
//
if (cdData->Mmc.IsMmc) { sizeNeeded += sizeof(DEVICE_MEDIA_INFO) * 1; // return two media types
}
//
// Ensure that buffer is large enough.
//
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeNeeded) {
//
// Buffer too small.
//
Irp->IoStatus.Information = sizeNeeded; status = STATUS_BUFFER_TOO_SMALL; break; }
RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, sizeNeeded);
//
// ISSUE-2000/5/11-henrygab - need to update GET_MEDIA_TYPES_EX
//
mediaTypes->DeviceType = CdRomGetDeviceType(DeviceObject);
mediaTypes->MediaInfoCount = 1; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = CD_ROM; mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_ONLY; mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart; mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
if (cdData->Mmc.IsMmc) {
//
// also report a removable disk
//
mediaTypes->MediaInfoCount += 1;
mediaInfo++; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaType = RemovableMedia; mediaInfo->DeviceSpecific.RemovableDiskInfo.NumberMediaSides = 1; mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics = MEDIA_READ_WRITE; mediaInfo->DeviceSpecific.RemovableDiskInfo.Cylinders.QuadPart = fdoExtension->DiskGeometry.Cylinders.QuadPart; mediaInfo->DeviceSpecific.RemovableDiskInfo.TracksPerCylinder = fdoExtension->DiskGeometry.TracksPerCylinder; mediaInfo->DeviceSpecific.RemovableDiskInfo.SectorsPerTrack = fdoExtension->DiskGeometry.SectorsPerTrack; mediaInfo->DeviceSpecific.RemovableDiskInfo.BytesPerSector = fdoExtension->DiskGeometry.BytesPerSector; mediaInfo--;
}
//
// Status will either be success, if media is present, or no media.
// It would be optimal to base from density code and medium type, but not all devices
// have values for these fields.
//
//
// Send a TUR to determine if media is present.
//
srb.CdbLength = 6; cdb = (PCDB)srb.Cdb; cdb->CDB6GENERIC.OperationCode = SCSIOP_TEST_UNIT_READY;
//
// Set timeout value.
//
srb.TimeOutValue = fdoExtension->TimeOutValue;
status = ClassSendSrbSynchronous(DeviceObject, &srb, NULL, 0, FALSE);
TraceLog((CdromDebugWarning, "CdRomDeviceControl: GET_MEDIA_TYPES status of TUR - %lx\n", status));
if (NT_SUCCESS(status)) {
//
// set the disk's media as current if we can write to it.
//
if (cdData->Mmc.IsMmc && cdData->Mmc.WriteAllowed) {
mediaInfo++; SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED); mediaInfo--;
} else {
SET_FLAG(mediaInfo->DeviceSpecific.RemovableDiskInfo.MediaCharacteristics, MEDIA_CURRENTLY_MOUNTED);
}
}
Irp->IoStatus.Information = sizeNeeded; status = STATUS_SUCCESS; break; }
case IOCTL_CDROM_RAW_READ: {
LARGE_INTEGER startingOffset = {0}; ULONGLONG transferBytes; ULONGLONG endOffset; ULONGLONG mdlBytes; ULONG startingSector; RAW_READ_INFO rawReadInfo;
//
// Ensure that XA reads are supported.
//
if (TEST_FLAG(cdData->XAFlags, XA_NOT_SUPPORTED)) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: XA Reads not supported. Flags (%x)\n", cdData->XAFlags)); status = STATUS_INVALID_DEVICE_REQUEST; break; }
//
// Check that ending sector is on disc and buffers are there and of
// correct size.
//
if (!irpStack->Parameters.DeviceIoControl.Type3InputBuffer){ /*
* This is a call from user space. This is the only time that we need to validate parameters. * Validate the input and get the input buffer into Type3InputBuffer * so the rest of the code will be uniform. */ if (Irp->AssociatedIrp.SystemBuffer){ irpStack->Parameters.DeviceIoControl.Type3InputBuffer = Irp->AssociatedIrp.SystemBuffer; if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(RAW_READ_INFO)){ status = STATUS_BUFFER_TOO_SMALL; break; } } else { status = STATUS_INVALID_PARAMETER; break; } }
/*
* Since this ioctl is METHOD_OUT_DIRECT, we need to copy away the input buffer before interpreting it. * This prevents a malicious app from messing with the input buffer while we are interpreting it. */ rawReadInfo = *(PRAW_READ_INFO)irpStack->Parameters.DeviceIoControl.Type3InputBuffer;
startingOffset.QuadPart = rawReadInfo.DiskOffset.QuadPart; startingSector = (ULONG)(rawReadInfo.DiskOffset.QuadPart >> fdoExtension->SectorShift); transferBytes = (ULONGLONG)rawReadInfo.SectorCount * RAW_SECTOR_SIZE;
endOffset = (ULONGLONG)rawReadInfo.SectorCount * COOKED_SECTOR_SIZE; endOffset += startingOffset.QuadPart;
//
// check for overflows....
//
if (rawReadInfo.SectorCount == 0) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: Invalid I/O parameters for XA " "Read (zero sectors requested)\n")); status = STATUS_INVALID_PARAMETER; break; } if (transferBytes < (ULONGLONG)(rawReadInfo.SectorCount)) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: Invalid I/O parameters for XA " "Read (TransferBytes Overflow)\n")); status = STATUS_INVALID_PARAMETER; break; } if (endOffset < (ULONGLONG)startingOffset.QuadPart) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: Invalid I/O parameters for XA " "Read (EndingOffset Overflow)\n")); status = STATUS_INVALID_PARAMETER; break; } if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < transferBytes) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: Invalid I/O parameters for XA " "Read (Bad buffer size)\n")); status = STATUS_INVALID_PARAMETER; break; } if (endOffset > (ULONGLONG)commonExtension->PartitionLength.QuadPart) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: Invalid I/O parameters for XA " "Read (Request Out of Bounds)\n")); status = STATUS_INVALID_PARAMETER; break; }
//
// cannot validate the MdlAddress, since it is not included in any
// other location per the DDK and file system calls.
//
//
// validate the mdl describes at least the number of bytes
// requested from us.
//
mdlBytes = (ULONGLONG)MmGetMdlByteCount(Irp->MdlAddress); if (mdlBytes < transferBytes) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: Invalid MDL %s, Irp %p\n", "size (5)", Irp)); status = STATUS_INVALID_PARAMETER; break; }
//
// check the buffer for alignment
// This is important for x86 as some busses (ie ATAPI)
// require word-aligned buffers.
//
if ( ((ULONG_PTR)MmGetMdlVirtualAddress(Irp->MdlAddress)) & fdoExtension->AdapterDescriptor->AlignmentMask ) { TraceLog((CdromDebugWarning, "CdRomDeviceControl: Invalid I/O parameters for " "XA Read (Buffer %p not aligned with mask %x\n", irpStack->Parameters.DeviceIoControl.Type3InputBuffer, fdoExtension->AdapterDescriptor->AlignmentMask)); status = STATUS_INVALID_PARAMETER; break; }
//
// HACKHACK - REF #0001
// The retry count will be in this irp's IRP_MN function,
// as the new irp was freed, and we therefore cannot use
// this irp's next stack location for this function.
// This may be a good location to store this info for
// when we remove RAW_READ (mode switching), as we will
// no longer have the nextIrpStackLocation to play with
// when that occurs
//
// once XA_READ is removed, then this hack can also be
// removed.
//
irpStack->MinorFunction = MAXIMUM_RETRIES; // HACKHACK - REF #0001
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: { TraceLog((CdromDebugTrace, "CdRomDeviceControl: Get drive geometryEx\n")); if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength < FIELD_OFFSET(DISK_GEOMETRY_EX, Data)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = FIELD_OFFSET(DISK_GEOMETRY_EX, Data); break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING; }
case IOCTL_DISK_GET_DRIVE_GEOMETRY: case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Get drive geometry\n"));
if ( irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof( DISK_GEOMETRY ) ) {
status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_READ_TOC_EX: {
PCDROM_READ_TOC_EX inputBuffer;
if (CdRomIsPlayActive(DeviceObject)) { status = STATUS_DEVICE_BUSY; break; }
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_READ_TOC_EX)) { status = STATUS_INFO_LENGTH_MISMATCH; break; }
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = MINIMUM_CDROM_READ_TOC_EX_SIZE; break; }
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength > ((USHORT)-1)) { status = STATUS_INVALID_PARAMETER; break; }
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength & fdoExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; break; }
inputBuffer = Irp->AssociatedIrp.SystemBuffer;
if ((inputBuffer->Reserved1 != 0) || (inputBuffer->Reserved2 != 0) || (inputBuffer->Reserved3 != 0)) { status = STATUS_INVALID_PARAMETER; break; }
//
// NOTE: when adding new formats, ensure that first two bytes
// specify the amount of additional data available.
//
if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_TOC ) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_FULL_TOC) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_CDTEXT )) {
// SessionTrack field is used
} else if ((inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_SESSION) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_PMA) || (inputBuffer->Format == CDROM_READ_TOC_EX_FORMAT_ATIP)) {
// SessionTrack field is reserved
if (inputBuffer->SessionTrack != 0) { status = STATUS_INVALID_PARAMETER; break; }
} else { status = STATUS_INVALID_PARAMETER; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING; }
case IOCTL_CDROM_GET_LAST_SESSION: {
//
// If the cd is playing music then reject this request.
//
if (CdRomIsPlayActive(DeviceObject)) { status = STATUS_DEVICE_BUSY; break; }
//
// Make sure the caller is requesting enough data to make this worth
// our while.
//
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC_SESSION_DATA)) {
//
// they didn't request the entire TOC -- use _EX version
// for partial transfers and such.
//
status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(CDROM_TOC_SESSION_DATA); break; }
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength & fdoExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_READ_TOC: {
//
// If the cd is playing music then reject this request.
//
if (CdRomIsPlayActive(DeviceObject)) { status = STATUS_DEVICE_BUSY; break; }
//
// Make sure the caller is requesting enough data to make this worth
// our while.
//
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CDROM_TOC)) {
//
// they didn't request the entire TOC -- use _EX version
// for partial transfers and such.
//
status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(CDROM_TOC); break; } if (irpStack->Parameters.DeviceIoControl.OutputBufferLength & fdoExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_PLAY_AUDIO_MSF: {
//
// Play Audio MSF
//
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Play audio MSF\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_PLAY_AUDIO_MSF)) {
//
// Indicate unsuccessful status.
//
status = STATUS_INFO_LENGTH_MISMATCH; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_SEEK_AUDIO_MSF: {
//
// Seek Audio MSF
//
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Seek audio MSF\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SEEK_AUDIO_MSF)) {
//
// Indicate unsuccessful status.
//
status = STATUS_INFO_LENGTH_MISMATCH; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING;
}
case IOCTL_CDROM_PAUSE_AUDIO: {
//
// Pause audio
//
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Pause audio\n"));
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
break; }
case IOCTL_CDROM_RESUME_AUDIO: {
//
// Resume audio
//
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Resume audio\n"));
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_READ_Q_CHANNEL: {
PCDROM_SUB_Q_DATA_FORMAT inputBuffer = Irp->AssociatedIrp.SystemBuffer; ULONG transferByteCount;
if(irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(CDROM_SUB_Q_DATA_FORMAT)) {
status = STATUS_INFO_LENGTH_MISMATCH; break; }
//
// check for all valid types of request
//
if (inputBuffer->Format == IOCTL_CDROM_CURRENT_POSITION) { transferByteCount = sizeof(SUB_Q_CURRENT_POSITION); } else if (inputBuffer->Format == IOCTL_CDROM_MEDIA_CATALOG) { transferByteCount = sizeof(SUB_Q_MEDIA_CATALOG_NUMBER); } else if (inputBuffer->Format == IOCTL_CDROM_TRACK_ISRC) { transferByteCount = sizeof(SUB_Q_TRACK_ISRC); } else { status = STATUS_INVALID_PARAMETER; Irp->IoStatus.Information = 0; break; }
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < transferByteCount) { Irp->IoStatus.Information = transferByteCount; status = STATUS_BUFFER_TOO_SMALL; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_GET_VOLUME: {
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Get volume control\n"));
//
// Verify user buffer is large enough for data.
//
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(VOLUME_CONTROL)) {
//
// Indicate unsuccessful status and no data transferred.
//
status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(VOLUME_CONTROL); break;
}
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_SET_VOLUME: {
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Set volume control\n"));
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VOLUME_CONTROL)) {
//
// Indicate unsuccessful status.
//
status = STATUS_INFO_LENGTH_MISMATCH; break;
}
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_CDROM_STOP_AUDIO: {
//
// Stop play.
//
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Stop audio\n"));
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_STORAGE_CHECK_VERIFY: case IOCTL_DISK_CHECK_VERIFY: case IOCTL_CDROM_CHECK_VERIFY: {
TraceLog((CdromDebugTrace, "CdRomDeviceControl: [%p] Check Verify\n", Irp));
if((irpStack->Parameters.DeviceIoControl.OutputBufferLength) && (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))) {
TraceLog((CdromDebugWarning, "CdRomDeviceControl: Check Verify: media count " "buffer too small\n"));
status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(ULONG); break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_DVD_READ_STRUCTURE: {
TraceLog((CdromDebugTrace, "DvdDeviceControl: [%p] IOCTL_DVD_READ_STRUCTURE\n", Irp));
if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { TraceLog((CdromDebugWarning, "DvdDeviceControl: License Failure\n")); status = STATUS_COPY_PROTECTION_FAILURE; break; }
if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) { //
// if currently in-progress, this will just return.
// prevents looping by doing that interlockedExchange()
//
TraceLog((CdromDebugWarning, "DvdDeviceControl: PickRegion() from " "READ_STRUCTURE\n")); CdRomPickDvdRegion(DeviceObject); }
if(irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_READ_STRUCTURE)) {
TraceLog((CdromDebugWarning, "DvdDeviceControl - READ_STRUCTURE: input buffer " "length too small (was %d should be %d)\n", irpStack->Parameters.DeviceIoControl.InputBufferLength, sizeof(DVD_READ_STRUCTURE))); status = STATUS_INVALID_PARAMETER; break; }
if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(READ_DVD_STRUCTURES_HEADER)) {
TraceLog((CdromDebugWarning, "DvdDeviceControl - READ_STRUCTURE: output buffer " "cannot hold header information\n")); status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(READ_DVD_STRUCTURES_HEADER); break; }
if(irpStack->Parameters.DeviceIoControl.OutputBufferLength > MAXUSHORT) {
//
// key length must fit in two bytes
//
TraceLog((CdromDebugWarning, "DvdDeviceControl - READ_STRUCTURE: output buffer " "too large\n")); status = STATUS_INVALID_PARAMETER; break; } if (irpStack->Parameters.DeviceIoControl.OutputBufferLength & fdoExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_DVD_START_SESSION: {
TraceLog((CdromDebugTrace, "DvdDeviceControl: [%p] IOCTL_DVD_START_SESSION\n", Irp));
if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { TraceLog((CdromDebugWarning, "DvdDeviceControl: License Failure\n")); status = STATUS_COPY_PROTECTION_FAILURE; break; }
if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DVD_SESSION_ID)) {
TraceLog((CdromDebugWarning, "DvdDeviceControl: DVD_START_SESSION - output " "buffer too small\n")); status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(DVD_SESSION_ID); break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_DVD_SEND_KEY: case IOCTL_DVD_SEND_KEY2: {
PDVD_COPY_PROTECT_KEY key = Irp->AssociatedIrp.SystemBuffer; ULONG keyLength;
TraceLog((CdromDebugTrace, "DvdDeviceControl: [%p] IOCTL_DVD_SEND_KEY\n", Irp));
if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { TraceLog((CdromDebugWarning, "DvdDeviceControl: License Failure\n")); status = STATUS_COPY_PROTECTION_FAILURE; break; }
if((irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY)) || (irpStack->Parameters.DeviceIoControl.InputBufferLength != key->KeyLength)) {
//
// Key is too small to have a header or the key length doesn't
// match the input buffer length. Key must be invalid
//
TraceLog((CdromDebugWarning, "DvdDeviceControl: [%p] IOCTL_DVD_SEND_KEY - " "key is too small or does not match KeyLength\n", Irp)); status = STATUS_INVALID_PARAMETER; break; }
//
// allow only certain key type (non-destructive) to go through
// IOCTL_DVD_SEND_KEY (which only requires READ access to the device)
//
if (ioctlCode == IOCTL_DVD_SEND_KEY) {
if ((key->KeyType != DvdChallengeKey) && (key->KeyType != DvdBusKey2) && (key->KeyType != DvdInvalidateAGID)) {
status = STATUS_INVALID_PARAMETER; break; } } else {
if ((key->KeyType != DvdChallengeKey) && (key->KeyType != DvdBusKey1) && (key->KeyType != DvdBusKey2) && (key->KeyType != DvdTitleKey) && (key->KeyType != DvdAsf) && (key->KeyType != DvdSetRpcKey) && (key->KeyType != DvdGetRpcKey) && (key->KeyType != DvdDiskKey) && (key->KeyType != DvdInvalidateAGID)) { status = STATUS_INVALID_PARAMETER; break; }
}
if (cdData->DvdRpc0Device) {
if (key->KeyType == DvdSetRpcKey) {
PDVD_SET_RPC_KEY rpcKey = (PDVD_SET_RPC_KEY) key->KeyData;
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < DVD_SET_RPC_KEY_LENGTH) {
status = STATUS_INVALID_PARAMETER; break; }
//
// we have a request to set region code
// on a RPC0 device which doesn't support
// region coding.
//
// we have to fake it.
//
KeWaitForMutexObject( &cdData->Rpc0RegionMutex, UserRequest, KernelMode, FALSE, NULL );
if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) { //
// if currently in-progress, this will just return.
// prevents looping by doing that interlockedExchange()
//
TraceLog((CdromDebugWarning, "DvdDeviceControl: PickRegion() from " "SEND_KEY\n")); CdRomPickDvdRegion(DeviceObject); }
if (cdData->Rpc0SystemRegion == rpcKey->PreferredDriveRegionCode) {
//
// nothing to change
//
TraceLog((CdromDebugWarning, "DvdDeviceControl (%p) => not changing " "regions -- requesting current region\n", DeviceObject)); status = STATUS_SUCCESS;
} else if (cdData->Rpc0SystemRegionResetCount == 0) {
//
// not allowed to change it again
//
TraceLog((CdromDebugWarning, "DvdDeviceControl (%p) => no more region " "changes are allowed for this device\n", DeviceObject)); status = STATUS_CSS_RESETS_EXHAUSTED;
} else {
ULONG i; UCHAR mask; ULONG bufferLen; PDVD_READ_STRUCTURE dvdReadStructure; PDVD_COPYRIGHT_DESCRIPTOR dvdCopyRight; IO_STATUS_BLOCK ioStatus = {0}; UCHAR mediaRegionData;
mask = ~rpcKey->PreferredDriveRegionCode;
if (CountOfSetBitsUChar(mask) != 1) {
status = STATUS_INVALID_DEVICE_REQUEST; break; }
//
// this test will always be TRUE except during initial
// automatic selection of the first region.
//
if (cdData->Rpc0SystemRegion != 0xff) {
//
// make sure we have a media in the drive with the same
// region code if the drive is already has a region set
//
TraceLog((CdromDebugTrace, "DvdDeviceControl (%p) => Checking " "media region\n", DeviceObject));
bufferLen = max(sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR), sizeof(DVD_READ_STRUCTURE) );
dvdReadStructure = (PDVD_READ_STRUCTURE) ExAllocatePoolWithTag(PagedPool, bufferLen, DVD_TAG_RPC2_CHECK);
if (dvdReadStructure == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); break; }
dvdCopyRight = (PDVD_COPYRIGHT_DESCRIPTOR) ((PDVD_DESCRIPTOR_HEADER) dvdReadStructure)->Data;
//
// check to see if we have a DVD device
//
RtlZeroMemory (dvdReadStructure, bufferLen); dvdReadStructure->Format = DvdCopyrightDescriptor;
//
// Build a request for READ_KEY
//
ClassSendDeviceIoControlSynchronous( IOCTL_DVD_READ_STRUCTURE, DeviceObject, dvdReadStructure, sizeof(DVD_READ_STRUCTURE), sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR), FALSE, &ioStatus);
//
// this is just to prevent bugs from creeping in
// if status is not set later in development
//
status = ioStatus.Status;
//
// handle errors
//
if (!NT_SUCCESS(status)) { KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); ExFreePool(dvdReadStructure); status = STATUS_INVALID_DEVICE_REQUEST; break; }
//
// save the mediaRegionData before freeing the
// allocated memory
//
mediaRegionData = dvdCopyRight->RegionManagementInformation; ExFreePool(dvdReadStructure);
TraceLog((CdromDebugWarning, "DvdDeviceControl (%p) => new mask is %x" " MediaRegionData is %x\n", DeviceObject, rpcKey->PreferredDriveRegionCode, mediaRegionData));
//
// the media region must match the requested region
// for RPC0 drives for initial region selection
//
if (((UCHAR)~(mediaRegionData | rpcKey->PreferredDriveRegionCode)) == 0) { KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); status = STATUS_CSS_REGION_MISMATCH; break; }
}
//
// now try to set the region
//
TraceLog((CdromDebugTrace, "DvdDeviceControl (%p) => Soft-Setting " "region of RPC1 device to %x\n", DeviceObject, rpcKey->PreferredDriveRegionCode ));
status = CdRomSetRpc0Settings(DeviceObject, rpcKey->PreferredDriveRegionCode);
if (!NT_SUCCESS(status)) { TraceLog((CdromDebugWarning, "DvdDeviceControl (%p) => Could not " "set region code (%x)\n", DeviceObject, status )); } else {
TraceLog((CdromDebugTrace, "DvdDeviceControl (%p) => New region set " " for RPC1 drive\n", DeviceObject));
//
// if it worked, our extension is already updated.
// release the mutex
//
DebugPrint ((4, "DvdDeviceControl (%p) => DVD current " "region bitmap 0x%x\n", DeviceObject, cdData->Rpc0SystemRegion)); DebugPrint ((4, "DvdDeviceControl (%p) => DVD region " " reset Count 0x%x\n", DeviceObject, cdData->Rpc0SystemRegionResetCount)); }
}
KeReleaseMutex(&cdData->Rpc0RegionMutex,FALSE); break; } // end of key->KeyType == DvdSetRpcKey
} // end of Rpc0Device hacks
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING; break; }
case IOCTL_DVD_READ_KEY: {
PDVD_COPY_PROTECT_KEY keyParameters = Irp->AssociatedIrp.SystemBuffer; ULONG keyLength;
TraceLog((CdromDebugTrace, "DvdDeviceControl: [%p] IOCTL_DVD_READ_KEY\n", Irp));
if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { TraceLog((CdromDebugWarning, "DvdDeviceControl: License Failure\n")); status = STATUS_COPY_PROTECTION_FAILURE; break; }
if (cdData->DvdRpc0Device && cdData->Rpc0RetryRegistryCallback) { TraceLog((CdromDebugWarning, "DvdDeviceControl: PickRegion() from READ_KEY\n")); CdRomPickDvdRegion(DeviceObject); }
if(irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_COPY_PROTECT_KEY)) {
TraceLog((CdromDebugWarning, "DvdDeviceControl: EstablishDriveKey - challenge " "key buffer too small\n"));
status = STATUS_INVALID_PARAMETER; break;
}
switch(keyParameters->KeyType) {
case DvdChallengeKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_CHALLENGE_KEY_LENGTH); keyLength = DVD_CHALLENGE_KEY_LENGTH; break; } case DvdBusKey1: case DvdBusKey2: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_BUS_KEY_LENGTH); keyLength = DVD_BUS_KEY_LENGTH; break; } case DvdTitleKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_TITLE_KEY_LENGTH); keyLength = DVD_TITLE_KEY_LENGTH; break; } case DvdAsf: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_ASF_LENGTH); keyLength = DVD_ASF_LENGTH; break; } case DvdDiskKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_DISK_KEY_LENGTH); keyLength = DVD_DISK_KEY_LENGTH; break; } case DvdGetRpcKey: { C_ASSERT(sizeof(DVD_COPY_PROTECT_KEY) <= DVD_RPC_KEY_LENGTH); keyLength = DVD_RPC_KEY_LENGTH; break; } default: { keyLength = sizeof(DVD_COPY_PROTECT_KEY); break; } }
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < keyLength) {
TraceLog((CdromDebugWarning, "DvdDeviceControl: EstablishDriveKey - output " "buffer too small\n")); status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = keyLength; break; } if (irpStack->Parameters.DeviceIoControl.OutputBufferLength & fdoExtension->AdapterDescriptor->AlignmentMask) { status = STATUS_INVALID_PARAMETER; break; }
if (keyParameters->KeyType == DvdGetRpcKey) {
CdRomPickDvdRegion(DeviceObject); }
if ((keyParameters->KeyType == DvdGetRpcKey) && (cdData->DvdRpc0Device)) {
PDVD_RPC_KEY rpcKey; rpcKey = (PDVD_RPC_KEY)keyParameters->KeyData; RtlZeroMemory (rpcKey, sizeof (*rpcKey));
KeWaitForMutexObject( &cdData->Rpc0RegionMutex, UserRequest, KernelMode, FALSE, NULL );
//
// make up the data
//
rpcKey->UserResetsAvailable = cdData->Rpc0SystemRegionResetCount; rpcKey->ManufacturerResetsAvailable = 0; if (cdData->Rpc0SystemRegion == 0xff) { rpcKey->TypeCode = 0; } else { rpcKey->TypeCode = 1; } rpcKey->RegionMask = (UCHAR) cdData->Rpc0SystemRegion; rpcKey->RpcScheme = 1;
KeReleaseMutex( &cdData->Rpc0RegionMutex, FALSE );
Irp->IoStatus.Information = DVD_RPC_KEY_LENGTH; status = STATUS_SUCCESS; break;
} else if (keyParameters->KeyType == DvdDiskKey) {
PDVD_COPY_PROTECT_KEY keyHeader; PDVD_READ_STRUCTURE readStructureRequest;
//
// Special case - build a request to get the dvd structure
// so we can get the disk key.
//
//
// save the key header so we can restore the interesting
// parts later
//
keyHeader = ExAllocatePoolWithTag(NonPagedPool, sizeof(DVD_COPY_PROTECT_KEY), DVD_TAG_READ_KEY);
if(keyHeader == NULL) {
//
// Can't save the context so return an error
//
TraceLog((CdromDebugWarning, "DvdDeviceControl - READ_KEY: unable to " "allocate context\n")); status = STATUS_INSUFFICIENT_RESOURCES; break; }
RtlCopyMemory(keyHeader, Irp->AssociatedIrp.SystemBuffer, sizeof(DVD_COPY_PROTECT_KEY));
IoCopyCurrentIrpStackLocationToNext(Irp);
nextStack = IoGetNextIrpStackLocation(Irp);
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_DVD_READ_STRUCTURE;
readStructureRequest = Irp->AssociatedIrp.SystemBuffer; readStructureRequest->Format = DvdDiskKeyDescriptor; readStructureRequest->BlockByteOffset.QuadPart = 0; readStructureRequest->LayerNumber = 0; readStructureRequest->SessionId = keyHeader->SessionId;
nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(DVD_READ_STRUCTURE);
nextStack->Parameters.DeviceIoControl.OutputBufferLength = sizeof(READ_DVD_STRUCTURES_HEADER) + sizeof(DVD_DISK_KEY_DESCRIPTOR);
IoSetCompletionRoutine(Irp, CdRomDvdReadDiskKeyCompletion, (PVOID) keyHeader, TRUE, TRUE, TRUE);
{ UCHAR uniqueAddress; ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress); ClassReleaseRemoveLock(DeviceObject, Irp);
IoMarkIrpPending(Irp); IoCallDriver(commonExtension->DeviceObject, Irp); status = STATUS_PENDING;
ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress); }
return STATUS_PENDING;
} else {
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
} return STATUS_PENDING; }
case IOCTL_DVD_END_SESSION: {
PDVD_SESSION_ID sessionId = Irp->AssociatedIrp.SystemBuffer;
TraceLog((CdromDebugTrace, "DvdDeviceControl: [%p] END_SESSION\n", Irp));
if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { TraceLog((CdromDebugWarning, "DvdDeviceControl: License Failure\n")); status = STATUS_COPY_PROTECTION_FAILURE; break; }
if(irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(DVD_SESSION_ID)) {
TraceLog((CdromDebugWarning, "DvdDeviceControl: EndSession - input buffer too " "small\n")); status = STATUS_INVALID_PARAMETER; break; }
IoMarkIrpPending(Irp);
if(*sessionId == DVD_END_ALL_SESSIONS) {
status = CdRomDvdEndAllSessionsCompletion(DeviceObject, Irp, NULL);
if(status == STATUS_SUCCESS) {
//
// Just complete the request - it was never issued to the
// lower device
//
break;
} else {
return STATUS_PENDING;
} }
IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_DVD_GET_REGION: {
PDVD_COPY_PROTECT_KEY copyProtectKey; ULONG keyLength; IO_STATUS_BLOCK ioStatus = {0}; PDVD_DESCRIPTOR_HEADER dvdHeader; PDVD_COPYRIGHT_DESCRIPTOR copyRightDescriptor; PDVD_REGION dvdRegion; PDVD_READ_STRUCTURE readStructure; PDVD_RPC_KEY rpcKey;
TraceLog((CdromDebugTrace, "DvdDeviceControl: [%p] IOCTL_DVD_GET_REGION\n", Irp));
if (cdData->DvdRpc0Device && cdData->DvdRpc0LicenseFailure) { TraceLog((CdromDebugWarning, "DvdDeviceControl: License Failure\n")); status = STATUS_COPY_PROTECTION_FAILURE; break; }
if(irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DVD_REGION)) {
TraceLog((CdromDebugWarning, "DvdDeviceControl: output buffer DVD_REGION too small\n")); status = STATUS_INVALID_PARAMETER; break; }
//
// figure out how much data buffer we need
//
keyLength = max(sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR), sizeof(DVD_READ_STRUCTURE) ); keyLength = max(keyLength, DVD_RPC_KEY_LENGTH );
//
// round the size to nearest ULONGLONG -- why?
// could this be to deal with device alignment issues?
//
keyLength += sizeof(ULONGLONG) - (keyLength & (sizeof(ULONGLONG) - 1));
readStructure = ExAllocatePoolWithTag(NonPagedPool, keyLength, DVD_TAG_READ_KEY); if (readStructure == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; break; }
RtlZeroMemory (readStructure, keyLength); readStructure->Format = DvdCopyrightDescriptor;
//
// Build a request for READ_STRUCTURE
//
ClassSendDeviceIoControlSynchronous( IOCTL_DVD_READ_STRUCTURE, DeviceObject, readStructure, keyLength, sizeof(DVD_DESCRIPTOR_HEADER) + sizeof(DVD_COPYRIGHT_DESCRIPTOR), FALSE, &ioStatus);
status = ioStatus.Status;
if (!NT_SUCCESS(status)) { TraceLog((CdromDebugWarning, "CdRomDvdGetRegion => read structure failed %x\n", status)); ExFreePool(readStructure); break; }
//
// we got the copyright descriptor, so now get the region if possible
//
dvdHeader = (PDVD_DESCRIPTOR_HEADER) readStructure; copyRightDescriptor = (PDVD_COPYRIGHT_DESCRIPTOR) dvdHeader->Data;
//
// the original irp's systembuffer has a copy of the info that
// should be passed down in the request
//
dvdRegion = Irp->AssociatedIrp.SystemBuffer;
dvdRegion->CopySystem = copyRightDescriptor->CopyrightProtectionType; dvdRegion->RegionData = copyRightDescriptor->RegionManagementInformation;
//
// now reuse the buffer to request the copy protection info
//
copyProtectKey = (PDVD_COPY_PROTECT_KEY) readStructure; RtlZeroMemory (copyProtectKey, DVD_RPC_KEY_LENGTH); copyProtectKey->KeyLength = DVD_RPC_KEY_LENGTH; copyProtectKey->KeyType = DvdGetRpcKey;
//
// send a request for READ_KEY
//
ClassSendDeviceIoControlSynchronous( IOCTL_DVD_READ_KEY, DeviceObject, copyProtectKey, DVD_RPC_KEY_LENGTH, DVD_RPC_KEY_LENGTH, FALSE, &ioStatus); status = ioStatus.Status;
if (!NT_SUCCESS(status)) { TraceLog((CdromDebugWarning, "CdRomDvdGetRegion => read key failed %x\n", status)); ExFreePool(readStructure); break; }
//
// the request succeeded. if a supported scheme is returned,
// then return the information to the caller
//
rpcKey = (PDVD_RPC_KEY) copyProtectKey->KeyData;
if (rpcKey->RpcScheme == 1) {
if (rpcKey->TypeCode) {
dvdRegion->SystemRegion = ~rpcKey->RegionMask; dvdRegion->ResetCount = rpcKey->UserResetsAvailable;
} else {
//
// the drive has not been set for any region
//
dvdRegion->SystemRegion = 0; dvdRegion->ResetCount = rpcKey->UserResetsAvailable; } Irp->IoStatus.Information = sizeof(DVD_REGION);
} else {
TraceLog((CdromDebugWarning, "CdRomDvdGetRegion => rpcKey->RpcScheme != 1\n")); status = STATUS_INVALID_DEVICE_REQUEST; }
ExFreePool(readStructure); break; }
case IOCTL_STORAGE_SET_READ_AHEAD: {
if(irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(STORAGE_SET_READ_AHEAD)) {
TraceLog((CdromDebugWarning, "DvdDeviceControl: SetReadAhead buffer too small\n")); status = STATUS_INVALID_PARAMETER; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING; }
case IOCTL_DISK_IS_WRITABLE: {
// all verification done in StartIo()
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL);
return STATUS_PENDING;
}
case IOCTL_DISK_GET_DRIVE_LAYOUT: {
ULONG size;
//
// we always fake zero or one partitions, and one partition
// structure is included in DRIVE_LAYOUT_INFORMATION
//
size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION, PartitionEntry[1]);
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Get drive layout\n")); if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < size) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = size; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING;
} case IOCTL_DISK_GET_DRIVE_LAYOUT_EX: {
ULONG size;
//
// we always fake zero or one partitions, and one partition
// structure is included in DRIVE_LAYOUT_INFORMATION_EX
//
size = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry[1]);
TraceLog((CdromDebugTrace, "CdRomDeviceControl: Get drive layout ex\n")); if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < size) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = size; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING;
}
case IOCTL_DISK_GET_PARTITION_INFO: {
//
// Check that the buffer is large enough.
//
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PARTITION_INFORMATION)) {
status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION); break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING;
} case IOCTL_DISK_GET_PARTITION_INFO_EX: {
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PARTITION_INFORMATION_EX)) {
status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(PARTITION_INFORMATION_EX); break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING; }
case IOCTL_DISK_VERIFY: {
TraceLog((CdromDebugTrace, "IOCTL_DISK_VERIFY to device %p through irp %p\n", DeviceObject, Irp));
//
// Validate buffer length.
//
if (irpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(VERIFY_INFORMATION)) {
status = STATUS_INFO_LENGTH_MISMATCH; break; } IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING; }
case IOCTL_DISK_GET_LENGTH_INFO: {
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); break; } IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING; }
case IOCTL_CDROM_GET_CONFIGURATION: {
PGET_CONFIGURATION_IOCTL_INPUT inputBuffer;
TraceLog((CdromDebugTrace, "IOCTL_CDROM_GET_CONFIGURATION to via irp %p\n", Irp));
//
// Validate buffer length.
//
if (irpStack->Parameters.DeviceIoControl.InputBufferLength != sizeof(GET_CONFIGURATION_IOCTL_INPUT)) { status = STATUS_INFO_LENGTH_MISMATCH; break; } if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_CONFIGURATION_HEADER)) { status = STATUS_BUFFER_TOO_SMALL; Irp->IoStatus.Information = sizeof(GET_CONFIGURATION_HEADER); break; } if (irpStack->Parameters.DeviceIoControl.OutputBufferLength > 0xffff) { // output buffer is too large
status = STATUS_INVALID_BUFFER_SIZE; break; } if (irpStack->Parameters.DeviceIoControl.OutputBufferLength & fdoExtension->AdapterDescriptor->AlignmentMask) { // buffer is not proper size multiple
status = STATUS_INVALID_PARAMETER; break; }
//
// also verify the arguments are reasonable.
//
inputBuffer = Irp->AssociatedIrp.SystemBuffer; if (inputBuffer->Feature > 0xffff) { status = STATUS_INVALID_PARAMETER; break; } if ((inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ONE) && (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_CURRENT) && (inputBuffer->RequestType != SCSI_GET_CONFIGURATION_REQUEST_TYPE_ALL)) { status = STATUS_INVALID_PARAMETER; break; } if (inputBuffer->Reserved[0] || inputBuffer->Reserved[1]) { status = STATUS_INVALID_PARAMETER; break; }
IoMarkIrpPending(Irp); IoStartPacket(DeviceObject, Irp, NULL, NULL); return STATUS_PENDING;
}
default: {
BOOLEAN synchronize = (KeGetCurrentIrql() == PASSIVE_LEVEL); PKEVENT deviceControlEvent;
//
// If the ioctl has come in at passive level then we will synchronize
// with our start-io routine when sending the ioctl. If the ioctl
// has come in at a higher interrupt level and it was not handled
// above then it's unlikely to be a request for the class DLL - however
// we'll still use it's common code to forward the request through.
//
if (synchronize) {
deviceControlEvent = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), CDROM_TAG_DC_EVENT);
if (deviceControlEvent == NULL) {
//
// must complete this irp unsuccessful here
//
status = STATUS_INSUFFICIENT_RESOURCES; break;
} else {
PIO_STACK_LOCATION currentStack;
UCHAR uniqueAddress;
KeInitializeEvent(deviceControlEvent, NotificationEvent, FALSE);
currentStack = IoGetCurrentIrpStackLocation(Irp); nextStack = IoGetNextIrpStackLocation(Irp);
//
// Copy the stack down a notch
//
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine( Irp, CdRomClassIoctlCompletion, deviceControlEvent, TRUE, TRUE, TRUE );
IoSetNextIrpStackLocation(Irp);
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0;
//
// Override volume verifies on this stack location so that we
// will be forced through the synchronization. Once this
// location goes away we get the old value back
//
SET_FLAG(nextStack->Flags, SL_OVERRIDE_VERIFY_VOLUME);
//
// because this will release the remove lock (even though it
// reacquires it in the completion routine) it is neccessary
// to acquire another remove lock here.
//
ClassAcquireRemoveLock(DeviceObject, (PIRP) &uniqueAddress);
//
// send the request into CDROM's StartIo (which will
// complete the request and release the remove lock).
// note: the completion routine will re-acquire the
// remove lock.
//
IoStartPacket(DeviceObject, Irp, NULL, NULL);
//
// Wait for CdRomClassIoctlCompletion to set the event. This
// ensures serialization remains intact for these unhandled device
// controls.
//
KeWaitForSingleObject( deviceControlEvent, Executive, KernelMode, FALSE, NULL);
//
// release the remove lock and free the pool for the event
//
ClassReleaseRemoveLock(DeviceObject, (PIRP) &uniqueAddress);
ExFreePool(deviceControlEvent);
TraceLog((CdromDebugTrace, "CdRomDeviceControl: irp %p synchronized\n", Irp));
status = Irp->IoStatus.Status; }
} else { status = STATUS_SUCCESS; }
//
// If an error occured then propagate that back up - we are no longer
// guaranteed synchronization and the upper layers will have to
// retry.
//
// If no error occured, call down to the class driver directly
// then start up the next request.
//
if (NT_SUCCESS(status)) {
UCHAR uniqueAddress;
//
// The class device control routine will release the remove
// lock for this Irp. We need to make sure we have one
// available so that it's safe to call IoStartNextPacket
//
if(synchronize) {
ClassAcquireRemoveLock(DeviceObject, (PIRP)&uniqueAddress);
}
status = ClassDeviceControl(DeviceObject, Irp);
if(synchronize) { KeRaiseIrql(DISPATCH_LEVEL, &irql); IoStartNextPacket(DeviceObject, FALSE); KeLowerIrql(irql); ClassReleaseRemoveLock(DeviceObject, (PIRP)&uniqueAddress); } return status;
}
//
// an error occurred (either STATUS_INSUFFICIENT_RESOURCES from
// attempting to synchronize or StartIo() error'd this one
// out), so we need to finish the irp, which is
// done at the end of this routine.
//
break;
} // end default case
} // end switch()
if (status == STATUS_VERIFY_REQUIRED) {
//
// If the status is verified required and this request
// should bypass verify required then retry the request.
//
if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {
status = STATUS_IO_DEVICE_ERROR; goto RetryControl;
} }
if (IoIsErrorUserInduced(status)) {
if (Irp->Tail.Overlay.Thread) { IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); }
}
//
// Update IRP with completion status.
//
Irp->IoStatus.Status = status;
//
// Complete the request.
//
ClassReleaseRemoveLock(DeviceObject, Irp); ClassCompleteRequest(DeviceObject, Irp, IO_DISK_INCREMENT); TraceLog((CdromDebugTrace, "CdRomDeviceControl: Status is %lx\n", status)); return status;
} // end CdRomDeviceControl()
NTSTATUS CdRomClassIoctlCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++
Routine Description:
This routine signals the event used by CdRomDeviceControl to synchronize class driver (and lower level driver) ioctls with cdrom's startio routine. The irp completion is short-circuited so that CdRomDeviceControlDispatch can reissue it once it wakes up.
Arguments:
DeviceObject - the device object Irp - the request we are synchronizing Context - a PKEVENT that we need to signal
Return Value:
NTSTATUS
--*/ { PKEVENT syncEvent = (PKEVENT) Context;
TraceLog((CdromDebugTrace, "CdRomClassIoctlCompletion: setting event for irp %p\n", Irp));
//
// We released the lock when we completed this request. Reacquire it.
//
ClassAcquireRemoveLock(DeviceObject, Irp);
KeSetEvent(syncEvent, IO_DISK_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CdRomDeviceControlCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE);
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PIO_STACK_LOCATION realIrpStack; PIO_STACK_LOCATION realIrpNextStack;
PSCSI_REQUEST_BLOCK srb = Context;
PIRP realIrp = NULL;
NTSTATUS status; BOOLEAN retry;
//
// Extract the 'real' irp from the irpstack.
//
realIrp = (PIRP) irpStack->Parameters.Others.Argument2; realIrpStack = IoGetCurrentIrpStackLocation(realIrp); realIrpNextStack = IoGetNextIrpStackLocation(realIrp);
//
// check that we've really got the correct irp
//
ASSERT(realIrpNextStack->Parameters.Others.Argument3 == Irp);
//
// Check SRB status for success of completing request.
//
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
ULONG retryInterval;
TraceLog((CdromDebugTrace, "CdRomDeviceControlCompletion: Irp %p, Srb %p Real Irp %p Status %lx\n", Irp, srb, realIrp, srb->SrbStatus));
//
// Release the queue if it is frozen.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { TraceLog((CdromDebugTrace, "CdRomDeviceControlCompletion: Releasing Queue\n")); ClassReleaseQueue(DeviceObject); }
retry = ClassInterpretSenseInfo(DeviceObject, srb, irpStack->MajorFunction, irpStack->Parameters.DeviceIoControl.IoControlCode, MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1), &status, &retryInterval);
TraceLog((CdromDebugTrace, "CdRomDeviceControlCompletion: IRP will %sbe retried\n", (retry ? "" : "not ")));
//
// Some of the Device Controls need special cases on non-Success status's.
//
if (realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) { if ((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) || (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC) || (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_TOC_EX) || (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_VOLUME)) {
if (status == STATUS_DATA_OVERRUN) { status = STATUS_SUCCESS; retry = FALSE; } }
if (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_READ_Q_CHANNEL) { PLAY_ACTIVE(fdoExtension) = FALSE; } }
//
// If the status is verified required and the this request
// should bypass verify required then retry the request.
//
if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && status == STATUS_VERIFY_REQUIRED) {
// note: status gets overwritten here
status = STATUS_IO_DEVICE_ERROR; retry = TRUE;
if (((realIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL) || (realIrpStack->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) ) && ((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY) || (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_CHECK_VERIFY) || (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_STORAGE_CHECK_VERIFY2) || (realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DISK_CHECK_VERIFY) ) ) {
//
// Update the geometry information, as the media could have
// changed. The completion routine for this will complete
// the real irp and start the next packet.
//
if (srb) { if (srb->SenseInfoBuffer) { ExFreePool(srb->SenseInfoBuffer); } if (srb->DataBuffer) { ExFreePool(srb->DataBuffer); } ExFreePool(srb); srb = NULL; }
if (Irp->MdlAddress) { IoFreeMdl(Irp->MdlAddress); Irp->MdlAddress = NULL; }
IoFreeIrp(Irp); Irp = NULL;
status = CdRomUpdateCapacity(fdoExtension, realIrp, NULL); TraceLog((CdromDebugTrace, "CdRomDeviceControlCompletion: [%p] " "CdRomUpdateCapacity completed with status %lx\n", realIrp, status));
//
// needed to update the capacity.
// the irp's already handed off to CdRomUpdateCapacity().
// we've already free'd the current irp.
// nothing left to do in this code path.
//
return STATUS_MORE_PROCESSING_REQUIRED;
} // end of ioctls to update capacity
}
if (retry && ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)--) {
if (((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) {
//
// Retry request.
//
TraceLog((CdromDebugWarning, "Retry request %p - Calling StartIo\n", Irp));
ExFreePool(srb->SenseInfoBuffer); if (srb->DataBuffer) { ExFreePool(srb->DataBuffer); } ExFreePool(srb); if (Irp->MdlAddress) { IoFreeMdl(Irp->MdlAddress); }
realIrpNextStack->Parameters.Others.Argument3 = (PVOID)-1; IoFreeIrp(Irp);
CdRomRetryRequest(fdoExtension, realIrp, retryInterval, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; }
//
// Exhausted retries. Fall through and complete the request with
// the appropriate status.
//
} } else {
//
// Set status for successful request.
//
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status)) {
BOOLEAN b = FALSE;
switch (realIrpStack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_DISK_VERIFY: { //
// nothing to do but return the status...
//
break; }
case IOCTL_STORAGE_SET_READ_AHEAD: case IOCTL_CDROM_SET_VOLUME: case IOCTL_DVD_END_SESSION: case IOCTL_DVD_SEND_KEY: case IOCTL_DVD_SEND_KEY2: {
//
// nothing to return
//
realIrp->IoStatus.Information = 0; break; }
case IOCTL_CDROM_PLAY_AUDIO_MSF: {
PLAY_ACTIVE(fdoExtension) = TRUE; realIrp->IoStatus.Information = 0; break; } case IOCTL_CDROM_STOP_AUDIO: case IOCTL_CDROM_PAUSE_AUDIO: {
PLAY_ACTIVE(fdoExtension) = FALSE; realIrp->IoStatus.Information = 0; break; }
case IOCTL_CDROM_SEEK_AUDIO_MSF: case IOCTL_CDROM_RESUME_AUDIO: { realIrp->IoStatus.Information = 0; break; }
case IOCTL_CDROM_GET_CONFIGURATION: { RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, srb->DataBuffer, srb->DataTransferLength); realIrp->IoStatus.Information = srb->DataTransferLength; break; }
case IOCTL_DISK_GET_LENGTH_INFO: {
PGET_LENGTH_INFORMATION lengthInfo;
CdRomInterpretReadCapacity(DeviceObject, (PREAD_CAPACITY_DATA)srb->DataBuffer);
lengthInfo = (PGET_LENGTH_INFORMATION)realIrp->AssociatedIrp.SystemBuffer; lengthInfo->Length = commonExtension->PartitionLength; realIrp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); status = STATUS_SUCCESS; break; }
case IOCTL_DISK_GET_DRIVE_GEOMETRY_EX: case IOCTL_CDROM_GET_DRIVE_GEOMETRY_EX: {
PDISK_GEOMETRY_EX geometryEx;
CdRomInterpretReadCapacity(DeviceObject, (PREAD_CAPACITY_DATA)srb->DataBuffer);
geometryEx = (PDISK_GEOMETRY_EX)(realIrp->AssociatedIrp.SystemBuffer); geometryEx->DiskSize = commonExtension->PartitionLength; geometryEx->Geometry = fdoExtension->DiskGeometry; realIrp->IoStatus.Information = FIELD_OFFSET(DISK_GEOMETRY_EX, Data); break; }
case IOCTL_DISK_GET_DRIVE_GEOMETRY: case IOCTL_CDROM_GET_DRIVE_GEOMETRY: {
PDISK_GEOMETRY geometry;
CdRomInterpretReadCapacity(DeviceObject, (PREAD_CAPACITY_DATA)srb->DataBuffer);
geometry = (PDISK_GEOMETRY)(realIrp->AssociatedIrp.SystemBuffer); *geometry = fdoExtension->DiskGeometry; realIrp->IoStatus.Information = sizeof(DISK_GEOMETRY); break; }
case IOCTL_DISK_CHECK_VERIFY: case IOCTL_STORAGE_CHECK_VERIFY: case IOCTL_CDROM_CHECK_VERIFY: {
if((realIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_CHECK_VERIFY) && (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength)) {
*((PULONG)realIrp->AssociatedIrp.SystemBuffer) = commonExtension->PartitionZeroExtension->MediaChangeCount;
realIrp->IoStatus.Information = sizeof(ULONG); } else { realIrp->IoStatus.Information = 0; }
TraceLog((CdromDebugTrace, "CdRomDeviceControlCompletion: [%p] completing " "CHECK_VERIFY buddy irp %p\n", realIrp, Irp)); break; }
case IOCTL_CDROM_READ_TOC_EX: {
if (srb->DataTransferLength < MINIMUM_CDROM_READ_TOC_EX_SIZE) { status = STATUS_INVALID_DEVICE_REQUEST; break; }
//
// Copy the returned info into the user buffer.
//
RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, srb->DataBuffer, srb->DataTransferLength);
//
// update information field.
//
realIrp->IoStatus.Information = srb->DataTransferLength; break; }
case IOCTL_CDROM_GET_LAST_SESSION: case IOCTL_CDROM_READ_TOC: {
//
// Copy the returned info into the user buffer.
//
RtlMoveMemory(realIrp->AssociatedIrp.SystemBuffer, srb->DataBuffer, srb->DataTransferLength);
//
// update information field.
//
realIrp->IoStatus.Information = srb->DataTransferLength; break; }
case IOCTL_DVD_READ_STRUCTURE: {
DVD_STRUCTURE_FORMAT format = ((PDVD_READ_STRUCTURE) realIrp->AssociatedIrp.SystemBuffer)->Format;
PDVD_DESCRIPTOR_HEADER header = realIrp->AssociatedIrp.SystemBuffer;
FOUR_BYTE fourByte = {0}; PTWO_BYTE twoByte; UCHAR tmp;
TraceLog((CdromDebugTrace, "DvdDeviceControlCompletion - IOCTL_DVD_READ_STRUCTURE: completing irp %p (buddy %p)\n", Irp, realIrp));
TraceLog((CdromDebugTrace, "DvdDCCompletion - READ_STRUCTURE: descriptor format of %d\n", format));
RtlMoveMemory(header, srb->DataBuffer, srb->DataTransferLength);
//
// Cook the data. There are a number of fields that really
// should be byte-swapped for the caller.
//
TraceLog((CdromDebugInfo, "DvdDCCompletion - READ_STRUCTURE:\n" "\tHeader at %p\n" "\tDvdDCCompletion - READ_STRUCTURE: data at %p\n" "\tDataBuffer was at %p\n" "\tDataTransferLength was %lx\n", header, header->Data, srb->DataBuffer, srb->DataTransferLength));
//
// First the fields in the header
//
TraceLog((CdromDebugInfo, "READ_STRUCTURE: header->Length %lx -> ", header->Length)); REVERSE_SHORT(&header->Length); TraceLog((CdromDebugInfo, "%lx\n", header->Length));
//
// Now the fields in the descriptor
//
if(format == DvdPhysicalDescriptor) {
PDVD_LAYER_DESCRIPTOR layer = (PDVD_LAYER_DESCRIPTOR) &(header->Data[0]);
TraceLog((CdromDebugInfo, "READ_STRUCTURE: StartingDataSector %lx -> ", layer->StartingDataSector)); REVERSE_LONG(&(layer->StartingDataSector)); TraceLog((CdromDebugInfo, "%lx\n", layer->StartingDataSector));
TraceLog((CdromDebugInfo, "READ_STRUCTURE: EndDataSector %lx -> ", layer->EndDataSector)); REVERSE_LONG(&(layer->EndDataSector)); TraceLog((CdromDebugInfo, "%lx\n", layer->EndDataSector));
TraceLog((CdromDebugInfo, "READ_STRUCTURE: EndLayerZeroSector %lx -> ", layer->EndLayerZeroSector)); REVERSE_LONG(&(layer->EndLayerZeroSector)); TraceLog((CdromDebugInfo, "%lx\n", layer->EndLayerZeroSector)); }
TraceLog((CdromDebugTrace, "Status is %lx\n", Irp->IoStatus.Status)); TraceLog((CdromDebugTrace, "DvdDeviceControlCompletion - " "IOCTL_DVD_READ_STRUCTURE: data transfer length of %d\n", srb->DataTransferLength));
realIrp->IoStatus.Information = srb->DataTransferLength; break; }
case IOCTL_DVD_READ_KEY: {
PDVD_COPY_PROTECT_KEY copyProtectKey = realIrp->AssociatedIrp.SystemBuffer;
PCDVD_KEY_HEADER keyHeader = srb->DataBuffer; ULONG dataLength;
ULONG transferLength = srb->DataTransferLength - FIELD_OFFSET(CDVD_KEY_HEADER, Data);
//
// Adjust the data length to ignore the two reserved bytes in the
// header.
//
dataLength = (keyHeader->DataLength[0] << 8) + keyHeader->DataLength[1]; dataLength -= 2;
//
// take the minimum of the transferred length and the
// length as specified in the header.
//
if(dataLength < transferLength) { transferLength = dataLength; }
TraceLog((CdromDebugTrace, "DvdDeviceControlCompletion: [%p] - READ_KEY with " "transfer length of (%d or %d) bytes\n", Irp, dataLength, srb->DataTransferLength - 2));
//
// Copy the key data into the return buffer
//
if(copyProtectKey->KeyType == DvdTitleKey) {
RtlMoveMemory(copyProtectKey->KeyData, keyHeader->Data + 1, transferLength - 1); copyProtectKey->KeyData[transferLength - 1] = 0;
//
// If this is a title key then we need to copy the CGMS flags
// as well.
//
copyProtectKey->KeyFlags = *(keyHeader->Data);
} else {
RtlMoveMemory(copyProtectKey->KeyData, keyHeader->Data, transferLength); }
copyProtectKey->KeyLength = sizeof(DVD_COPY_PROTECT_KEY); copyProtectKey->KeyLength += transferLength;
realIrp->IoStatus.Information = copyProtectKey->KeyLength; break; }
case IOCTL_DVD_START_SESSION: {
PDVD_SESSION_ID sessionId = realIrp->AssociatedIrp.SystemBuffer;
PCDVD_KEY_HEADER keyHeader = srb->DataBuffer; PCDVD_REPORT_AGID_DATA keyData = (PCDVD_REPORT_AGID_DATA) keyHeader->Data;
*sessionId = keyData->AGID;
realIrp->IoStatus.Information = sizeof(DVD_SESSION_ID);
break; }
case IOCTL_CDROM_READ_Q_CHANNEL: {
PSUB_Q_CHANNEL_DATA userChannelData = realIrp->AssociatedIrp.SystemBuffer; PCDROM_SUB_Q_DATA_FORMAT inputBuffer = realIrp->AssociatedIrp.SystemBuffer; PSUB_Q_CHANNEL_DATA subQPtr = srb->DataBuffer;
#if DBG
switch( inputBuffer->Format ) {
case IOCTL_CDROM_CURRENT_POSITION: TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->CurrentPosition.Header.AudioStatus )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: ADR = 0x%x\n", subQPtr->CurrentPosition.ADR )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Control = 0x%x\n", subQPtr->CurrentPosition.Control )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Track = %u\n", subQPtr->CurrentPosition.TrackNumber )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Index = %u\n", subQPtr->CurrentPosition.IndexNumber )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Absolute Address = %x\n", *((PULONG)subQPtr->CurrentPosition.AbsoluteAddress) )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Relative Address = %x\n", *((PULONG)subQPtr->CurrentPosition.TrackRelativeAddress) )); break;
case IOCTL_CDROM_MEDIA_CATALOG: TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->MediaCatalog.Header.AudioStatus )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Mcval is %u\n", subQPtr->MediaCatalog.Mcval )); break;
case IOCTL_CDROM_TRACK_ISRC: TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Audio Status is %u\n", subQPtr->TrackIsrc.Header.AudioStatus )); TraceLog((CdromDebugTrace,"CdRomDeviceControlCompletion: Tcval is %u\n", subQPtr->TrackIsrc.Tcval )); break;
} #endif
//
// Update the play active status.
//
if (subQPtr->CurrentPosition.Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
PLAY_ACTIVE(fdoExtension) = TRUE;
} else {
PLAY_ACTIVE(fdoExtension) = FALSE;
}
//
// Check if output buffer is large enough to contain
// the data.
//
if (realIrpStack->Parameters.DeviceIoControl.OutputBufferLength < srb->DataTransferLength) {
srb->DataTransferLength = realIrpStack->Parameters.DeviceIoControl.OutputBufferLength; }
//
// Copy our buffer into users.
//
RtlMoveMemory(userChannelData, subQPtr, srb->DataTransferLength);
realIrp->IoStatus.Information = srb->DataTransferLength; break; }
case IOCTL_CDROM_GET_VOLUME: {
PAUDIO_OUTPUT audioOutput; PVOLUME_CONTROL volumeControl = srb->DataBuffer; ULONG i; ULONG bytesTransferred;
audioOutput = ClassFindModePage((PCHAR)volumeControl, srb->DataTransferLength, CDROM_AUDIO_CONTROL_PAGE, use6Byte);
//
// Verify the page is as big as expected.
//
bytesTransferred = (ULONG)((PCHAR) audioOutput - (PCHAR) volumeControl) + sizeof(AUDIO_OUTPUT);
if (audioOutput != NULL && srb->DataTransferLength >= bytesTransferred) {
for (i=0; i<4; i++) { volumeControl->PortVolume[i] = audioOutput->PortOutput[i].Volume; }
//
// Set bytes transferred in IRP.
//
realIrp->IoStatus.Information = sizeof(VOLUME_CONTROL);
} else { realIrp->IoStatus.Information = 0; status = STATUS_INVALID_DEVICE_REQUEST; }
break; }
default:
ASSERT(FALSE); realIrp->IoStatus.Information = 0; status = STATUS_INVALID_DEVICE_REQUEST;
} // end switch()
}
//
// Deallocate srb and sense buffer.
//
if (srb) { if (srb->DataBuffer) { ExFreePool(srb->DataBuffer); } if (srb->SenseInfoBuffer) { ExFreePool(srb->SenseInfoBuffer); } ExFreePool(srb); }
if (realIrp->PendingReturned) { IoMarkIrpPending(realIrp); }
if (Irp->MdlAddress) { IoFreeMdl(Irp->MdlAddress); }
IoFreeIrp(Irp);
//
// Set status in completing IRP.
//
realIrp->IoStatus.Status = status;
//
// Set the hard error if necessary.
//
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
//
// Store DeviceObject for filesystem, and clear
// in IoStatus.Information field.
//
TraceLog((CdromDebugWarning, "CdRomDeviceCompletion - Setting Hard Error on realIrp %p\n", realIrp)); if (realIrp->Tail.Overlay.Thread) { IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); }
realIrp->IoStatus.Information = 0; }
//
// note: must complete the realIrp, as the completed irp (above)
// was self-allocated.
//
CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp); return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CdRomSetVolumeIntermediateCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PCDROM_DATA cdData = (PCDROM_DATA)(commonExtension->DriverData); BOOLEAN use6Byte = TEST_FLAG(cdData->XAFlags, XA_USE_6_BYTE); PIO_STACK_LOCATION realIrpStack; PIO_STACK_LOCATION realIrpNextStack; PSCSI_REQUEST_BLOCK srb = Context; PIRP realIrp = NULL; NTSTATUS status; BOOLEAN retry;
//
// Extract the 'real' irp from the irpstack.
//
realIrp = (PIRP) irpStack->Parameters.Others.Argument2; realIrpStack = IoGetCurrentIrpStackLocation(realIrp); realIrpNextStack = IoGetNextIrpStackLocation(realIrp);
//
// Check SRB status for success of completing request.
//
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
ULONG retryInterval;
TraceLog((CdromDebugTrace, "CdRomSetVolumeIntermediateCompletion: Irp %p, Srb %p, Real Irp %p\n", Irp, srb, realIrp));
//
// Release the queue if it is frozen.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { ClassReleaseQueue(DeviceObject); }
retry = ClassInterpretSenseInfo(DeviceObject, srb, irpStack->MajorFunction, irpStack->Parameters.DeviceIoControl.IoControlCode, MAXIMUM_RETRIES - ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1), &status, &retryInterval);
if (status == STATUS_DATA_OVERRUN) { status = STATUS_SUCCESS; retry = FALSE; }
//
// If the status is verified required and the this request
// should bypass verify required then retry the request.
//
if (realIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && status == STATUS_VERIFY_REQUIRED) {
status = STATUS_IO_DEVICE_ERROR; retry = TRUE; }
if (retry && ((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)--) {
if (((ULONG)(ULONG_PTR)realIrpNextStack->Parameters.Others.Argument1)) {
//
// Retry request.
//
TraceLog((CdromDebugWarning, "Retry request %p - Calling StartIo\n", Irp));
ExFreePool(srb->SenseInfoBuffer); ExFreePool(srb->DataBuffer); ExFreePool(srb); if (Irp->MdlAddress) { IoFreeMdl(Irp->MdlAddress); }
IoFreeIrp(Irp);
CdRomRetryRequest(deviceExtension, realIrp, retryInterval, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Exhausted retries. Fall through and complete the request with the appropriate status.
//
} } else {
//
// Set status for successful request.
//
status = STATUS_SUCCESS;
}
if (NT_SUCCESS(status)) {
PAUDIO_OUTPUT audioInput = NULL; PAUDIO_OUTPUT audioOutput; PVOLUME_CONTROL volumeControl = realIrp->AssociatedIrp.SystemBuffer; ULONG i,bytesTransferred,headerLength; PVOID dataBuffer; PCDB cdb;
audioInput = ClassFindModePage((PCHAR)srb->DataBuffer, srb->DataTransferLength, CDROM_AUDIO_CONTROL_PAGE, use6Byte);
//
// Check to make sure the mode sense data is valid before we go on
//
if(audioInput == NULL) {
TraceLog((CdromDebugWarning, "Mode Sense Page %d not found\n", CDROM_AUDIO_CONTROL_PAGE));
realIrp->IoStatus.Information = 0; realIrp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; goto SafeExit; }
if (use6Byte) { headerLength = sizeof(MODE_PARAMETER_HEADER); } else { headerLength = sizeof(MODE_PARAMETER_HEADER10); }
bytesTransferred = sizeof(AUDIO_OUTPUT) + headerLength;
//
// Allocate a new buffer for the mode select.
//
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, bytesTransferred, CDROM_TAG_VOLUME_INT);
if (!dataBuffer) { realIrp->IoStatus.Information = 0; realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto SafeExit; }
RtlZeroMemory(dataBuffer, bytesTransferred);
//
// Rebuild the data buffer to include the user requested values.
//
audioOutput = (PAUDIO_OUTPUT) ((PCHAR) dataBuffer + headerLength);
for (i=0; i<4; i++) { audioOutput->PortOutput[i].Volume = volumeControl->PortVolume[i]; audioOutput->PortOutput[i].ChannelSelection = audioInput->PortOutput[i].ChannelSelection; }
audioOutput->CodePage = CDROM_AUDIO_CONTROL_PAGE; audioOutput->ParameterLength = sizeof(AUDIO_OUTPUT) - 2; audioOutput->Immediate = MODE_SELECT_IMMEDIATE;
//
// Free the old data buffer, mdl.
//
IoFreeMdl(Irp->MdlAddress); Irp->MdlAddress = NULL; ExFreePool(srb->DataBuffer);
//
// set the data buffer to new allocation, so it can be
// freed in the exit path
//
srb->DataBuffer = dataBuffer;
//
// rebuild the srb.
//
cdb = (PCDB)srb->Cdb; RtlZeroMemory(cdb, CDB12GENERIC_LENGTH);
srb->SrbStatus = srb->ScsiStatus = 0; srb->SrbFlags = deviceExtension->SrbFlags; SET_FLAG(srb->SrbFlags, SRB_FLAGS_DISABLE_SYNCH_TRANSFER); SET_FLAG(srb->SrbFlags, SRB_FLAGS_DATA_OUT); srb->DataTransferLength = bytesTransferred;
if (use6Byte) {
cdb->MODE_SELECT.OperationCode = SCSIOP_MODE_SELECT; cdb->MODE_SELECT.ParameterListLength = (UCHAR) bytesTransferred; cdb->MODE_SELECT.PFBit = 1; srb->CdbLength = 6; } else {
cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR) (bytesTransferred >> 8); cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR) (bytesTransferred & 0xFF); cdb->MODE_SELECT10.PFBit = 1; srb->CdbLength = 10; }
//
// Prepare the MDL
//
Irp->MdlAddress = IoAllocateMdl(dataBuffer, bytesTransferred, FALSE, FALSE, (PIRP) NULL);
if (!Irp->MdlAddress) { realIrp->IoStatus.Information = 0; realIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; goto SafeExit; }
MmBuildMdlForNonPagedPool(Irp->MdlAddress);
irpStack = IoGetNextIrpStackLocation(Irp); irpStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SCSI_EXECUTE_IN; irpStack->Parameters.Scsi.Srb = srb;
//
// reset the irp completion.
//
IoSetCompletionRoutine(Irp, CdRomDeviceControlCompletion, srb, TRUE, TRUE, TRUE); //
// Call the port driver.
//
IoCallDriver(commonExtension->LowerDeviceObject, Irp);
return STATUS_MORE_PROCESSING_REQUIRED; }
SafeExit:
//
// Deallocate srb and sense buffer.
//
if (srb) { if (srb->DataBuffer) { ExFreePool(srb->DataBuffer); } if (srb->SenseInfoBuffer) { ExFreePool(srb->SenseInfoBuffer); } ExFreePool(srb); }
if (Irp->PendingReturned) { IoMarkIrpPending(Irp); }
if (realIrp->PendingReturned) { IoMarkIrpPending(realIrp); }
if (Irp->MdlAddress) { IoFreeMdl(Irp->MdlAddress); }
IoFreeIrp(Irp);
//
// Set status in completing IRP.
//
realIrp->IoStatus.Status = status;
//
// Set the hard error if necessary.
//
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status)) {
//
// Store DeviceObject for filesystem, and clear
// in IoStatus.Information field.
//
if (realIrp->Tail.Overlay.Thread) { IoSetHardErrorOrVerifyDevice(realIrp, DeviceObject); } realIrp->IoStatus.Information = 0; }
CdRomCompleteIrpAndStartNextPacketSafely(DeviceObject, realIrp); return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CdRomDvdEndAllSessionsCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
/*++
Routine Description:
This routine will setup the next stack location to issue an end session to the device. It will increment the session id in the system buffer and issue an END_SESSION for that AGID if the AGID is valid.
When the new AGID is > 3 this routine will complete the request.
Arguments:
DeviceObject - the device object for this drive
Irp - the request
Context - done
Return Value:
STATUS_MORE_PROCESSING_REQUIRED if there is another AGID to clear status otherwise.
--*/
{ PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
PDVD_SESSION_ID sessionId = Irp->AssociatedIrp.SystemBuffer;
NTSTATUS status;
if(++(*sessionId) > MAX_COPY_PROTECT_AGID) {
//
// We're done here - just return success and let the io system
// continue to complete it.
//
return STATUS_SUCCESS;
}
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp, CdRomDvdEndAllSessionsCompletion, NULL, TRUE, FALSE, FALSE);
IoMarkIrpPending(Irp);
IoCallDriver(fdoExtension->CommonExtension.DeviceObject, Irp);
//
// At this point we have to assume the irp may have already been
// completed. Ignore the returned status and return.
//
return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS CdRomDvdReadDiskKeyCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
/*++
Routine Description:
This routine handles the completion of a request to obtain the disk key from the dvd media. It will transform the raw 2K of key data into a DVD_COPY_PROTECT_KEY structure and copy back the saved key parameters from the context pointer before returning.
Arguments:
DeviceObject -
Irp -
Context - a DVD_COPY_PROTECT_KEY pointer which contains the key parameters handed down by the caller.
Return Value:
STATUS_SUCCESS;
--*/
{ PDVD_COPY_PROTECT_KEY savedKey = Context;
PREAD_DVD_STRUCTURES_HEADER rawKey = Irp->AssociatedIrp.SystemBuffer; PDVD_COPY_PROTECT_KEY outputKey = Irp->AssociatedIrp.SystemBuffer;
if (NT_SUCCESS(Irp->IoStatus.Status)) {
//
// Shift the data down to its new position.
//
RtlMoveMemory(outputKey->KeyData, rawKey->Data, sizeof(DVD_DISK_KEY_DESCRIPTOR));
RtlCopyMemory(outputKey, savedKey, sizeof(DVD_COPY_PROTECT_KEY));
outputKey->KeyLength = DVD_DISK_KEY_LENGTH;
Irp->IoStatus.Information = DVD_DISK_KEY_LENGTH;
} else {
TraceLog((CdromDebugWarning, "DiskKey Failed with status %x, %p (%x) bytes\n", Irp->IoStatus.Status, (PVOID)Irp->IoStatus.Information, ((rawKey->Length[0] << 16) | rawKey->Length[1]) ));
}
//
// release the context block
//
ExFreePool(Context);
return STATUS_SUCCESS; }
NTSTATUS CdRomXACompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
/*++
Routine Description:
This routine executes when the port driver has completed a request. It looks at the SRB status in the completing SRB and if not success it checks for valid request sense buffer information. If valid, the info is used to update status with more precise message of type of error. This routine deallocates the SRB.
Arguments:
DeviceObject - Supplies the device object which represents the logical unit.
Irp - Supplies the Irp which has completed.
Context - Supplies a pointer to the SRB.
Return Value:
NT status
--*/
{ PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PSCSI_REQUEST_BLOCK srb = Context; PFUNCTIONAL_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension; NTSTATUS status; BOOLEAN retry;
//
// Check SRB status for success of completing request.
//
if (SRB_STATUS(srb->SrbStatus) != SRB_STATUS_SUCCESS) {
ULONG retryInterval;
TraceLog((CdromDebugTrace, "CdromXAComplete: IRP %p SRB %p Status %x\n", Irp, srb, srb->SrbStatus));
//
// Release the queue if it is frozen.
//
if (srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) { ClassReleaseQueue(DeviceObject); }
retry = ClassInterpretSenseInfo( DeviceObject, srb, irpStack->MajorFunction, irpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL ? irpStack->Parameters.DeviceIoControl.IoControlCode : 0, MAXIMUM_RETRIES - irpStack->MinorFunction, // HACKHACK - REF #0001
&status, &retryInterval);
//
// If the status is verified required and the this request
// should bypass verify required then retry the request.
//
if (irpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME && status == STATUS_VERIFY_REQUIRED) {
status = STATUS_IO_DEVICE_ERROR; retry = TRUE; }
if (retry) {
if (irpStack->MinorFunction != 0) { // HACKHACK - REF #0001
irpStack->MinorFunction--; // HACKHACK - REF #0001
//
// Retry request.
//
TraceLog((CdromDebugWarning, "CdRomXACompletion: Retry request %p (%x) - " "Calling StartIo\n", Irp, irpStack->MinorFunction));
ExFreePool(srb->SenseInfoBuffer); ExFreePool(srb);
//
// Call StartIo directly since IoStartNextPacket hasn't been called,
// the serialisation is still intact.
//
CdRomRetryRequest(deviceExtension, Irp, retryInterval, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
//
// Exhausted retries, fall through and complete the request
// with the appropriate status
//
TraceLog((CdromDebugWarning, "CdRomXACompletion: Retries exhausted for irp %p\n", Irp));
}
} else {
//
// Set status for successful request.
//
status = STATUS_SUCCESS;
} // end if (SRB_STATUS(srb->SrbStatus) ...
//
// Return SRB to nonpaged pool.
//
ExFreePool(srb->SenseInfoBuffer); ExFreePool(srb);
//
// Set status in completing IRP.
//
Irp->IoStatus.Status = status;
//
// Set the hard error if necessary.
//
if (!NT_SUCCESS(status) && IoIsErrorUserInduced(status) && Irp->Tail.Overlay.Thread != NULL ) {
//
// Store DeviceObject for filesystem, and clear
// in IoStatus.Information field.
//
IoSetHardErrorOrVerifyDevice(Irp, DeviceObject); Irp->IoStatus.Information = 0; }
//
// If pending has be returned for this irp then mark the current stack as
// pending.
//
if (Irp->PendingReturned) { IoMarkIrpPending(Irp); }
{ KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); IoStartNextPacket(DeviceObject, FALSE); KeLowerIrql(oldIrql); } ClassReleaseRemoveLock(DeviceObject, Irp);
return status; }
VOID CdRomDeviceControlDvdReadStructure( IN PDEVICE_OBJECT Fdo, IN PIRP OriginalIrp, IN PIRP NewIrp, IN PSCSI_REQUEST_BLOCK Srb ) { PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCDB cdb = (PCDB)Srb->Cdb; PVOID dataBuffer;
PDVD_READ_STRUCTURE request; USHORT dataLength; ULONG blockNumber; PFOUR_BYTE fourByte;
dataLength = (USHORT)currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
request = OriginalIrp->AssociatedIrp.SystemBuffer; blockNumber = (ULONG)(request->BlockByteOffset.QuadPart >> fdoExtension->SectorShift); fourByte = (PFOUR_BYTE) &blockNumber;
Srb->CdbLength = 12; Srb->TimeOutValue = fdoExtension->TimeOutValue; Srb->SrbFlags = fdoExtension->SrbFlags; SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
cdb->READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE; cdb->READ_DVD_STRUCTURE.RMDBlockNumber[0] = fourByte->Byte3; cdb->READ_DVD_STRUCTURE.RMDBlockNumber[1] = fourByte->Byte2; cdb->READ_DVD_STRUCTURE.RMDBlockNumber[2] = fourByte->Byte1; cdb->READ_DVD_STRUCTURE.RMDBlockNumber[3] = fourByte->Byte0; cdb->READ_DVD_STRUCTURE.LayerNumber = request->LayerNumber; cdb->READ_DVD_STRUCTURE.Format = (UCHAR)request->Format;
#if DBG
{ if ((UCHAR)request->Format > DvdMaxDescriptor) { TraceLog((CdromDebugWarning, "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n", (UCHAR)request->Format, READ_DVD_STRUCTURE_FORMAT_STRINGS[DvdMaxDescriptor], dataLength )); } else { TraceLog((CdromDebugWarning, "READ_DVD_STRUCTURE format %x = %s (%x bytes)\n", (UCHAR)request->Format, READ_DVD_STRUCTURE_FORMAT_STRINGS[(UCHAR)request->Format], dataLength )); } } #endif // DBG
if (request->Format == DvdDiskKeyDescriptor) {
cdb->READ_DVD_STRUCTURE.AGID = (UCHAR) request->SessionId;
}
cdb->READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataLength >> 8); cdb->READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataLength & 0xff); Srb->DataTransferLength = dataLength;
dataBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, dataLength, DVD_TAG_READ_STRUCTURE);
if (!dataBuffer) { ExFreePool(Srb->SenseInfoBuffer); ExFreePool(Srb); IoFreeIrp(NewIrp); OriginalIrp->IoStatus.Information = 0; OriginalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
BAIL_OUT(OriginalIrp); CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp); return; }
/*
* Zero out input buffer in case the device returns fewer bytes than advertized, * which would cause us to return uninitialized kernel memory. */ RtlZeroMemory(dataBuffer, dataLength);
NewIrp->MdlAddress = IoAllocateMdl(dataBuffer, currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength, FALSE, FALSE, (PIRP) NULL);
if (NewIrp->MdlAddress == NULL) { ExFreePool(dataBuffer); ExFreePool(Srb->SenseInfoBuffer); ExFreePool(Srb); IoFreeIrp(NewIrp); OriginalIrp->IoStatus.Information = 0; OriginalIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
BAIL_OUT(OriginalIrp); CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp); return; }
//
// Prepare the MDL
//
MmBuildMdlForNonPagedPool(NewIrp->MdlAddress);
Srb->DataBuffer = dataBuffer;
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp);
return; }
VOID CdRomDeviceControlDvdEndSession( IN PDEVICE_OBJECT Fdo, IN PIRP OriginalIrp, IN PIRP NewIrp, IN PSCSI_REQUEST_BLOCK Srb ) { PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCDB cdb = (PCDB)Srb->Cdb;
PDVD_SESSION_ID sessionId = OriginalIrp->AssociatedIrp.SystemBuffer;
Srb->CdbLength = 12; Srb->TimeOutValue = fdoExtension->TimeOutValue; Srb->SrbFlags = fdoExtension->SrbFlags; SET_FLAG(Srb->SrbFlags, SRB_FLAGS_NO_DATA_TRANSFER);
cdb->SEND_KEY.OperationCode = SCSIOP_SEND_KEY; cdb->SEND_KEY.AGID = (UCHAR) (*sessionId); cdb->SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp); return;
}
VOID CdRomDeviceControlDvdStartSessionReadKey( IN PDEVICE_OBJECT Fdo, IN PIRP OriginalIrp, IN PIRP NewIrp, IN PSCSI_REQUEST_BLOCK Srb ) { PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCDB cdb = (PCDB)Srb->Cdb; NTSTATUS status;
PDVD_COPY_PROTECT_KEY keyParameters; PCDVD_KEY_HEADER keyBuffer = NULL;
ULONG keyLength;
ULONG allocationLength; PFOUR_BYTE fourByte;
//
// Both of these use REPORT_KEY commands.
// Determine the size of the input buffer
//
if(currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_READ_KEY) {
keyParameters = OriginalIrp->AssociatedIrp.SystemBuffer;
keyLength = sizeof(CDVD_KEY_HEADER) + (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength - sizeof(DVD_COPY_PROTECT_KEY)); } else {
keyParameters = NULL; keyLength = sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA); }
TRY {
keyBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, keyLength, DVD_TAG_READ_KEY);
if(keyBuffer == NULL) {
TraceLog((CdromDebugWarning, "IOCTL_DVD_READ_KEY - couldn't allocate " "%d byte buffer for key\n", keyLength)); status = STATUS_INSUFFICIENT_RESOURCES; LEAVE; }
NewIrp->MdlAddress = IoAllocateMdl(keyBuffer, keyLength, FALSE, FALSE, (PIRP) NULL);
if(NewIrp->MdlAddress == NULL) {
TraceLog((CdromDebugWarning, "IOCTL_DVD_READ_KEY - couldn't create mdl\n")); status = STATUS_INSUFFICIENT_RESOURCES; LEAVE; }
MmBuildMdlForNonPagedPool(NewIrp->MdlAddress);
Srb->DataBuffer = keyBuffer; Srb->CdbLength = 12;
cdb->REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
allocationLength = keyLength; fourByte = (PFOUR_BYTE) &allocationLength; cdb->REPORT_KEY.AllocationLength[0] = fourByte->Byte1; cdb->REPORT_KEY.AllocationLength[1] = fourByte->Byte0;
Srb->DataTransferLength = keyLength;
//
// set the specific parameters....
//
if(currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_DVD_READ_KEY) {
if(keyParameters->KeyType == DvdTitleKey) {
ULONG logicalBlockAddress;
logicalBlockAddress = (ULONG) (keyParameters->Parameters.TitleOffset.QuadPart >> fdoExtension->SectorShift);
fourByte = (PFOUR_BYTE) &(logicalBlockAddress);
cdb->REPORT_KEY.LogicalBlockAddress[0] = fourByte->Byte3; cdb->REPORT_KEY.LogicalBlockAddress[1] = fourByte->Byte2; cdb->REPORT_KEY.LogicalBlockAddress[2] = fourByte->Byte1; cdb->REPORT_KEY.LogicalBlockAddress[3] = fourByte->Byte0; }
cdb->REPORT_KEY.KeyFormat = (UCHAR)keyParameters->KeyType; cdb->REPORT_KEY.AGID = (UCHAR) keyParameters->SessionId; TraceLog((CdromDebugWarning, "CdRomDvdReadKey => sending irp %p for irp %p (%s)\n", NewIrp, OriginalIrp, "READ_KEY"));
} else {
cdb->REPORT_KEY.KeyFormat = DVD_REPORT_AGID; cdb->REPORT_KEY.AGID = 0; TraceLog((CdromDebugWarning, "CdRomDvdReadKey => sending irp %p for irp %p (%s)\n", NewIrp, OriginalIrp, "START_SESSION")); }
Srb->TimeOutValue = fdoExtension->TimeOutValue; Srb->SrbFlags = fdoExtension->SrbFlags; SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_IN);
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp);
status = STATUS_SUCCESS;
} FINALLY {
if (!NT_SUCCESS(status)) {
//
// An error occured during setup - free resources and
// complete this request.
//
if (NewIrp->MdlAddress != NULL) { IoFreeMdl(NewIrp->MdlAddress); }
if (keyBuffer != NULL) { ExFreePool(keyBuffer); } ExFreePool(Srb->SenseInfoBuffer); ExFreePool(Srb); IoFreeIrp(NewIrp);
OriginalIrp->IoStatus.Information = 0; OriginalIrp->IoStatus.Status = status;
BAIL_OUT(OriginalIrp); CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp);
} // end !NT_SUCCESS
} return; }
VOID CdRomDeviceControlDvdSendKey( IN PDEVICE_OBJECT Fdo, IN PIRP OriginalIrp, IN PIRP NewIrp, IN PSCSI_REQUEST_BLOCK Srb ) { PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(OriginalIrp); PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCDB cdb = (PCDB)Srb->Cdb;
PDVD_COPY_PROTECT_KEY key; PCDVD_KEY_HEADER keyBuffer = NULL;
NTSTATUS status; ULONG keyLength; PFOUR_BYTE fourByte;
key = OriginalIrp->AssociatedIrp.SystemBuffer; keyLength = (key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY)) + sizeof(CDVD_KEY_HEADER);
TRY {
keyBuffer = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, keyLength, DVD_TAG_SEND_KEY);
if(keyBuffer == NULL) {
TraceLog((CdromDebugWarning, "IOCTL_DVD_SEND_KEY - couldn't allocate " "%d byte buffer for key\n", keyLength)); status = STATUS_INSUFFICIENT_RESOURCES; LEAVE; }
RtlZeroMemory(keyBuffer, keyLength);
//
// keylength is decremented here by two because the
// datalength does not include the header, which is two
// bytes. keylength is immediately incremented later
// by the same amount.
//
keyLength -= 2; fourByte = (PFOUR_BYTE) &keyLength; keyBuffer->DataLength[0] = fourByte->Byte1; keyBuffer->DataLength[1] = fourByte->Byte0; keyLength += 2;
//
// copy the user's buffer to our own allocated buffer
//
RtlMoveMemory(keyBuffer->Data, key->KeyData, key->KeyLength - sizeof(DVD_COPY_PROTECT_KEY));
NewIrp->MdlAddress = IoAllocateMdl(keyBuffer, keyLength, FALSE, FALSE, (PIRP) NULL);
if(NewIrp->MdlAddress == NULL) { TraceLog((CdromDebugWarning, "IOCTL_DVD_SEND_KEY - couldn't create mdl\n")); status = STATUS_INSUFFICIENT_RESOURCES; LEAVE; }
MmBuildMdlForNonPagedPool(NewIrp->MdlAddress);
Srb->CdbLength = 12; Srb->DataBuffer = keyBuffer; Srb->DataTransferLength = keyLength;
Srb->TimeOutValue = fdoExtension->TimeOutValue; Srb->SrbFlags = fdoExtension->SrbFlags; SET_FLAG(Srb->SrbFlags, SRB_FLAGS_DATA_OUT);
cdb->REPORT_KEY.OperationCode = SCSIOP_SEND_KEY;
fourByte = (PFOUR_BYTE) &keyLength;
cdb->SEND_KEY.ParameterListLength[0] = fourByte->Byte1; cdb->SEND_KEY.ParameterListLength[1] = fourByte->Byte0; cdb->SEND_KEY.KeyFormat = (UCHAR)key->KeyType; cdb->SEND_KEY.AGID = (UCHAR) key->SessionId;
if (key->KeyType == DvdSetRpcKey) { TraceLog((CdromDebugWarning, "IOCTL_DVD_SEND_KEY - Setting RPC2 drive region\n")); } else { TraceLog((CdromDebugWarning, "IOCTL_DVD_SEND_KEY - key type %x\n", key->KeyType)); }
IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, NewIrp);
status = STATUS_SUCCESS;
} FINALLY {
if (!NT_SUCCESS(status)) {
//
// An error occured during setup - free resources and
// complete this request.
//
if (NewIrp->MdlAddress != NULL) { IoFreeMdl(NewIrp->MdlAddress); }
if (keyBuffer != NULL) { ExFreePool(keyBuffer); }
ExFreePool(Srb->SenseInfoBuffer); ExFreePool(Srb); IoFreeIrp(NewIrp);
OriginalIrp->IoStatus.Information = 0; OriginalIrp->IoStatus.Status = status;
BAIL_OUT(OriginalIrp); CdRomCompleteIrpAndStartNextPacketSafely(Fdo, OriginalIrp);
} }
return; }
VOID CdRomInterpretReadCapacity( IN PDEVICE_OBJECT Fdo, IN PREAD_CAPACITY_DATA ReadCapacityBuffer ) { PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension; PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension; ULONG lastSector; ULONG bps; ULONG lastBit; ULONG tmp;
ASSERT(ReadCapacityBuffer); ASSERT(commonExtension->IsFdo);
TraceLog((CdromDebugError, "CdRomInterpretReadCapacity: Entering\n"));
//
// Swizzle bytes from Read Capacity and translate into
// the necessary geometry information in the device extension.
//
tmp = ReadCapacityBuffer->BytesPerBlock; ((PFOUR_BYTE)&bps)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; ((PFOUR_BYTE)&bps)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; ((PFOUR_BYTE)&bps)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; ((PFOUR_BYTE)&bps)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
//
// Insure that bps is a power of 2.
// This corrects a problem with the HP 4020i CDR where it
// returns an incorrect number for bytes per sector.
//
if (!bps) { bps = 2048; } else { lastBit = (ULONG) -1; while (bps) { lastBit++; bps = bps >> 1; } bps = 1 << lastBit; }
fdoExtension->DiskGeometry.BytesPerSector = bps;
TraceLog((CdromDebugTrace, "CdRomInterpretReadCapacity: Calculated bps %#x\n", fdoExtension->DiskGeometry.BytesPerSector));
//
// Copy last sector in reverse byte order.
//
tmp = ReadCapacityBuffer->LogicalBlockAddress; ((PFOUR_BYTE)&lastSector)->Byte0 = ((PFOUR_BYTE)&tmp)->Byte3; ((PFOUR_BYTE)&lastSector)->Byte1 = ((PFOUR_BYTE)&tmp)->Byte2; ((PFOUR_BYTE)&lastSector)->Byte2 = ((PFOUR_BYTE)&tmp)->Byte1; ((PFOUR_BYTE)&lastSector)->Byte3 = ((PFOUR_BYTE)&tmp)->Byte0;
//
// Calculate sector to byte shift.
//
WHICH_BIT(bps, fdoExtension->SectorShift);
TraceLog((CdromDebugTrace,"CdRomInterpretReadCapacity: Sector size is %d\n", fdoExtension->DiskGeometry.BytesPerSector));
TraceLog((CdromDebugTrace,"CdRomInterpretReadCapacity: Number of Sectors is %d\n", lastSector + 1));
//
// Calculate media capacity in bytes.
//
commonExtension->PartitionLength.QuadPart = (LONGLONG)(lastSector + 1);
//
// we've defaulted to 32/64 forever. don't want to change this now...
//
fdoExtension->DiskGeometry.TracksPerCylinder = 0x40; fdoExtension->DiskGeometry.SectorsPerTrack = 0x20;
//
// Calculate number of cylinders.
//
fdoExtension->DiskGeometry.Cylinders.QuadPart = (LONGLONG)((lastSector + 1) / (32 * 64));
commonExtension->PartitionLength.QuadPart = (commonExtension->PartitionLength.QuadPart << fdoExtension->SectorShift);
ASSERT(TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA));
//
// This device supports removable media.
//
fdoExtension->DiskGeometry.MediaType = RemovableMedia;
return; }
|