You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4080 lines
127 KiB
4080 lines
127 KiB
/*--
|
|
|
|
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;
|
|
}
|
|
|