mirror of https://github.com/lianthony/NT4.0
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.
6801 lines
195 KiB
6801 lines
195 KiB
/*++
|
|
|
|
Copyright (c) 1992, 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
audio.c
|
|
|
|
Abstract:
|
|
|
|
This driver filters scsi-2 cdrom audio commands for non-scsi-2
|
|
compliant cdrom drives. At initialization, the driver scans the
|
|
scsi bus for a recognized non-scsi-2 cdrom drive, and if one is
|
|
found attached, installs itself to intercept IO_DEVICE_CONTROL
|
|
requests for this drive.
|
|
|
|
Author:
|
|
|
|
Rick Turner
|
|
Mike Glass (mglass)
|
|
Chuck Park (chuckp)
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "ntddk.h"
|
|
#include "scsi.h"
|
|
#include "stdio.h"
|
|
#include "stdarg.h"
|
|
|
|
#include "cdaudio.h"
|
|
|
|
#ifdef POOL_TAGGING
|
|
#ifdef ExAllocatePool
|
|
#undef ExAllocatePool
|
|
#endif
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' AdC')
|
|
#endif
|
|
|
|
|
|
|
|
//
|
|
// Function declarations
|
|
//
|
|
|
|
NTSTATUS
|
|
DriverEntry (
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioInitialize(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioSendToNextDriver(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
BOOLEAN
|
|
CdAudioIsPlayActive(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
BOOLEAN
|
|
NecSupportNeeded(
|
|
PUCHAR InquiryData
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioNECDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioNECInterpretSenseInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN UCHAR MajorFunctionCode,
|
|
IN ULONG IoDeviceCode,
|
|
IN UCHAR RetryCount,
|
|
OUT NTSTATUS *Status
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioNECSendSrbSynchronous(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PSCSI_REQUEST_BLOCK Srb,
|
|
PVOID BufferAddress,
|
|
ULONG BufferLength,
|
|
BOOLEAN WriteToDevice
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioPioneerDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioDenonDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioHitachiSendPauseCommand(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioHitachiDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudio535DeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
CdAudio435DeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioPan533DeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioAtapiDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioLionOpticsDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioIoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
NTSTATUS
|
|
CdAudioHPCdrDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
HpCdrProcessLastSession(
|
|
IN PCDROM_TOC Toc
|
|
);
|
|
|
|
NTSTATUS
|
|
HPCdrCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
);
|
|
|
|
//
|
|
// from class.h
|
|
//
|
|
|
|
NTSTATUS
|
|
ScsiClassSendSrbSynchronous(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PSCSI_REQUEST_BLOCK Srb,
|
|
PVOID BufferAddress,
|
|
ULONG BufferLength,
|
|
BOOLEAN WriteToDevice
|
|
);
|
|
|
|
VOID
|
|
ScsiClassReleaseQueue(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
|
|
#if 0
|
|
//
|
|
// define for private CdDump
|
|
//
|
|
|
|
int
|
|
sprintf(
|
|
char *s,
|
|
const char *format,
|
|
...
|
|
);
|
|
#endif
|
|
|
|
//
|
|
// Define the sections that allow for discarding (i.e. paging) some of
|
|
// the code. NEC is put into one section, all others go into another
|
|
// section. This way unless there are both and NEC and one of the other
|
|
// devices, some amount of code is freed.
|
|
//
|
|
|
|
#pragma alloc_text(INIT, DriverEntry)
|
|
#pragma alloc_text(PAGECDNC, CdAudioNECSendSrbSynchronous)
|
|
#pragma alloc_text(PAGECDNC, CdAudioNECInterpretSenseInfo)
|
|
#pragma alloc_text(PAGECDNC, CdAudioNECDeviceControl)
|
|
#pragma alloc_text(PAGECDOT, CdAudioHitachiSendPauseCommand)
|
|
#pragma alloc_text(PAGECDOT, CdAudioHitachiDeviceControl)
|
|
#pragma alloc_text(PAGECDOT, CdAudioDenonDeviceControl)
|
|
#pragma alloc_text(PAGECDNC, CdAudio435DeviceControl)
|
|
#pragma alloc_text(PAGECDNC, CdAudio535DeviceControl)
|
|
#pragma alloc_text(PAGECDOT, CdAudioPioneerDeviceControl)
|
|
#pragma alloc_text(PAGECDNC, CdAudioPan533DeviceControl)
|
|
#pragma alloc_text(PAGECDOT, CdAudioAtapiDeviceControl)
|
|
#pragma alloc_text(PAGECDOT, CdAudioLionOpticsDeviceControl)
|
|
#pragma alloc_text(PAGECDOT, CdAudioHPCdrDeviceControl)
|
|
#pragma alloc_text(PAGECDOT, HpCdrProcessLastSession)
|
|
#pragma alloc_text(PAGECDOT, HPCdrCompletion)
|
|
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize CdAudio driver.
|
|
This is the system initialization entry point when
|
|
the driver is linked into the kernel.
|
|
|
|
Arguments:
|
|
|
|
DriverObject
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCONFIGURATION_INFORMATION configurationInformation;
|
|
UCHAR deviceNameBuffer[64];
|
|
ANSI_STRING deviceName;
|
|
UNICODE_STRING unicodeDeviceName;
|
|
PDEVICE_OBJECT cdaudioDevice;
|
|
PCD_DEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS status;
|
|
ULONG disks,activedisks;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb = (PCDB)srb.Cdb;
|
|
PUCHAR inquiryDataPtr;
|
|
ULONG i;
|
|
|
|
//
|
|
// Set up the device driver entry points.
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = CdAudioCreate;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = CdAudioReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = CdAudioReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CdAudioDeviceControl;
|
|
|
|
//
|
|
// Get the configuration information for this driver.
|
|
//
|
|
|
|
configurationInformation = IoGetConfigurationInformation();
|
|
|
|
CdDump((1, "CdAudioInitialize: Found %d CdRom drives\n",
|
|
configurationInformation->CdRomCount));
|
|
|
|
//
|
|
// Find disk devices.
|
|
//
|
|
|
|
activedisks = 0;
|
|
for (disks = 0; disks < configurationInformation->CdRomCount; disks++) {
|
|
|
|
//
|
|
// Create device name for CdAudio (one per CdRom).
|
|
//
|
|
|
|
sprintf(deviceNameBuffer,
|
|
"\\Device\\CdAudio%d",
|
|
disks);
|
|
CdDump(( 5, "CdAudioInitialize: Checking \\Device\\CdRom%d\n",
|
|
disks ));
|
|
RtlInitAnsiString(&deviceName,
|
|
deviceNameBuffer);
|
|
RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
|
&deviceName,
|
|
TRUE);
|
|
|
|
//
|
|
// Create the device object for CdAudio.
|
|
//
|
|
|
|
status = IoCreateDevice(DriverObject,
|
|
sizeof(CD_DEVICE_EXTENSION),
|
|
&unicodeDeviceName,
|
|
FILE_DEVICE_CD_ROM,
|
|
0,
|
|
FALSE,
|
|
&cdaudioDevice);
|
|
|
|
RtlFreeUnicodeString(&unicodeDeviceName);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CdDump((1, "CdAudioInitialize: IoCreateDevice( \"%s\" ) failed, 0x%lX\n",
|
|
deviceNameBuffer, status));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Point device extension back at device object.
|
|
//
|
|
|
|
deviceExtension = cdaudioDevice->DeviceExtension;
|
|
deviceExtension->DeviceObject = cdaudioDevice;
|
|
|
|
//
|
|
// Attach to cdrom drive. This call links the newly created
|
|
// device to the target device, returning the target device
|
|
// object.
|
|
//
|
|
|
|
sprintf(deviceNameBuffer,
|
|
"\\Device\\CdRom%d",
|
|
disks);
|
|
RtlInitAnsiString(&deviceName,
|
|
deviceNameBuffer);
|
|
RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
|
&deviceName,
|
|
TRUE);
|
|
status = IoAttachDevice(cdaudioDevice,
|
|
&unicodeDeviceName,
|
|
&deviceExtension->TargetDeviceObject);
|
|
RtlFreeUnicodeString(&unicodeDeviceName);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IoDeleteDevice(cdaudioDevice);
|
|
CdDump((1, "CdAudioInialize: IoAttachDevice( \"%s\" ) failed, 0x%lX\n",
|
|
deviceNameBuffer, status));
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Initialize the class device extension. This is used by the
|
|
// class commands such as ScsiClassSendSrbSynchronous. The only fields
|
|
// used are the port device object pointer which is set to the attached
|
|
// device. The DeviceNumber, DeviceObject, TimeOutValue and
|
|
// PhysicalDevice are also used. The logical unit address will be set
|
|
// by the lower driver.
|
|
//
|
|
|
|
deviceExtension->ClassDeviceExtension.DeviceObject = cdaudioDevice;
|
|
deviceExtension->ClassDeviceExtension.DeviceNumber = disks;
|
|
deviceExtension->ClassDeviceExtension.PortDeviceObject =
|
|
deviceExtension->TargetDeviceObject;
|
|
deviceExtension->ClassDeviceExtension.TimeOutValue = AUDIO_TIMEOUT;
|
|
deviceExtension->ClassDeviceExtension.PhysicalDevice = cdaudioDevice;
|
|
|
|
//
|
|
// Indicate to drivers above us that we want IRPs
|
|
// with MDLs, so we can pass them directly on to
|
|
// scsicdrm.sys when we don't handle the call.
|
|
//
|
|
|
|
cdaudioDevice->Flags |= DO_DIRECT_IO;
|
|
|
|
//
|
|
// Check to see if we want to install for this
|
|
// cdrom drive. Build up cdb/srb, and send it
|
|
// to this drive.
|
|
//
|
|
|
|
//
|
|
// Zero CDB in SRB on stack
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// Fill in CDB for INQUIRY to CDROM
|
|
//
|
|
|
|
cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY;
|
|
cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE;
|
|
|
|
//
|
|
// Allocate buffer for returned inquiry data
|
|
//
|
|
|
|
inquiryDataPtr = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
INQUIRYDATABUFFERSIZE
|
|
);
|
|
|
|
//
|
|
// Set length of CDB
|
|
//
|
|
|
|
srb.CdbLength = 6;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
inquiryDataPtr,
|
|
INQUIRYDATABUFFERSIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1, "CdAudioInitialize: Inquiry failed! (0x%lx)\n",
|
|
status ));
|
|
|
|
ExFreePool( inquiryDataPtr );
|
|
IoDetachDevice(deviceExtension->TargetDeviceObject);
|
|
IoDeleteDevice(cdaudioDevice);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Initialize device extension data
|
|
//
|
|
|
|
deviceExtension->Active = CDAUDIO_NOT_ACTIVE;
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
//
|
|
// Check for NEC drive
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='N') &&
|
|
(inquiryDataPtr[9] =='E') &&
|
|
(inquiryDataPtr[10] =='C')) {
|
|
|
|
|
|
if (NecSupportNeeded(inquiryDataPtr)) {
|
|
|
|
//
|
|
// Map NEC support section into non-paged pool
|
|
//
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioNECDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_NEC;
|
|
CdDump(( 3, "CdAudioInitialize: %s is a NEC drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for PIONEER DRM-6xx drive
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='P') &&
|
|
(inquiryDataPtr[9] =='I') &&
|
|
(inquiryDataPtr[10]=='O') &&
|
|
(inquiryDataPtr[23]=='D') &&
|
|
(inquiryDataPtr[24]=='R') &&
|
|
(inquiryDataPtr[25]=='M') &&
|
|
(inquiryDataPtr[27]=='6')) {
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioPioneerDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_PIONEER;
|
|
|
|
if (inquiryDataPtr[28] != '0') {
|
|
deviceExtension->Active= CDAUDIO_PIONEER624;
|
|
}
|
|
|
|
CdDump(( 3, "CdAudioInitialize: %s is a PIONEER DRM-6xx drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
|
|
//
|
|
// Check for DENON drive
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='D') &&
|
|
(inquiryDataPtr[9] =='E') &&
|
|
(inquiryDataPtr[10]=='N') &&
|
|
(inquiryDataPtr[16]=='D') &&
|
|
(inquiryDataPtr[17]=='R') &&
|
|
(inquiryDataPtr[18]=='D') &&
|
|
(inquiryDataPtr[20]=='2') &&
|
|
(inquiryDataPtr[21]=='5') &&
|
|
(inquiryDataPtr[22]=='X')) {
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioDenonDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_DENON;
|
|
CdDump(( 3, "CdAudioInitialize: %s is a DENON DRD-25X drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
|
|
//
|
|
// Check for Chinon CDS-535
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='C') &&
|
|
(inquiryDataPtr[9] =='H') &&
|
|
(inquiryDataPtr[10]=='I') &&
|
|
(inquiryDataPtr[11]=='N') &&
|
|
(inquiryDataPtr[12]=='O') &&
|
|
(inquiryDataPtr[13]=='N') &&
|
|
(inquiryDataPtr[27]=='5') &&
|
|
(inquiryDataPtr[28]=='3') &&
|
|
(inquiryDataPtr[29]=='5') &&
|
|
(inquiryDataPtr[32]=='Q')) {
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudio535DeviceControl);
|
|
deviceExtension->Active = CDAUDIO_CDS535;
|
|
CdDump(( 3, "CdAudioInitialize: %s is a Chinon CDS-535 drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
|
|
//
|
|
// Check for Chinon CDS-435 or CDS-431
|
|
// (willing to handle versions M/N, S/U, and H)
|
|
|
|
if ((inquiryDataPtr[8] =='C') &&
|
|
(inquiryDataPtr[9] =='H') &&
|
|
(inquiryDataPtr[10]=='I') &&
|
|
(inquiryDataPtr[11]=='N') &&
|
|
(inquiryDataPtr[12]=='O') &&
|
|
(inquiryDataPtr[13]=='N') &&
|
|
(inquiryDataPtr[27]=='4') &&
|
|
(inquiryDataPtr[28]=='3') &&
|
|
((inquiryDataPtr[29]=='5') || (inquiryDataPtr[29]=='1')) &&
|
|
((inquiryDataPtr[32]=='M') ||
|
|
(inquiryDataPtr[32]=='N') ||
|
|
(inquiryDataPtr[32]=='S') ||
|
|
(inquiryDataPtr[32]=='U') ||
|
|
(inquiryDataPtr[32]=='H')
|
|
)) {
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudio435DeviceControl);
|
|
deviceExtension->Active = CDAUDIO_CDS435;
|
|
CdDump(( 3, "CdAudioInitialize: %s is a Chinon CDS-435/431 drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
|
|
//
|
|
// Check for HITACHI drives
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='H') &&
|
|
(inquiryDataPtr[9] =='I') &&
|
|
(inquiryDataPtr[10]=='T') &&
|
|
(inquiryDataPtr[16]=='C') &&
|
|
(inquiryDataPtr[17]=='D') &&
|
|
(inquiryDataPtr[18]=='R')) {
|
|
|
|
//
|
|
// It's an Hitachi drive...is it one we want to handle...?
|
|
//
|
|
|
|
if ( ((inquiryDataPtr[20]=='1') &&
|
|
(inquiryDataPtr[21]=='7') &&
|
|
(inquiryDataPtr[22]=='5') &&
|
|
(inquiryDataPtr[23]=='0') &&
|
|
(inquiryDataPtr[24]=='S')) ||
|
|
|
|
((inquiryDataPtr[20]=='1') &&
|
|
(inquiryDataPtr[21]=='6') &&
|
|
(inquiryDataPtr[22]=='5') &&
|
|
(inquiryDataPtr[23]=='0') &&
|
|
(inquiryDataPtr[24]=='S')) ||
|
|
|
|
((inquiryDataPtr[20]=='3') &&
|
|
(inquiryDataPtr[21]=='6') &&
|
|
(inquiryDataPtr[22]=='5') &&
|
|
(inquiryDataPtr[23]=='0'))
|
|
|
|
) {
|
|
|
|
//
|
|
// Yes, we want to handle this HITACHI drive...
|
|
//
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioHitachiDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_HITACHI;
|
|
inquiryDataPtr[25] = (UCHAR)0;
|
|
CdDump(( 3,
|
|
"CdAudioInitialize: %s is a HITACHI %s drive.\n",
|
|
deviceNameBuffer, &inquiryDataPtr[16] ));
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check for Panasonic CR-533
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='M') &&
|
|
(inquiryDataPtr[9] =='A') &&
|
|
(inquiryDataPtr[10]=='T') &&
|
|
(inquiryDataPtr[11]=='S') &&
|
|
(inquiryDataPtr[12]=='H') &&
|
|
(inquiryDataPtr[13]=='I') &&
|
|
(inquiryDataPtr[23]=='C') &&
|
|
(inquiryDataPtr[24]=='R') &&
|
|
(inquiryDataPtr[26]=='5') &&
|
|
(inquiryDataPtr[27]=='3') &&
|
|
(inquiryDataPtr[28]=='3')
|
|
) {
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioPan533DeviceControl);
|
|
deviceExtension->Active = CDAUDIO_CR533;
|
|
inquiryDataPtr[25] = (UCHAR)0;
|
|
CdDump(( 3, "CdAudioInitialize: %s is a Panasonic CR-533 drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
|
|
//
|
|
// Check for Atapi drives that require support.
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='W') &&
|
|
(inquiryDataPtr[9] =='E') &&
|
|
(inquiryDataPtr[10]=='A') &&
|
|
(inquiryDataPtr[11]=='R') &&
|
|
(inquiryDataPtr[12]=='N') &&
|
|
(inquiryDataPtr[13]=='E') &&
|
|
(inquiryDataPtr[14]=='S') &&
|
|
(inquiryDataPtr[15]==' ') &&
|
|
(inquiryDataPtr[16]=='R') &&
|
|
(inquiryDataPtr[17]=='U') &&
|
|
(inquiryDataPtr[18]=='B') ||
|
|
|
|
(inquiryDataPtr[8] =='O') &&
|
|
(inquiryDataPtr[9] =='T') &&
|
|
(inquiryDataPtr[10]=='I') &&
|
|
(inquiryDataPtr[11]==' ') &&
|
|
(inquiryDataPtr[16]=='D') &&
|
|
(inquiryDataPtr[17]=='O') &&
|
|
(inquiryDataPtr[18]=='L') &&
|
|
(inquiryDataPtr[19]=='P') &&
|
|
(inquiryDataPtr[20]=='H')
|
|
|
|
) {
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioAtapiDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_ATAPI;
|
|
inquiryDataPtr[25] = (UCHAR)0;
|
|
CdDump(( 3, "CdAudioInitialize: %s is an Atapi drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
|
|
//
|
|
// Check for FUJITSU drives
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='F') &&
|
|
(inquiryDataPtr[9] =='U') &&
|
|
(inquiryDataPtr[10]=='J') &&
|
|
(inquiryDataPtr[11]=='I') &&
|
|
(inquiryDataPtr[12]=='T')) {
|
|
|
|
//
|
|
// It's a Fujitsu drive...is it one we want to
|
|
// handle...?
|
|
//
|
|
|
|
if ((inquiryDataPtr[16]=='C') &&
|
|
(inquiryDataPtr[17]=='D') &&
|
|
(inquiryDataPtr[18]=='R') &&
|
|
(inquiryDataPtr[20]=='3') &&
|
|
(inquiryDataPtr[21]=='6') &&
|
|
(inquiryDataPtr[22]=='5') &&
|
|
(inquiryDataPtr[23]=='0')) {
|
|
|
|
//
|
|
// Yes, we want to handle this as HITACHI compatible drive...
|
|
//
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioHitachiDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_HITACHI;
|
|
inquiryDataPtr[25] = (UCHAR)0;
|
|
CdDump(( 3,
|
|
"CdAudioInitialize: %s is a FUJITSU %s drive.\n",
|
|
deviceNameBuffer, &inquiryDataPtr[16] ));
|
|
|
|
} else if ((inquiryDataPtr[16]=='F') &&
|
|
(inquiryDataPtr[17]=='M') &&
|
|
(inquiryDataPtr[18]=='C') &&
|
|
(inquiryDataPtr[21]=='1') &&
|
|
(inquiryDataPtr[22]=='0') &&
|
|
((inquiryDataPtr[23]=='1') ||
|
|
(inquiryDataPtr[23]=='2')) ) {
|
|
|
|
//
|
|
// Yes, we want to handle this as FUJITSU drive...
|
|
//
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioHitachiDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_FUJITSU;
|
|
inquiryDataPtr[25] = (UCHAR)0;
|
|
CdDump(( 3,
|
|
"CdAudioInitialize: %s is a FUJITSU %s drive.\n",
|
|
deviceNameBuffer, &inquiryDataPtr[16] ));
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check for HP CDR
|
|
//
|
|
|
|
if ((inquiryDataPtr[8] =='H') &&
|
|
(inquiryDataPtr[9] =='P') &&
|
|
(inquiryDataPtr[10]==' ') &&
|
|
(inquiryDataPtr[11]==' ') &&
|
|
(inquiryDataPtr[12]==' ') &&
|
|
(inquiryDataPtr[13]==' ') &&
|
|
(inquiryDataPtr[14]==' ') &&
|
|
(inquiryDataPtr[15]==' ') &&
|
|
(inquiryDataPtr[16]=='C') &&
|
|
(inquiryDataPtr[17]=='4') &&
|
|
(inquiryDataPtr[18]=='3') &&
|
|
(inquiryDataPtr[19]=='2') &&
|
|
(inquiryDataPtr[20]=='4') &&
|
|
(inquiryDataPtr[21]=='/') &&
|
|
(inquiryDataPtr[22]=='C') &&
|
|
(inquiryDataPtr[23]=='4') &&
|
|
(inquiryDataPtr[24]=='3') &&
|
|
(inquiryDataPtr[25]=='2') &&
|
|
(inquiryDataPtr[26]=='5')
|
|
) {
|
|
|
|
MmLockPagableCodeSection((PVOID)CdAudioHPCdrDeviceControl);
|
|
deviceExtension->Active = CDAUDIO_HPCDR;
|
|
CdDump(( 3, "CdAudioInitialize: %s is an HPCDR drive.\n",
|
|
deviceNameBuffer ));
|
|
}
|
|
|
|
ExFreePool( inquiryDataPtr );
|
|
|
|
//
|
|
// Check to see if we attached to ANY drives.
|
|
//
|
|
|
|
if (deviceExtension->Active==CDAUDIO_NOT_ACTIVE) {
|
|
|
|
IoDetachDevice(deviceExtension->TargetDeviceObject);
|
|
IoDeleteDevice(cdaudioDevice);
|
|
CdDump((1, "CdAudioInitialize: failed attach to %s\n",
|
|
deviceNameBuffer));
|
|
|
|
} else
|
|
|
|
activedisks++;
|
|
|
|
} // end inquiry was successful
|
|
|
|
} // end for (disks ...)
|
|
|
|
CdDump((1, "CdAudioInitialize: Done searching all cdrom drives\n"));
|
|
|
|
if (activedisks==0) {
|
|
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
} else {
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
} // end DriverEntry()
|
|
|
|
#define NEC_NO_CDAUDIO_SUPPORT_DRIVES 9
|
|
|
|
BOOLEAN
|
|
NecSupportNeeded(
|
|
PUCHAR InquiryData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether the NEC drive in question needs assistance from
|
|
this driver.
|
|
|
|
Arguments:
|
|
|
|
InquiryData - Pointer to the inquiry data buffer.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if support is needed.
|
|
|
|
--*/
|
|
|
|
{
|
|
PINQUIRYDATA inquiryData = (PINQUIRYDATA)InquiryData;
|
|
ULONG i;
|
|
PUCHAR goodDriveList[NEC_NO_CDAUDIO_SUPPORT_DRIVES] = {"CD-ROM 1300A ",
|
|
"CD-ROM DRIVE:273",
|
|
"CD-ROM DRIVE:502",
|
|
"CD-ROM DRIVE:260",
|
|
"CD-ROM DRIVE:280",
|
|
"CD-ROM DRIVE:282",
|
|
"CD-ROM DRIVE:271",
|
|
"CD-ROM DRIVE:272",
|
|
"CD-ROM DRIVE:251"};
|
|
|
|
|
|
for (i = 0; i < NEC_NO_CDAUDIO_SUPPORT_DRIVES; i++) {
|
|
if (RtlCompareMemory(inquiryData->ProductId, goodDriveList[i], 16) == 16) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioCreate(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine serves create commands. It does no more than
|
|
establish the drivers existance by returning status success.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
IRP
|
|
|
|
Return Value:
|
|
|
|
NT Status
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(Irp, 0);
|
|
return STATUS_SUCCESS;
|
|
|
|
} // end CdAudioCreate()
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the driver entry point for read and write requests
|
|
to the cdrom. Since we only want to trap device control requests,
|
|
we will just pass these requests on to the original driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to device object for disk partition
|
|
Irp - NT IO Request Packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of request
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (deviceExtension->PlayActive) {
|
|
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_DEVICE_BUSY;
|
|
}
|
|
|
|
//
|
|
// simply return status of driver below us...
|
|
//
|
|
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
|
|
} // end CdAudioReadWrite()
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the I/O subsystem for device controls.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
NTSTATUS status;
|
|
|
|
switch( deviceExtension->Active ) {
|
|
|
|
case CDAUDIO_NOT_ACTIVE:
|
|
CdDump(( 3,
|
|
"CdAudioDeviceControl: NOT ACTIVE for this drive.\n"
|
|
));
|
|
status = CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_NEC:
|
|
status = CdAudioNECDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_PIONEER:
|
|
case CDAUDIO_PIONEER624:
|
|
status = CdAudioPioneerDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_DENON:
|
|
status = CdAudioDenonDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_FUJITSU:
|
|
case CDAUDIO_HITACHI:
|
|
status = CdAudioHitachiDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_CDS535:
|
|
status = CdAudio535DeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_CDS435:
|
|
status = CdAudio435DeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_CR533:
|
|
status = CdAudioPan533DeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_ATAPI:
|
|
status = CdAudioAtapiDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
case CDAUDIO_HPCDR:
|
|
status = CdAudioHPCdrDeviceControl( DeviceObject, Irp );
|
|
break;
|
|
|
|
default:
|
|
CdDump(( 1,
|
|
"CdAudioDeviceControl: Active==UNKNOWN!!! This is bad!\n"
|
|
));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
} // end CdAudioDeviceControl()
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioSendToNextDriver(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is sends the Irp to the next driver in line
|
|
when the Irp is not processed by this driver.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Copy stack parameters to next stack.
|
|
//
|
|
|
|
RtlMoveMemory(nextIrpStack,
|
|
currentIrpStack,
|
|
sizeof(IO_STACK_LOCATION));
|
|
|
|
//
|
|
// Set IRP so IoComplete calls our completion routine.
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
CdAudioIoCompletion,
|
|
deviceExtension,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
//
|
|
// Pass unrecognized device control requests
|
|
// down to next driver layer.
|
|
//
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioIoCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the I/O request has completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - SimBad device object.
|
|
Irp - Completed request.
|
|
Context - not used. Set up to also be a pointer to the DeviceObject.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
|
|
//
|
|
// If pending is returned for this Irp then mark current stack
|
|
// as pending
|
|
//
|
|
|
|
if (Irp->PendingReturned) {
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
}
|
|
|
|
return Irp->IoStatus.Status;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
CdAudioIsPlayActive(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the cd is currently playing music.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device object to test.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the device is playing music.
|
|
|
|
--*/
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIRP irp;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
KEVENT event;
|
|
NTSTATUS status;
|
|
PSUB_Q_CURRENT_POSITION currentBuffer;
|
|
BOOLEAN returnValue;
|
|
|
|
if (!deviceExtension->PlayActive) {
|
|
return(FALSE);
|
|
}
|
|
|
|
currentBuffer = ExAllocatePool(NonPagedPoolCacheAligned, sizeof(SUB_Q_CURRENT_POSITION));
|
|
|
|
if (currentBuffer == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Format = IOCTL_CDROM_CURRENT_POSITION;
|
|
((PCDROM_SUB_Q_DATA_FORMAT) currentBuffer)->Track = 0;
|
|
|
|
//
|
|
// Create notification event object to be used to signal the
|
|
// request completion.
|
|
//
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Build the synchronous request to be sent to the port driver
|
|
// to perform the request.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_CDROM_READ_Q_CHANNEL,
|
|
deviceExtension->DeviceObject,
|
|
currentBuffer,
|
|
sizeof(CDROM_SUB_Q_DATA_FORMAT),
|
|
currentBuffer,
|
|
sizeof(SUB_Q_CURRENT_POSITION),
|
|
FALSE,
|
|
&event,
|
|
&ioStatus);
|
|
|
|
if (irp == NULL) {
|
|
ExFreePool(currentBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Pass request to port driver and wait for request to complete.
|
|
//
|
|
|
|
status = IoCallDriver(deviceExtension->DeviceObject, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
|
|
status = ioStatus.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(currentBuffer);
|
|
return FALSE;
|
|
}
|
|
|
|
if (currentBuffer->Header.AudioStatus == AUDIO_STATUS_IN_PROGRESS) {
|
|
|
|
returnValue = TRUE;
|
|
} else {
|
|
returnValue = FALSE;
|
|
deviceExtension->PlayActive = FALSE;
|
|
}
|
|
|
|
ExFreePool(currentBuffer);
|
|
|
|
return(returnValue);
|
|
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioNECInterpretSenseInfo(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN UCHAR MajorFunctionCode,
|
|
IN ULONG IoDeviceCode,
|
|
IN UCHAR RetryCount,
|
|
OUT NTSTATUS *Status
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine interprets the data returned from the SCSI
|
|
request sense. It determines the status to return in the
|
|
IRP and whether this request can be retried.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object associated with this request.
|
|
|
|
Srb - Supplies the scsi request block which failed.
|
|
|
|
MajorFunctionCode - Supplies the function code to be used for logging.
|
|
|
|
IoDeviceCode - Supplies the device code to be used for logging.
|
|
|
|
Status - Returns the status for the request.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN TRUE: Drivers should retry this request.
|
|
FALSE: Drivers should not retry this request.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PSENSE_DATA senseBuffer = Srb->SenseInfoBuffer;
|
|
PIRP irp = Srb->OriginalRequest;
|
|
BOOLEAN retry;
|
|
BOOLEAN logError;
|
|
ULONG uniqueId;
|
|
NTSTATUS logStatus;
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
ULONG badSector;
|
|
|
|
logError = FALSE;
|
|
retry = TRUE;
|
|
badSector = 0;
|
|
|
|
//
|
|
// Check that request sense buffer is valid.
|
|
//
|
|
|
|
if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) {
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Error code is %x\n",
|
|
senseBuffer->ErrorCode));
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Sense key is %x\n",
|
|
senseBuffer->SenseKey));
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Additional sense code is %x\n",
|
|
senseBuffer->CommandSpecificInformation[1]));
|
|
|
|
switch (senseBuffer->SenseKey & 0xf) {
|
|
|
|
case SCSI_SENSE_NOT_READY:
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Device not ready\n"));
|
|
|
|
*Status = STATUS_DEVICE_NOT_READY;
|
|
|
|
retry = TRUE;
|
|
|
|
switch (senseBuffer->CommandSpecificInformation[1]) {
|
|
|
|
case NEC_SCSI_ERROR_NO_DISC:
|
|
CdDump(( 1, "CdAudioNECInterpretSenseInfo: No disc in drive\n" ));
|
|
*Status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
case NEC_SCSI_ERROR_ILLEGAL_DISC:
|
|
CdDump(( 1, "CdAudioNECInterpretSenseInfo: Unrecognized media\n" ));
|
|
*Status = STATUS_UNRECOGNIZED_MEDIA;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
case NEC_SCSI_ERROR_TRAY_OPEN:
|
|
CdDump(( 1, "CdAudioNECInterpretSenseInfo: Tray open\n" ));
|
|
*Status = STATUS_NO_MEDIA_IN_DEVICE;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
} // end switch
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_DATA_PROTECT:
|
|
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Media write protected\n"));
|
|
|
|
*Status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
|
|
retry = FALSE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_MEDIUM_ERROR:
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Bad media\n"));
|
|
*Status = STATUS_DEVICE_DATA_ERROR;
|
|
|
|
retry = TRUE;
|
|
logError = TRUE;
|
|
uniqueId = 256;
|
|
logStatus = IO_ERR_BAD_BLOCK;
|
|
switch (senseBuffer->CommandSpecificInformation[1]) {
|
|
|
|
case NEC_SCSI_ERROR_SEEK_ERROR:
|
|
CdDump(( 1, "CdAudioNECInterpretSenseInfo: Sector not found\n" ));
|
|
*Status = STATUS_NONEXISTENT_SECTOR;
|
|
retry = FALSE;
|
|
break;
|
|
case NEC_SCSI_ERROR_MUSIC_AREA:
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Music area\n"));
|
|
break;
|
|
|
|
case NEC_SCSI_ERROR_DATA_AREA:
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Data area\n"));
|
|
break;
|
|
|
|
|
|
} // end switch
|
|
break;
|
|
|
|
case SCSI_SENSE_HARDWARE_ERROR:
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Hardware error\n"));
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
retry = TRUE;
|
|
logError = TRUE;
|
|
uniqueId = 257;
|
|
logStatus = IO_ERR_CONTROLLER_ERROR;
|
|
switch (senseBuffer->CommandSpecificInformation[1]) {
|
|
|
|
case NEC_SCSI_ERROR_PARITY_ERROR:
|
|
CdDump(( 1, "CdAudioNECInterpretSenseInfo: Parity error\n" ));
|
|
*Status = STATUS_PARITY_ERROR;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
} // end switch
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ILLEGAL_REQUEST:
|
|
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Illegal SCSI request\n"));
|
|
|
|
*Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
switch (senseBuffer->AdditionalSenseCode) {
|
|
|
|
case NEC_SCSI_ERROR_INVALID_COMMAND:
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Illegal command\n"));
|
|
break;
|
|
|
|
case NEC_SCSI_ERROR_INVALID_ADDRESS:
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Illegal block address\n"));
|
|
*Status = STATUS_NONEXISTENT_SECTOR;
|
|
break;
|
|
|
|
case NEC_SCSI_ERROR_END_OF_VOLUME:
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Volume overflow\n"));
|
|
*Status = STATUS_NONEXISTENT_SECTOR;
|
|
break;
|
|
|
|
case NEC_SCSI_ERROR_MEDIA_CHANGED:
|
|
case NEC_SCSI_ERROR_DEVICE_RESET:
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Media Changed or device reset\n"));
|
|
|
|
if (DeviceObject->Vpb->Flags & VPB_MOUNTED &&
|
|
DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
|
|
|
|
//
|
|
// Set bit to indicate that media may have changed
|
|
// and volume needs verification.
|
|
//
|
|
|
|
DeviceObject->Flags |= DO_VERIFY_VOLUME;
|
|
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
|
|
retry = FALSE;
|
|
|
|
} else {
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
retry = TRUE;
|
|
|
|
}
|
|
break;
|
|
|
|
|
|
} // end switch ...
|
|
|
|
retry = FALSE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_UNIT_ATTENTION:
|
|
|
|
CdDump((3,"CdAudioNECInterpretSenseInfo: Unit attention\n"));
|
|
|
|
if (DeviceObject->Vpb->Flags & VPB_MOUNTED &&
|
|
DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
|
|
|
|
//
|
|
// Set bit to indicate that media may have changed
|
|
// and volume needs verification.
|
|
//
|
|
|
|
DeviceObject->Flags |= DO_VERIFY_VOLUME;
|
|
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
|
|
retry = FALSE;
|
|
|
|
} else {
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
retry = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_ABORTED_COMMAND:
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Command aborted\n"));
|
|
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
retry = TRUE;
|
|
|
|
break;
|
|
|
|
case SCSI_SENSE_RECOVERED_ERROR:
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Recovered error\n"));
|
|
*Status = STATUS_SUCCESS;
|
|
retry = FALSE;
|
|
logError = TRUE;
|
|
uniqueId = 258;
|
|
logStatus = IO_ERR_CONTROLLER_ERROR;
|
|
break;
|
|
|
|
case SCSI_SENSE_NO_SENSE:
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: No specific sense key\n"));
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
retry = TRUE;
|
|
break;
|
|
|
|
case SCSI_SENSE_BLANK_CHECK:
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Media blank check\n"));
|
|
*Status = STATUS_NO_DATA_DETECTED;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
default:
|
|
|
|
CdDump((1,
|
|
"CdAudioNECInterpretSenseInfo: Unrecognized sense code\n"));
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
retry = TRUE;
|
|
|
|
} // end switch
|
|
|
|
} else {
|
|
|
|
//
|
|
// Request sense buffer not valid. No sense information
|
|
// to pinpoint the error. Return general request fail.
|
|
//
|
|
|
|
CdDump((1,"CdAudioNECInterpretSenseInfo: Request sense info not valid\n"));
|
|
retry = TRUE;
|
|
|
|
switch (SRB_STATUS(Srb->SrbStatus)) {
|
|
case SRB_STATUS_INVALID_LUN:
|
|
case SRB_STATUS_INVALID_TARGET_ID:
|
|
case SRB_STATUS_NO_DEVICE:
|
|
case SRB_STATUS_NO_HBA:
|
|
*Status = STATUS_NO_SUCH_DEVICE;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
case SRB_STATUS_COMMAND_TIMEOUT:
|
|
case SRB_STATUS_TIMEOUT:
|
|
*Status = STATUS_IO_TIMEOUT;
|
|
break;
|
|
|
|
case SRB_STATUS_SELECTION_TIMEOUT:
|
|
*Status = STATUS_DEVICE_NOT_CONNECTED;
|
|
break;
|
|
|
|
case SRB_STATUS_DATA_OVERRUN:
|
|
*Status = STATUS_DATA_OVERRUN;
|
|
retry = FALSE;
|
|
break;
|
|
|
|
case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
|
|
//
|
|
// If there was phase sequence error then limit the number of
|
|
// retries.
|
|
//
|
|
|
|
if (RetryCount > 1 ) {
|
|
retry = FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case SRB_STATUS_REQUEST_FLUSHED:
|
|
|
|
//
|
|
// If the status needs verification bit is set. Then set
|
|
// the status to need verificaiton and no retry; otherwise,
|
|
// just retry the request.
|
|
//
|
|
|
|
if (DeviceObject->Flags & DO_VERIFY_VOLUME) {
|
|
|
|
*Status = STATUS_VERIFY_REQUIRED;
|
|
retry = FALSE;
|
|
} else {
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
retry = TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
*Status = STATUS_IO_DEVICE_ERROR;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If there is a class specific error handler call it.
|
|
//
|
|
|
|
// if (deviceExtension->ClassError != NULL) {
|
|
//
|
|
// deviceExtension->ClassError(
|
|
// DeviceObject,
|
|
// Srb
|
|
// );
|
|
// }
|
|
|
|
if (logError) {
|
|
|
|
errorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
|
|
DeviceObject,
|
|
sizeof(IO_ERROR_LOG_PACKET) + 5 * sizeof(ULONG)
|
|
);
|
|
|
|
if (errorLogEntry == NULL) {
|
|
|
|
return retry;
|
|
|
|
}
|
|
|
|
if (retry && RetryCount < MAXIMUM_RETRIES) {
|
|
errorLogEntry->FinalStatus = STATUS_SUCCESS;
|
|
} else {
|
|
errorLogEntry->FinalStatus = *Status;
|
|
}
|
|
|
|
errorLogEntry->ErrorCode = logStatus;
|
|
errorLogEntry->SequenceNumber = 0;
|
|
errorLogEntry->MajorFunctionCode = MajorFunctionCode;
|
|
errorLogEntry->IoControlCode = IoDeviceCode;
|
|
errorLogEntry->RetryCount = RetryCount;
|
|
errorLogEntry->DumpDataSize = 5 * sizeof(ULONG);
|
|
errorLogEntry->UniqueErrorValue = uniqueId;
|
|
errorLogEntry->DumpDataSize = 5 * sizeof(ULONG);
|
|
errorLogEntry->DumpData[0] = Srb->PathId;
|
|
errorLogEntry->DumpData[1] = Srb->TargetId;
|
|
errorLogEntry->DumpData[2] = Srb->Lun;
|
|
errorLogEntry->DumpData[3] = 0;
|
|
errorLogEntry->DumpData[4] = Srb->SrbStatus << 8 | Srb->ScsiStatus;
|
|
errorLogEntry->DumpData[5] = senseBuffer->SenseKey << 16 |
|
|
senseBuffer->AdditionalSenseCode << 8 |
|
|
senseBuffer->AdditionalSenseCodeQualifier;
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
|
|
return retry;
|
|
|
|
} // end InterpretSenseInfo()
|
|
|
|
NTSTATUS
|
|
CdAudioNECSendSrbSynchronous(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PSCSI_REQUEST_BLOCK Srb,
|
|
PVOID BufferAddress,
|
|
ULONG BufferLength,
|
|
BOOLEAN WriteToDevice
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by SCSI device controls to complete an
|
|
SRB and send it to the port driver synchronously (ie wait for
|
|
completion).
|
|
The CDB is already completed along with the SRB CDB size and
|
|
request timeout value.
|
|
|
|
Arguments:
|
|
|
|
Device Object
|
|
SRB
|
|
Buffer address and length (if transfer)
|
|
WriteToDevice
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
UCHAR retryCount = MAXIMUM_RETRIES;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
KEVENT event;
|
|
PUCHAR senseInfoBuffer;
|
|
LARGE_INTEGER largeInt;
|
|
NTSTATUS status;
|
|
BOOLEAN retry;
|
|
|
|
largeInt.QuadPart = 0;
|
|
//
|
|
// Write length to SRB.
|
|
//
|
|
|
|
Srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|
|
|
//
|
|
// Set SCSI bus address.
|
|
//
|
|
|
|
Srb->PathId = deviceExtension->PathId;
|
|
Srb->TargetId = deviceExtension->TargetId;
|
|
Srb->Lun = deviceExtension->Lun;
|
|
|
|
Srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|
|
|
//
|
|
// This is a violation of the SCSI spec but it is required for
|
|
// some targets.
|
|
//
|
|
|
|
Srb->Cdb[1] |= deviceExtension->Lun << 5;
|
|
|
|
//
|
|
// Enable auto request sense.
|
|
//
|
|
|
|
Srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|
|
|
//
|
|
// Sense buffer is in non-paged pool.
|
|
//
|
|
|
|
senseInfoBuffer = ExAllocatePool( NonPagedPoolCacheAligned,
|
|
SENSE_BUFFER_SIZE
|
|
);
|
|
|
|
if (senseInfoBuffer == NULL) {
|
|
|
|
CdDump((1,
|
|
"CdAudioNECSendSrbSynchronous: Can't allocate request sense buffer\n"));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
Srb->SenseInfoBuffer = senseInfoBuffer;
|
|
|
|
Srb->DataBuffer = BufferAddress;
|
|
|
|
//
|
|
// Start retries here.
|
|
//
|
|
|
|
retry:
|
|
|
|
if (BufferAddress != NULL) {
|
|
|
|
//
|
|
// Build the synchronous request with data transfer.
|
|
//
|
|
|
|
irp = IoBuildSynchronousFsdRequest(
|
|
WriteToDevice ? IRP_MJ_WRITE : IRP_MJ_READ,
|
|
deviceExtension->PortDeviceObject,
|
|
BufferAddress,
|
|
BufferLength,
|
|
&largeInt,
|
|
&event,
|
|
&ioStatus);
|
|
|
|
if (irp == NULL) {
|
|
ExFreePool(senseInfoBuffer);
|
|
CdDump((1, "CdAudioNECSendSrbSynchronous: Can't allocate Irp\n"));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Set read flag.
|
|
//
|
|
|
|
Srb->SrbFlags = WriteToDevice ? SRB_FLAGS_DATA_OUT : SRB_FLAGS_DATA_IN;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Build synchronous request with no transfer.
|
|
//
|
|
|
|
irp = IoBuildDeviceIoControlRequest(
|
|
IRP_MJ_DEVICE_CONTROL,
|
|
deviceExtension->PortDeviceObject,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
&event,
|
|
&ioStatus
|
|
);
|
|
|
|
if (irp == NULL) {
|
|
ExFreePool(senseInfoBuffer);
|
|
CdDump((1, "ScsiClassSendSrbSynchronous: Can't allocate Irp\n"));
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// Clear flags.
|
|
//
|
|
|
|
Srb->SrbFlags = SRB_FLAGS_NO_DATA_TRANSFER;
|
|
}
|
|
|
|
//
|
|
// Disable synchronous transfer for these requests.
|
|
//
|
|
|
|
Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|
|
|
//
|
|
// Set the event object to the unsignaled state.
|
|
// It will be used to signal request completion.
|
|
//
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
//
|
|
// Set the transfer length.
|
|
//
|
|
|
|
Srb->DataTransferLength = BufferLength;
|
|
|
|
//
|
|
// Zero out status.
|
|
//
|
|
|
|
Srb->ScsiStatus = Srb->SrbStatus = 0;
|
|
|
|
Srb->NextSrb = 0;
|
|
|
|
//
|
|
// Get next stack location and
|
|
// set major function code.
|
|
//
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
//
|
|
// Set up SRB for execute scsi request.
|
|
// Save SRB address in next stack for port driver.
|
|
//
|
|
|
|
irpStack->Parameters.Others.Argument1 = (PVOID)Srb;
|
|
|
|
//
|
|
// Set up IRP Address.
|
|
//
|
|
|
|
Srb->OriginalRequest = irp;
|
|
|
|
//
|
|
// No need to check the following 2 returned statuses as
|
|
// SRB will have ending status.
|
|
//
|
|
|
|
status = IoCallDriver(deviceExtension->PortDeviceObject, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
|
|
}
|
|
|
|
//
|
|
// Check that request completed without error.
|
|
//
|
|
|
|
if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) {
|
|
|
|
//
|
|
// Release the queue if it is frozen.
|
|
//
|
|
|
|
if (Srb->SrbStatus & SRB_STATUS_QUEUE_FROZEN) {
|
|
ScsiClassReleaseQueue(DeviceObject);
|
|
}
|
|
|
|
//
|
|
// Update status and determine if request should be retried.
|
|
//
|
|
|
|
retry = (BOOLEAN)CdAudioNECInterpretSenseInfo(
|
|
DeviceObject,
|
|
Srb,
|
|
IRP_MJ_SCSI,
|
|
0,
|
|
(UCHAR)(MAXIMUM_RETRIES - retryCount),
|
|
&status
|
|
);
|
|
|
|
if (retry) {
|
|
|
|
if ((status == STATUS_DEVICE_NOT_READY && ((PSENSE_DATA) senseInfoBuffer)
|
|
->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) ||
|
|
SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SELECTION_TIMEOUT) {
|
|
|
|
LARGE_INTEGER delay;
|
|
|
|
//
|
|
// Delay for 2 seconds.
|
|
//
|
|
|
|
delay.LowPart = (ULONG)(- ( 10 * 1000 * 1000 * 2 ));
|
|
delay.HighPart = -1;
|
|
|
|
|
|
//
|
|
// Stall for a while to let the controller spinup.
|
|
//
|
|
|
|
(VOID) KeDelayExecutionThread(
|
|
KernelMode,
|
|
FALSE,
|
|
&delay
|
|
);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If retries are not exhausted then
|
|
// retry this operation.
|
|
//
|
|
|
|
if (retryCount--) {
|
|
goto retry;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
ExFreePool(senseInfoBuffer);
|
|
return status;
|
|
|
|
} // end CdAudioNECSendSrbSynchronous()
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioNECDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by CdAudioDeviceControl to handle
|
|
audio IOCTLs sent to NEC cdrom drives.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCDROM_TOC cdaudioDataOut = Irp->AssociatedIrp.SystemBuffer;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PNEC_CDB cdb = (PNEC_CDB)srb.Cdb;
|
|
NTSTATUS status;
|
|
ULONG i,bytesTransfered;
|
|
PUCHAR Toc;
|
|
ULONG retryCount = 0;
|
|
ULONG address;
|
|
LARGE_INTEGER delay;
|
|
|
|
|
|
NECRestart:
|
|
|
|
//
|
|
// Clear out cdb
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// What IOCTL do we need to execute?
|
|
//
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_GET_LAST_SESSION:
|
|
|
|
CdDump(( 2,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_GET_LAST_SESSION received.\n"
|
|
));
|
|
|
|
if (currentIrpStack->Parameters.Read.Length <
|
|
(ULONG)FIELD_OFFSET(CDROM_TOC, TrackData[1])) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
NEC_CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
|
|
RtlZeroMemory( Toc, NEC_CDROM_TOC_SIZE );
|
|
srb.CdbLength = 10;
|
|
|
|
//
|
|
// Fill in CDB
|
|
//
|
|
|
|
cdb->NEC_READ_TOC.OperationCode = NEC_READ_TOC_CODE;
|
|
cdb->NEC_READ_TOC.Type = NEC_TRANSFER_WHOLE_TOC;
|
|
cdb->NEC_READ_TOC.TrackNumber = NEC_TOC_TYPE_SESSION;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
NEC_CDROM_TOC_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioNECDeviceControl: READ_TOC error (0x%08lX)\n",
|
|
status ));
|
|
|
|
|
|
if (SRB_STATUS(srb.SrbStatus)!=SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "CdAudioNECDeviceControl: SRB ERROR (0x%lX)\n",
|
|
srb.SrbStatus ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into our format.
|
|
//
|
|
|
|
bytesTransfered = FIELD_OFFSET(CDROM_TOC, TrackData[1]);
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
|
|
RtlZeroMemory(cdaudioDataOut, bytesTransfered);
|
|
|
|
cdaudioDataOut->Length[0] = (UCHAR)(bytesTransfered >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)((bytesTransfered & 0xFF));
|
|
|
|
//
|
|
// Determine if this is a multisession cd.
|
|
//
|
|
|
|
if (*((ULONG UNALIGNED *) &Toc[14]) == 0) {
|
|
|
|
//
|
|
// This is a single session disk. Just return.
|
|
//
|
|
|
|
ExFreePool(Toc);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Fake the session information.
|
|
//
|
|
|
|
cdaudioDataOut->FirstTrack = 1;
|
|
cdaudioDataOut->LastTrack = 2;
|
|
|
|
CdDump(( 4,
|
|
"CdAudioNECDeviceControl: Tracks %d - %d, (0x%lX bytes)\n",
|
|
cdaudioDataOut->FirstTrack,
|
|
cdaudioDataOut->LastTrack,
|
|
bytesTransfered
|
|
));
|
|
|
|
|
|
//
|
|
// Grab Information for the last session.
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[0].Reserved = 0;
|
|
cdaudioDataOut->TrackData[0].Control =
|
|
((Toc[2] & 0x0F) << 4) | (Toc[2] >> 4);
|
|
cdaudioDataOut->TrackData[0].TrackNumber = 1;
|
|
|
|
cdaudioDataOut->TrackData[0].Reserved1 = 0;
|
|
|
|
//
|
|
// Convert the minutes, seconds and frames to an absolute block
|
|
// address. The formula comes from NEC.
|
|
//
|
|
|
|
address = (BCD_TO_DEC(Toc[15]) * 60 + BCD_TO_DEC(Toc[16])) * 75 + BCD_TO_DEC(Toc[17]);
|
|
|
|
//
|
|
// Put the address in big-endian in the the user's TOC.
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[0].Address[0] = (UCHAR) (address >> 24);
|
|
cdaudioDataOut->TrackData[0].Address[1] = (UCHAR) (address >> 16);
|
|
cdaudioDataOut->TrackData[0].Address[2] = (UCHAR) (address >> 8);
|
|
cdaudioDataOut->TrackData[0].Address[3] = (UCHAR) address;
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( Toc );
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
|
|
CdDump(( 2,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_READ_TOC received.\n"
|
|
));
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
NEC_CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
CdDump(( 4,
|
|
"CdAudioNECDeviceControl: Toc = 0x%lX cdaudioDataOut = 0x%lX\n",
|
|
Toc, cdaudioDataOut
|
|
));
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
|
|
RtlZeroMemory( Toc, NEC_CDROM_TOC_SIZE );
|
|
srb.CdbLength = 10;
|
|
|
|
//
|
|
// Fill in CDB
|
|
//
|
|
|
|
cdb->NEC_READ_TOC.OperationCode = NEC_READ_TOC_CODE;
|
|
cdb->NEC_READ_TOC.Type = NEC_TRANSFER_WHOLE_TOC;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
NEC_CDROM_TOC_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioNECDeviceControl: READ_TOC error (0x%08lX)\n",
|
|
status ));
|
|
|
|
|
|
if (SRB_STATUS(srb.SrbStatus)!=SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "CdAudioNECDeviceControl: SRB ERROR (0x%lX)\n",
|
|
srb.SrbStatus ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into our format.
|
|
//
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.Read.Length > sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) : currentIrpStack->Parameters.Read.Length;
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
|
|
cdaudioDataOut->Length[0] = (UCHAR)(bytesTransfered >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(bytesTransfered & 0xFF);
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[9]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[19]);
|
|
|
|
CdDump(( 4,
|
|
"CdAudioNECDeviceControl: Tracks %d - %d, (0x%lX bytes)\n",
|
|
cdaudioDataOut->FirstTrack,
|
|
cdaudioDataOut->LastTrack,
|
|
bytesTransfered
|
|
));
|
|
|
|
|
|
for( i=0;
|
|
i<=( (ULONG)cdaudioDataOut->LastTrack -
|
|
(ULONG)cdaudioDataOut->FirstTrack );
|
|
i++
|
|
) {
|
|
|
|
//
|
|
// Grab Information for each track
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control =
|
|
((Toc[(i*10)+32] & 0x0F) << 4) | (Toc[(i*10)+32] >> 4);
|
|
cdaudioDataOut->TrackData[i].TrackNumber =
|
|
(UCHAR)(i + cdaudioDataOut->FirstTrack);
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = BCD_TO_DEC((Toc[(i*10)+39]));
|
|
cdaudioDataOut->TrackData[i].Address[2] = BCD_TO_DEC((Toc[(i*10)+40]));
|
|
cdaudioDataOut->TrackData[i].Address[3] = BCD_TO_DEC((Toc[(i*10)+41]));
|
|
|
|
CdDump(( 4,
|
|
"CdAudioNECDeviceControl: Track %d %d:%d:%d\n",
|
|
cdaudioDataOut->TrackData[i].TrackNumber,
|
|
cdaudioDataOut->TrackData[i].Address[1],
|
|
cdaudioDataOut->TrackData[i].Address[2],
|
|
cdaudioDataOut->TrackData[i].Address[3]
|
|
));
|
|
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Fake "lead out track" info
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = 0x10;
|
|
cdaudioDataOut->TrackData[i].TrackNumber = 0xaa;
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = BCD_TO_DEC(Toc[29]);
|
|
cdaudioDataOut->TrackData[i].Address[2] = BCD_TO_DEC(Toc[30]);
|
|
cdaudioDataOut->TrackData[i].Address[3] = BCD_TO_DEC(Toc[31]);
|
|
|
|
CdDump(( 4,
|
|
"CdAudioNECDeviceControl: Track %d %d:%d:%d\n",
|
|
cdaudioDataOut->TrackData[i].TrackNumber,
|
|
cdaudioDataOut->TrackData[i].Address[1],
|
|
cdaudioDataOut->TrackData[i].Address[2],
|
|
cdaudioDataOut->TrackData[i].Address[3]
|
|
));
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( Toc );
|
|
break;
|
|
|
|
case IOCTL_CDROM_STOP_AUDIO:
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Same as scsi-2 spec, so just send to default driver
|
|
//
|
|
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_PLAY_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// First, seek to Starting MSF and enter play mode.
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->NEC_PLAY_AUDIO.OperationCode = NEC_AUDIO_TRACK_SEARCH_CODE;
|
|
cdb->NEC_PLAY_AUDIO.PlayMode = NEC_ENTER_PLAY_MODE;
|
|
cdb->NEC_PLAY_AUDIO.Minute = DEC_TO_BCD(inputBuffer->StartingM);
|
|
cdb->NEC_PLAY_AUDIO.Second = DEC_TO_BCD(inputBuffer->StartingS);
|
|
cdb->NEC_PLAY_AUDIO.Frame = DEC_TO_BCD(inputBuffer->StartingF);
|
|
cdb->NEC_PLAY_AUDIO.Control = NEC_TYPE_ATIME;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: play start MSF is BCD(%x:%x:%x)\n",
|
|
cdb->NEC_PLAY_AUDIO.Minute,
|
|
cdb->NEC_PLAY_AUDIO.Second,
|
|
cdb->NEC_PLAY_AUDIO.Frame
|
|
));
|
|
|
|
|
|
status = CdAudioNECSendSrbSynchronous(deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Indicate the play actition is active.
|
|
//
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
|
|
//
|
|
// Now, set the termination point for the play operation
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
cdb->NEC_PLAY_AUDIO.OperationCode = NEC_PLAY_AUDIO_CODE;
|
|
cdb->NEC_PLAY_AUDIO.PlayMode = NEC_PLAY_STEREO;
|
|
cdb->NEC_PLAY_AUDIO.Minute = DEC_TO_BCD(inputBuffer->EndingM);
|
|
cdb->NEC_PLAY_AUDIO.Second = DEC_TO_BCD(inputBuffer->EndingS);
|
|
cdb->NEC_PLAY_AUDIO.Frame = DEC_TO_BCD(inputBuffer->EndingF);
|
|
cdb->NEC_PLAY_AUDIO.Control = NEC_TYPE_ATIME;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: play end MSF is BCD(%x:%x:%x)\n",
|
|
cdb->NEC_PLAY_AUDIO.Minute,
|
|
cdb->NEC_PLAY_AUDIO.Second,
|
|
cdb->NEC_PLAY_AUDIO.Frame
|
|
));
|
|
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_SEEK_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// seek to MSF and enter pause (still) mode.
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->NEC_SEEK_AUDIO.OperationCode = NEC_AUDIO_TRACK_SEARCH_CODE;
|
|
cdb->NEC_SEEK_AUDIO.Minute = DEC_TO_BCD(inputBuffer->M);
|
|
cdb->NEC_SEEK_AUDIO.Second = DEC_TO_BCD(inputBuffer->S);
|
|
cdb->NEC_SEEK_AUDIO.Frame = DEC_TO_BCD(inputBuffer->F);
|
|
cdb->NEC_SEEK_AUDIO.Control = NEC_TYPE_ATIME;
|
|
CdDump(( 4,
|
|
"CdAudioNECDeviceControl: seek MSF is %d:%d:%d\n",
|
|
cdb->NEC_SEEK_AUDIO.Minute,
|
|
cdb->NEC_SEEK_AUDIO.Second,
|
|
cdb->NEC_SEEK_AUDIO.Frame
|
|
));
|
|
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_PAUSE_AUDIO received.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->NEC_PAUSE_AUDIO.OperationCode = NEC_STILL_CODE;
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
break;
|
|
|
|
case IOCTL_CDROM_RESUME_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_RESUME_AUDIO received.\n"
|
|
));
|
|
|
|
//
|
|
// Resume play
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->NEC_PLAY_AUDIO.OperationCode = NEC_PLAY_AUDIO_CODE;
|
|
cdb->NEC_PLAY_AUDIO.PlayMode = NEC_PLAY_STEREO;
|
|
cdb->NEC_PLAY_AUDIO.Control = NEC_TYPE_NO_CHANGE;
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
{
|
|
|
|
PSUB_Q_CURRENT_POSITION userPtr =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
NEC_Q_CHANNEL_TRANSFER_SIZE
|
|
);
|
|
|
|
CdDump(( 5,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_READ_Q_CHANNEL received.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioNECDeviceControl: READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
RtlZeroMemory( SubQPtr, NEC_Q_CHANNEL_TRANSFER_SIZE );
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioNECDeviceControl: READ_Q_CHANNEL, illegal Format (%d)\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
NECSeek:
|
|
|
|
//
|
|
// Set up to read Q Channel
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->NEC_READ_Q_CHANNEL.OperationCode = NEC_READ_SUB_Q_CHANNEL_CODE;
|
|
cdb->NEC_READ_Q_CHANNEL.TransferSize = NEC_Q_CHANNEL_TRANSFER_SIZE; // Transfer Length
|
|
CdDump(( 4, "CdAudioNECDeviceControl: cdb = 0x%lx srb = 0x%x SubQPtr = 0x%lx\n",
|
|
cdb,
|
|
&srb,
|
|
SubQPtr
|
|
));
|
|
#if DBG
|
|
if (CdAudioDebug>5) {
|
|
|
|
DbgBreakPoint();
|
|
|
|
}
|
|
#endif
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
NEC_Q_CHANNEL_TRANSFER_SIZE,
|
|
FALSE
|
|
);
|
|
CdDump(( 4, "CdAudioNECDeviceControl: READ_Q_CHANNEL, status is 0x%lX\n",
|
|
status
|
|
));
|
|
|
|
if ((NT_SUCCESS(status)) || (status==STATUS_DATA_OVERRUN)) {
|
|
|
|
userPtr->Header.Reserved = 0;
|
|
if (SubQPtr[0]==0x00)
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_IN_PROGRESS;
|
|
else if (SubQPtr[0]==0x01) {
|
|
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PAUSED;
|
|
deviceExtension->PlayActive = FALSE;
|
|
} else if (SubQPtr[0]==0x02) {
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PAUSED;
|
|
deviceExtension->PlayActive = FALSE;
|
|
} else if (SubQPtr[0]==0x03) {
|
|
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
} else {
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
}
|
|
|
|
userPtr->Header.DataLength[0] = 0;
|
|
userPtr->Header.DataLength[0] = 12;
|
|
|
|
userPtr->FormatCode = 0x01;
|
|
userPtr->Control = SubQPtr[1] & 0x0F;
|
|
userPtr->ADR = 0;
|
|
userPtr->TrackNumber = BCD_TO_DEC(SubQPtr[2]);
|
|
userPtr->IndexNumber = BCD_TO_DEC(SubQPtr[3]);
|
|
userPtr->AbsoluteAddress[0] = 0;
|
|
userPtr->AbsoluteAddress[1] = BCD_TO_DEC((SubQPtr[7]));
|
|
userPtr->AbsoluteAddress[2] = BCD_TO_DEC((SubQPtr[8]));
|
|
userPtr->AbsoluteAddress[3] = BCD_TO_DEC((SubQPtr[9]));
|
|
userPtr->TrackRelativeAddress[0] = 0;
|
|
userPtr->TrackRelativeAddress[1] = BCD_TO_DEC((SubQPtr[4]));
|
|
userPtr->TrackRelativeAddress[2] = BCD_TO_DEC((SubQPtr[5]));
|
|
userPtr->TrackRelativeAddress[3] = BCD_TO_DEC((SubQPtr[6]));
|
|
Irp->IoStatus.Information = 16;
|
|
CdDump(( 5,
|
|
"CdAudioNECDeviceControl: <SubQPtr> Status = 0x%lx, [%x %x:%x] (%x:%x:%x)\n",
|
|
SubQPtr[0],
|
|
SubQPtr[2],
|
|
SubQPtr[4],
|
|
SubQPtr[5],
|
|
SubQPtr[7],
|
|
SubQPtr[8],
|
|
SubQPtr[9]
|
|
));
|
|
CdDump(( 5,
|
|
"CdAudioNECDeviceControl: <userPtr> Status = 0x%lx, [%d %d:%d] (%d:%d:%d)\n",
|
|
userPtr->Header.AudioStatus,
|
|
userPtr->TrackNumber,
|
|
userPtr->TrackRelativeAddress[1],
|
|
userPtr->TrackRelativeAddress[2],
|
|
userPtr->AbsoluteAddress[1],
|
|
userPtr->AbsoluteAddress[2],
|
|
userPtr->AbsoluteAddress[3]
|
|
));
|
|
|
|
//
|
|
// Sometimes the NEC will return a bogus value for track number.
|
|
// if this occurs just retry.
|
|
//
|
|
|
|
if (userPtr->TrackNumber > MAXIMUM_NUMBER_TRACKS) {
|
|
|
|
//
|
|
// Delay for .5 seconds.
|
|
//
|
|
|
|
delay.QuadPart = - 10 * 1000 * 100 * 5;
|
|
|
|
//
|
|
// Stall for a while to let the controller spinup.
|
|
//
|
|
|
|
KeDelayExecutionThread(KernelMode,
|
|
FALSE,
|
|
&delay);
|
|
|
|
if (retryCount++ < MAXIMUM_RETRIES) {
|
|
goto NECSeek;
|
|
} else {
|
|
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
|
|
} else {
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
CdDump(( 1,
|
|
"CdAudioNECDeviceControl: READ_Q_CHANNEL failed (0x%08lX)\n",
|
|
status
|
|
));
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: IOCTL_CDROM_EJECT_MEDIA received.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Set up to read Q Channel
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->NEC_EJECT.OperationCode = NEC_EJECT_CODE;
|
|
status = CdAudioNECSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
CdDump(( 3,
|
|
"CdAudioNECDeviceControl: invalidating cached TOC!\n"
|
|
));
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
|
|
CdDump(( 3, "CdAudioNECDeviceControl: Not Supported yet.\n" ));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
//
|
|
// Fall through and pass the request to the next driver.
|
|
//
|
|
|
|
default:
|
|
|
|
CdDump(( 5,
|
|
"CdAudioNECDeviceControl: Unsupported device IOCTL (0x%lX)\n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode
|
|
));
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
} // end switch( IOCTL )
|
|
|
|
SetStatusAndReturn:
|
|
|
|
//
|
|
// set status code and return
|
|
//
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
|
|
//
|
|
// If the status is verified required and the this request
|
|
// should bypass verify required then retry the request.
|
|
//
|
|
|
|
if (currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {
|
|
|
|
status = STATUS_IO_DEVICE_ERROR;
|
|
goto NECRestart;
|
|
|
|
}
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp,
|
|
deviceExtension->TargetDeviceObject
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioPioneerDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by CdAudioDeviceControl to handle
|
|
audio IOCTLs sent to PIONEER DRM-6xx cdrom drives.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCDROM_TOC cdaudioDataOut = Irp->AssociatedIrp.SystemBuffer;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PPNR_CDB cdb = (PPNR_CDB)srb.Cdb;
|
|
PCDB scsiCdb = (PCDB) srb.Cdb;
|
|
NTSTATUS status;
|
|
ULONG i,retry;
|
|
PUCHAR Toc;
|
|
|
|
PioneerRestart:
|
|
|
|
//
|
|
// Clear out cdb
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// What IOCTL do we need to execute?
|
|
//
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: IOCTL_CDROM_READ_TOC received.\n"
|
|
));
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
RtlZeroMemory( Toc, CDROM_TOC_SIZE );
|
|
|
|
//
|
|
// mount this disc (via START/STOP unit), which is
|
|
// necessary since we don't know which is the
|
|
// currently loaded disc.
|
|
//
|
|
|
|
if (deviceExtension->Active == CDAUDIO_PIONEER) {
|
|
cdb->PNR_START_STOP.Immediate = 1;
|
|
} else {
|
|
cdb->PNR_START_STOP.Immediate = 0;
|
|
}
|
|
|
|
cdb->PNR_START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
|
cdb->PNR_START_STOP.Start = 1;
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: Start Unit failed (0x%lx)\n",
|
|
status));
|
|
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
//
|
|
// Get first and last tracks
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
srb.CdbLength = 10;
|
|
cdb->PNR_READ_TOC.OperationCode = PIONEER_READ_TOC_CODE;
|
|
cdb->PNR_READ_TOC.AssignedLength[1] = PIONEER_TRANSFER_SIZE;
|
|
cdb->PNR_READ_TOC.Type = PIONEER_READ_FIRST_AND_LAST;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
PIONEER_TRANSFER_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: ReadTOC, First/Last Tracks failed (0x%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[0]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[1]);
|
|
|
|
//
|
|
// Now, get info about each track
|
|
//
|
|
|
|
for( i=0;
|
|
i<=( (ULONG)cdaudioDataOut->LastTrack -
|
|
(ULONG)cdaudioDataOut->FirstTrack );
|
|
i++
|
|
) {
|
|
|
|
//
|
|
// Get TOC info for this track
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
cdb->PNR_READ_TOC.OperationCode = PIONEER_READ_TOC_CODE;
|
|
cdb->PNR_READ_TOC.TrackNumber = (UCHAR)(DEC_TO_BCD((i+cdaudioDataOut->FirstTrack))); // track
|
|
cdb->PNR_READ_TOC.AssignedLength[1] = PIONEER_TRANSFER_SIZE;
|
|
cdb->PNR_READ_TOC.Type = PIONEER_READ_TRACK_INFO;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
PIONEER_TRANSFER_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: ReadTOC, Track #%d, failed (0x%lx)\n",
|
|
i+cdaudioDataOut->FirstTrack,
|
|
status ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = Toc[0];
|
|
cdaudioDataOut->TrackData[i].TrackNumber =
|
|
(UCHAR)((i + cdaudioDataOut->FirstTrack));
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0]=0;
|
|
cdaudioDataOut->TrackData[i].Address[1]=BCD_TO_DEC(Toc[1]);
|
|
cdaudioDataOut->TrackData[i].Address[2]=BCD_TO_DEC(Toc[2]);
|
|
cdaudioDataOut->TrackData[i].Address[3]=BCD_TO_DEC(Toc[3]);
|
|
|
|
}
|
|
|
|
//
|
|
// Get info for lead out track
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
cdb->PNR_READ_TOC.OperationCode = PIONEER_READ_TOC_CODE;
|
|
cdb->PNR_READ_TOC.AssignedLength[1] = PIONEER_TRANSFER_SIZE;
|
|
cdb->PNR_READ_TOC.Type = PIONEER_READ_LEAD_OUT_INFO;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
PIONEER_TRANSFER_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: ReadTOC, read LeadOutTrack failed (0x%lx)\n",
|
|
status ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = 0x10;
|
|
cdaudioDataOut->TrackData[i].TrackNumber = 0xaa;
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = BCD_TO_DEC(Toc[0]);
|
|
cdaudioDataOut->TrackData[i].Address[2] = BCD_TO_DEC(Toc[1]);
|
|
cdaudioDataOut->TrackData[i].Address[3] = BCD_TO_DEC(Toc[2]);
|
|
|
|
//
|
|
// Set size of information transfered to
|
|
// max size possible for CDROM Table of Contents
|
|
//
|
|
|
|
Irp->IoStatus.Information = CDROM_TOC_SIZE;
|
|
cdaudioDataOut->Length[0] = CDROM_TOC_SIZE >> 8;
|
|
cdaudioDataOut->Length[1] = CDROM_TOC_SIZE & 0xFF;
|
|
break;
|
|
|
|
case IOCTL_CDROM_STOP_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: IOCTL_CDROM_STOP_AUDIO received.\n"
|
|
));
|
|
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Same as scsi-2 spec, so just send to default driver
|
|
//
|
|
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_PLAY_AUDIO_MSF inputBuffer =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: IOCTL_CDROM_PLAY_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// First, set play END point for the play operation
|
|
//
|
|
|
|
retry = 5;
|
|
do {
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_SEEK_AUDIO.OperationCode = PIONEER_AUDIO_TRACK_SEARCH_CODE;
|
|
cdb->PNR_SEEK_AUDIO.Minute = DEC_TO_BCD(inputBuffer->StartingM);
|
|
cdb->PNR_SEEK_AUDIO.Second = DEC_TO_BCD(inputBuffer->StartingS);
|
|
cdb->PNR_SEEK_AUDIO.Frame = DEC_TO_BCD(inputBuffer->StartingF);
|
|
cdb->PNR_SEEK_AUDIO.Type = PIONEER_TYPE_ATIME;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
} while( !NT_SUCCESS(status) && ((retry--)>0) );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Now, set play start position and start playing.
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
retry = 5;
|
|
do {
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_PLAY_AUDIO.OperationCode = PIONEER_PLAY_AUDIO_CODE;
|
|
cdb->PNR_PLAY_AUDIO.StopAddr = 1;
|
|
cdb->PNR_PLAY_AUDIO.Minute = DEC_TO_BCD(inputBuffer->EndingM);
|
|
cdb->PNR_PLAY_AUDIO.Second = DEC_TO_BCD(inputBuffer->EndingS);
|
|
cdb->PNR_PLAY_AUDIO.Frame = DEC_TO_BCD(inputBuffer->EndingF);
|
|
cdb->PNR_PLAY_AUDIO.Type = PIONEER_TYPE_ATIME;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
} while( !NT_SUCCESS(status) && ((retry--)>0) );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Indicate the play actition is active.
|
|
//
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
|
|
|
|
}
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: PLAY_AUDIO_MSF(stop) failed 0x%lx\n",
|
|
status
|
|
));
|
|
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: cdb = 0x%lx, srb = 0x%lx\n",
|
|
cdb, &srb
|
|
));
|
|
|
|
if (CdAudioDebug>2)
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
}
|
|
#if DBG
|
|
else {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: PLAY_AUDIO_MSF(start) failed 0x%lx\n",
|
|
status
|
|
));
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: cdb = 0x%lx, srb = 0x%lx\n",
|
|
cdb, &srb
|
|
));
|
|
|
|
if (CdAudioDebug>2)
|
|
DbgBreakPoint();
|
|
|
|
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: IOCTL_CDROM_SEEK_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
|
|
retry = 5;
|
|
do {
|
|
|
|
//
|
|
// seek to MSF and enter pause (still) mode.
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_SEEK_AUDIO.OperationCode = PIONEER_AUDIO_TRACK_SEARCH_CODE;
|
|
cdb->PNR_SEEK_AUDIO.Minute = DEC_TO_BCD(inputBuffer->M);
|
|
cdb->PNR_SEEK_AUDIO.Second = DEC_TO_BCD(inputBuffer->S);
|
|
cdb->PNR_SEEK_AUDIO.Frame = DEC_TO_BCD(inputBuffer->F);
|
|
cdb->PNR_SEEK_AUDIO.Type = PIONEER_TYPE_ATIME;
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: Seek to MSF %d:%d:%d, BCD(%x:%x:%x)\n",
|
|
inputBuffer->M,
|
|
inputBuffer->S,
|
|
inputBuffer->F,
|
|
cdb->PNR_SEEK_AUDIO.Minute,
|
|
cdb->PNR_SEEK_AUDIO.Second,
|
|
cdb->PNR_SEEK_AUDIO.Frame
|
|
));
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: Seek to MSF, cdb is 0x%x, srb is 0x%x\n",
|
|
cdb,
|
|
&srb
|
|
));
|
|
#if DBG
|
|
if (CdAudioDebug>2) {
|
|
|
|
DbgBreakPoint();
|
|
|
|
}
|
|
#endif
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
} while( !NT_SUCCESS(status) && ((retry--)>0) );
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1, "CdAudioPioneerDeviceControl: Seek to MSF failed 0x%lx\n",
|
|
status
|
|
));
|
|
|
|
if (CdAudioDebug>5) {
|
|
|
|
DbgBreakPoint();
|
|
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: IOCTL_CDROM_PAUSE_AUDIO received.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_PAUSE_AUDIO.OperationCode = PIONEER_PAUSE_CODE;
|
|
cdb->PNR_PAUSE_AUDIO.Pause = 1;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_RESUME_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"CdAudioPioneerDeviceControl: IOCTL_CDROM_RESUME_AUDIO received.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// Resume Play
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_PAUSE_AUDIO.OperationCode = PIONEER_PAUSE_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
{
|
|
PSUB_Q_CURRENT_POSITION userPtr =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
PIONEER_Q_CHANNEL_TRANSFER_SIZE
|
|
);
|
|
|
|
CdDump(( 5,
|
|
"CdAudioPioneerDeviceControl: IOCTL_CDROM_READ_Q_CHANNEL received.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: READ_Q_CHANNEL, Illegal Format (%d)!\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
//
|
|
// Read audio play status
|
|
//
|
|
|
|
retry = 5;
|
|
do {
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_AUDIO_STATUS.OperationCode = PIONEER_AUDIO_STATUS_CODE;
|
|
cdb->PNR_AUDIO_STATUS.AssignedLength = PIONEER_AUDIO_STATUS_TRANSFER_SIZE; // Transfer Length
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
6,
|
|
FALSE
|
|
);
|
|
} while( !NT_SUCCESS(status) && ((retry--)>0) && status != STATUS_DEVICE_NOT_READY);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
userPtr->Header.Reserved = 0;
|
|
if (SubQPtr[0]==0x00)
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_IN_PROGRESS;
|
|
else if (SubQPtr[0]==0x01) {
|
|
deviceExtension->PlayActive = FALSE;
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PAUSED;
|
|
} else if (SubQPtr[0]==0x02) {
|
|
deviceExtension->PlayActive = FALSE;
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PAUSED;
|
|
} else if (SubQPtr[0]==0x03) {
|
|
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
} else {
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: read status code (Q) failed (0x%lx)\n",
|
|
status
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
ExFreePool( SubQPtr );
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up to read current position from Q Channel
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
retry = 5;
|
|
do {
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_READ_Q_CHANNEL.OperationCode = PIONEER_READ_SUB_Q_CHANNEL_CODE;
|
|
cdb->PNR_READ_Q_CHANNEL.AssignedLength = PIONEER_Q_CHANNEL_TRANSFER_SIZE; // Transfer Length
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
PIONEER_Q_CHANNEL_TRANSFER_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
} while( !NT_SUCCESS(status) && ((retry--)>0) );
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
userPtr->Header.DataLength[0] = 0;
|
|
userPtr->Header.DataLength[0] = 12;
|
|
|
|
userPtr->FormatCode = 0x01;
|
|
userPtr->Control = SubQPtr[0] & 0x0F;
|
|
userPtr->ADR = 0;
|
|
userPtr->TrackNumber = BCD_TO_DEC(SubQPtr[1]);
|
|
userPtr->IndexNumber = BCD_TO_DEC(SubQPtr[2]);
|
|
userPtr->AbsoluteAddress[0] = 0;
|
|
userPtr->AbsoluteAddress[1] = BCD_TO_DEC((SubQPtr[6]));
|
|
userPtr->AbsoluteAddress[2] = BCD_TO_DEC((SubQPtr[7]));
|
|
userPtr->AbsoluteAddress[3] = BCD_TO_DEC((SubQPtr[8]));
|
|
userPtr->TrackRelativeAddress[0] = 0;
|
|
userPtr->TrackRelativeAddress[1] = BCD_TO_DEC((SubQPtr[3]));
|
|
userPtr->TrackRelativeAddress[2] = BCD_TO_DEC((SubQPtr[4]));
|
|
userPtr->TrackRelativeAddress[3] = BCD_TO_DEC((SubQPtr[5]));
|
|
Irp->IoStatus.Information = 16;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
CdDump(( 1,
|
|
"CdAudioPioneerDeviceControl: read q channel failed (0x%lx)\n",
|
|
status
|
|
));
|
|
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
CdDump(( 3, "CdAudioPioneerDeviceControl: "
|
|
"IOCTL_CDROM_EJECT_MEDIA received. "));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Build cdb to eject cartridge
|
|
//
|
|
|
|
if (deviceExtension->Active == CDAUDIO_PIONEER) {
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PNR_EJECT.OperationCode = PIONEER_EJECT_CODE;
|
|
cdb->PNR_EJECT.Immediate = 1;
|
|
} else {
|
|
srb.CdbLength = 6;
|
|
|
|
scsiCdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
|
|
scsiCdb->START_STOP.LoadEject = 1;
|
|
scsiCdb->START_STOP.Start = 0;
|
|
}
|
|
|
|
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
CdDump(( 1, "CdAudioPioneerDeviceControl: "
|
|
"IOCTL_CDROM_EJECT_MEDIA returned %lx.\n",
|
|
status
|
|
));
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
|
|
CdDump(( 3, "CdAudioPioneerDeviceControl: Not Supported yet.\n" ));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((5,"CdAudioPioneerDeviceControl: Unsupported device IOCTL\n"));
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
|
|
|
|
} // end switch( IOCTL )
|
|
|
|
SetStatusAndReturn:
|
|
//
|
|
// set status code and return
|
|
//
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
//
|
|
// If the status is verified required and the this request
|
|
// should bypass verify required then retry the request.
|
|
//
|
|
|
|
if (currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {
|
|
|
|
status = STATUS_IO_DEVICE_ERROR;
|
|
goto PioneerRestart;
|
|
|
|
}
|
|
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp,
|
|
deviceExtension->TargetDeviceObject
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioDenonDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by CdAudioDeviceControl to handle
|
|
audio IOCTLs sent to DENON DRD-253 cdrom drive.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCDROM_TOC cdaudioDataOut = Irp->AssociatedIrp.SystemBuffer;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb = (PCDB)srb.Cdb;
|
|
NTSTATUS status;
|
|
ULONG i,bytesTransfered;
|
|
PUCHAR Toc;
|
|
|
|
DenonRestart:
|
|
|
|
//
|
|
// Clear out cdb
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// What IOCTL do we need to execute?
|
|
//
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_GET_LAST_SESSION:
|
|
|
|
//
|
|
// Multiple sessions are not supported.
|
|
//
|
|
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_READ_TOC received.\n"
|
|
));
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
|
|
RtlZeroMemory( Toc, CDROM_TOC_SIZE );
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
cdb->CDB6GENERIC.OperationCode = DENON_READ_TOC_CODE;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
srb.CdbLength = 6;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioDenonDeviceControl: READ_TOC error (0x%08lX)\n",
|
|
status ));
|
|
|
|
if (SRB_STATUS(srb.SrbStatus)!=SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "CdAudioDenonDeviceControl: SRB ERROR (0x%lX)\n",
|
|
srb.SrbStatus ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Set the status to success since data under runs are not an error.
|
|
//
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Since the Denon manual didn't define the format of
|
|
// the buffer returned from this call, here it is:
|
|
//
|
|
// Byte Data (4 byte "packets")
|
|
//
|
|
// 00 a0 FT 00 00 (FT == BCD of first track number on disc)
|
|
// 04 a1 LT 00 00 (LT == BCD of last track number on disc)
|
|
// 08 a2 MM SS FF (MM SS FF == BCD of total disc time, in MSF)
|
|
//
|
|
// For each track on disc:
|
|
//
|
|
// 0C XX MM SS FF (MM SS FF == BCD MSF start position of track XX)
|
|
// 0C+4 XX+1 MM SS FF (MM SS FF == BCD MSF start position of track XX+1)
|
|
//
|
|
// etc., for each track
|
|
//
|
|
|
|
//
|
|
// Translate data into our format
|
|
//
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.Read.Length > srb.DataTransferLength ?
|
|
srb.DataTransferLength : currentIrpStack->Parameters.Read.Length;
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(bytesTransfered >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(bytesTransfered & 0xFF);
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[1]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[5]);
|
|
|
|
for( i=0;
|
|
i<=( (ULONG)cdaudioDataOut->LastTrack -
|
|
(ULONG)cdaudioDataOut->FirstTrack );
|
|
i++
|
|
) {
|
|
|
|
//
|
|
// Grab Information for each track
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = Toc[(i*4)+12];
|
|
cdaudioDataOut->TrackData[i].TrackNumber =
|
|
(UCHAR)((i + cdaudioDataOut->FirstTrack));
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] =
|
|
BCD_TO_DEC((Toc[(i*4)+13]));
|
|
cdaudioDataOut->TrackData[i].Address[2] =
|
|
BCD_TO_DEC((Toc[(i*4)+14]));
|
|
cdaudioDataOut->TrackData[i].Address[3] =
|
|
BCD_TO_DEC((Toc[(i*4)+15]));
|
|
|
|
}
|
|
|
|
//
|
|
// Fake "lead out track" info
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = 0;
|
|
cdaudioDataOut->TrackData[i].TrackNumber = 0xaa;
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = BCD_TO_DEC(Toc[9]);
|
|
cdaudioDataOut->TrackData[i].Address[2] = BCD_TO_DEC(Toc[10]);
|
|
cdaudioDataOut->TrackData[i].Address[3] = BCD_TO_DEC(Toc[11]);
|
|
|
|
//
|
|
// Clear out deviceExtension data
|
|
//
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( Toc );
|
|
break;
|
|
|
|
case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
case IOCTL_CDROM_STOP_AUDIO:
|
|
{
|
|
|
|
PCDROM_PLAY_AUDIO_MSF inputBuffer =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB6GENERIC.OperationCode = DENON_STOP_AUDIO_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: STOP failed (0x%08lX)\n",
|
|
status ));
|
|
|
|
}
|
|
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_CDROM_STOP_AUDIO
|
|
) {
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_STOP_AUDIO received.\n"
|
|
));
|
|
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_PLAY_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = DENON_PLAY_AUDIO_EXTENDED_CODE;
|
|
cdb->CDB10.LogicalBlockByte0 = DEC_TO_BCD(inputBuffer->StartingM);
|
|
cdb->CDB10.LogicalBlockByte1 = DEC_TO_BCD(inputBuffer->StartingS);
|
|
cdb->CDB10.LogicalBlockByte2 = DEC_TO_BCD(inputBuffer->StartingF);
|
|
cdb->CDB10.LogicalBlockByte3 = DEC_TO_BCD(inputBuffer->EndingM);
|
|
cdb->CDB10.Reserved2 = DEC_TO_BCD(inputBuffer->EndingS);
|
|
cdb->CDB10.TransferBlocksMsb = DEC_TO_BCD(inputBuffer->EndingF);
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Indicate the play actition is active.
|
|
//
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
//
|
|
// Set last play ending address for next pause command
|
|
//
|
|
|
|
deviceExtension->LastEndM = DEC_TO_BCD(inputBuffer->EndingM);
|
|
deviceExtension->LastEndS = DEC_TO_BCD(inputBuffer->EndingS);
|
|
deviceExtension->LastEndF = DEC_TO_BCD(inputBuffer->EndingF);
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: PLAY ==> BcdLastEnd set to (%x %x %x)\n",
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF ));
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: PLAY failed (0x%08lX)\n",
|
|
status ));
|
|
|
|
//
|
|
// The Denon drive returns STATUS_INVALD_DEVICE_REQUEST
|
|
// when we ask to play an invalid address, so we need
|
|
// to map to STATUS_NONEXISTENT_SECTOR in order to be
|
|
// consistent with the other drives.
|
|
//
|
|
|
|
if (status==STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
status = STATUS_NONEXISTENT_SECTOR;
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_SEEK_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = DENON_PLAY_AUDIO_EXTENDED_CODE;
|
|
cdb->CDB10.LogicalBlockByte0 = DEC_TO_BCD(inputBuffer->M);
|
|
cdb->CDB10.LogicalBlockByte1 = DEC_TO_BCD(inputBuffer->S);
|
|
cdb->CDB10.LogicalBlockByte2 = DEC_TO_BCD(inputBuffer->F);
|
|
cdb->CDB10.LogicalBlockByte3 = DEC_TO_BCD(inputBuffer->M);
|
|
cdb->CDB10.Reserved2 = DEC_TO_BCD(inputBuffer->S);
|
|
cdb->CDB10.TransferBlocksMsb = DEC_TO_BCD(inputBuffer->F);
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Paused = CDAUDIO_PAUSED;
|
|
deviceExtension->PausedM = DEC_TO_BCD(inputBuffer->M);
|
|
deviceExtension->PausedS = DEC_TO_BCD(inputBuffer->S);
|
|
deviceExtension->PausedF = DEC_TO_BCD(inputBuffer->F);
|
|
deviceExtension->LastEndM = DEC_TO_BCD(inputBuffer->M);
|
|
deviceExtension->LastEndS = DEC_TO_BCD(inputBuffer->S);
|
|
deviceExtension->LastEndF = DEC_TO_BCD(inputBuffer->F);
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: SEEK, Paused (%x %x %x) LastEnd (%x %x %x)\n",
|
|
deviceExtension->PausedM,
|
|
deviceExtension->PausedS,
|
|
deviceExtension->PausedF,
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF ));
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: SEEK failed (0x%08lX)\n",
|
|
status ));
|
|
|
|
//
|
|
// The Denon drive returns STATUS_INVALD_DEVICE_REQUEST
|
|
// when we ask to play an invalid address, so we need
|
|
// to map to STATUS_NONEXISTENT_SECTOR in order to be
|
|
// consistent with the other drives.
|
|
//
|
|
|
|
if (status==STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
status = STATUS_NONEXISTENT_SECTOR;
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO:
|
|
{
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
10
|
|
);
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_PAUSE_AUDIO received.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_PAUSED) {
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: PAUSE: Already Paused!\n"
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
status = STATUS_SUCCESS;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Since the Denon doesn't have a pause mode,
|
|
// we'll just record the current position and
|
|
// stop the drive.
|
|
//
|
|
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB6GENERIC.OperationCode = DENON_READ_SUB_Q_CHANNEL_CODE;
|
|
cdb->CDB6GENERIC.CommandUniqueBytes[2] = 10; // Transfer Length
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
10,
|
|
FALSE
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioDenonDeviceControl: Pause, Read Q Channel failed (0x%lx)\n",
|
|
status ));
|
|
ExFreePool( SubQPtr );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
deviceExtension->PausedM = SubQPtr[7];
|
|
deviceExtension->PausedS = SubQPtr[8];
|
|
deviceExtension->PausedF = SubQPtr[9];
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB6GENERIC.OperationCode = DENON_STOP_AUDIO_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioDenonDeviceControl: PAUSE, StopAudio failed! (0x%08lX)\n",
|
|
status ));
|
|
ExFreePool( SubQPtr );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
deviceExtension->Paused = CDAUDIO_PAUSED;
|
|
deviceExtension->PausedM = SubQPtr[7];
|
|
deviceExtension->PausedS = SubQPtr[8];
|
|
deviceExtension->PausedF = SubQPtr[9];
|
|
|
|
CdDump((3,
|
|
"CdAudioDenonDeviceControl: PAUSE ==> Paused set to (%x %x %x)\n",
|
|
deviceExtension->PausedM,
|
|
deviceExtension->PausedS,
|
|
deviceExtension->PausedF ));
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_RESUME_AUDIO:
|
|
|
|
//
|
|
// Resume cdrom
|
|
//
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_RESUME_AUDIO received.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// Since the Denon doesn't have a resume IOCTL,
|
|
// we'll just start playing (if paused) from the
|
|
// last recored paused position to the last recorded
|
|
// "end of play" position.
|
|
//
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_NOT_PAUSED) {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = DENON_PLAY_AUDIO_EXTENDED_CODE;
|
|
cdb->CDB10.LogicalBlockByte0 = deviceExtension->PausedM;
|
|
cdb->CDB10.LogicalBlockByte1 = deviceExtension->PausedS;
|
|
cdb->CDB10.LogicalBlockByte2 = deviceExtension->PausedF;
|
|
cdb->CDB10.LogicalBlockByte3 = deviceExtension->LastEndM;
|
|
cdb->CDB10.Reserved2 = deviceExtension->LastEndS;
|
|
cdb->CDB10.TransferBlocksMsb = deviceExtension->LastEndF;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
} else {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioDenonDeviceControl: RESUME (%x %x %x) - (%x %x %x) failed (0x%08lX)\n",
|
|
deviceExtension->PausedM,
|
|
deviceExtension->PausedS,
|
|
deviceExtension->PausedF,
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF,
|
|
status ));
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
{
|
|
PSUB_Q_CURRENT_POSITION userPtr =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
sizeof(SUB_Q_CHANNEL_DATA)
|
|
);
|
|
|
|
CdDump(( 5,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_READ_Q_CHANNEL received.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioDenonDeviceControl: READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioDenonDeviceControl: READ_Q_CHANNEL, illegal Format (%d)\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
//
|
|
// Read audio play status
|
|
//
|
|
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB6GENERIC.OperationCode = DENON_READ_SUB_Q_CHANNEL_CODE;
|
|
cdb->CDB6GENERIC.CommandUniqueBytes[2] = 10; // Transfer Length
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
10,
|
|
FALSE
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
userPtr->Header.Reserved = 0;
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_PAUSED) {
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PAUSED;
|
|
|
|
} else {
|
|
|
|
if (SubQPtr[0]==0x01)
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_IN_PROGRESS;
|
|
else if (SubQPtr[0]==0x00){
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
} else {
|
|
deviceExtension->PlayActive = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
userPtr->Header.DataLength[0] = 0;
|
|
userPtr->Header.DataLength[0] = 12;
|
|
|
|
userPtr->FormatCode = 0x01;
|
|
userPtr->Control = SubQPtr[1];
|
|
userPtr->ADR = 0;
|
|
userPtr->TrackNumber = BCD_TO_DEC(SubQPtr[2]);
|
|
userPtr->IndexNumber = BCD_TO_DEC(SubQPtr[3]);
|
|
userPtr->AbsoluteAddress[0] = 0;
|
|
userPtr->AbsoluteAddress[1] = BCD_TO_DEC((SubQPtr[7]));
|
|
userPtr->AbsoluteAddress[2] = BCD_TO_DEC((SubQPtr[8]));
|
|
userPtr->AbsoluteAddress[3] = BCD_TO_DEC((SubQPtr[9]));
|
|
userPtr->TrackRelativeAddress[0] = 0;
|
|
userPtr->TrackRelativeAddress[1] = BCD_TO_DEC((SubQPtr[4]));
|
|
userPtr->TrackRelativeAddress[2] = BCD_TO_DEC((SubQPtr[5]));
|
|
userPtr->TrackRelativeAddress[3] = BCD_TO_DEC((SubQPtr[6]));
|
|
Irp->IoStatus.Information = 16;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
CdDump(( 1,
|
|
"CdAudioDenonDeviceControl: READ_Q_CHANNEL failed (0x%08lX)\n",
|
|
status
|
|
));
|
|
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
//
|
|
// Build cdb to eject cartridge
|
|
//
|
|
|
|
CdDump(( 3,
|
|
"CdAudioDenonDeviceControl: IOCTL_CDROM_EJECT_MEDIA received.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
srb.CdbLength = 6;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB6GENERIC.OperationCode = DENON_EJECT_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
CdDump(( 3, "CdAudioDenonDeviceControl: Not Supported yet.\n" ));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((5,"CdAudioDenonDeviceControl: Unsupported device IOCTL\n"));
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
} // end switch( IOCTL )
|
|
|
|
SetStatusAndReturn:
|
|
//
|
|
// set status code and return
|
|
//
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
//
|
|
// If the status is verified required and the this request
|
|
// should bypass verify required then retry the request.
|
|
//
|
|
|
|
if (currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {
|
|
|
|
status = STATUS_IO_DEVICE_ERROR;
|
|
goto DenonRestart;
|
|
|
|
}
|
|
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp,
|
|
deviceExtension->TargetDeviceObject
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioHitachiSendPauseCommand(
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sends a PAUSE cdb to the Hitachi drive. The Hitachi
|
|
drive returns a "busy" condition whenever a play audio command is in
|
|
progress...so we need to bump the drive out of audio play to issue
|
|
a new command. This routine is in place for this purpose.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB12 cdb = (PCDB12)srb.Cdb;
|
|
NTSTATUS status;
|
|
PUCHAR PausePos;
|
|
|
|
//
|
|
// Allocate buffer for pause data
|
|
//
|
|
|
|
PausePos = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned, 3 );
|
|
|
|
if (PausePos==NULL) {
|
|
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
}
|
|
|
|
RtlZeroMemory( PausePos, 3 );
|
|
|
|
//
|
|
// Clear out cdb
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// Clear audio play command so that next command will be issued
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PAUSE_AUDIO.OperationCode = HITACHI_PAUSE_AUDIO_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
PausePos,
|
|
3,
|
|
FALSE
|
|
);
|
|
|
|
ExFreePool( PausePos );
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioHitachiDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by CdAudioDeviceControl to handle
|
|
audio IOCTLs sent to Hitachi cdrom drives.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCDROM_TOC cdaudioDataOut = Irp->AssociatedIrp.SystemBuffer;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB12 cdb = (PCDB12)srb.Cdb;
|
|
NTSTATUS status;
|
|
ULONG i,bytesTransfered;
|
|
PUCHAR Toc;
|
|
|
|
HitachiRestart:
|
|
|
|
//
|
|
// Clear out cdb
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// What IOCTL do we need to execute?
|
|
//
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: IOCTL_CDROM_READ_TOC received.\n"
|
|
));
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
|
|
RtlZeroMemory( Toc, CDROM_TOC_SIZE );
|
|
srb.CdbLength = 12;
|
|
|
|
//
|
|
// Fill in CDB
|
|
//
|
|
|
|
if (deviceExtension->Active == CDAUDIO_FUJITSU) {
|
|
cdb->READ_DISC_INFO.OperationCode = FUJITSU_READ_TOC_CODE;
|
|
} else {
|
|
cdb->READ_DISC_INFO.OperationCode = HITACHI_READ_TOC_CODE;
|
|
}
|
|
cdb->READ_DISC_INFO.AllocationLength[0] = CDROM_TOC_SIZE >> 8;
|
|
cdb->READ_DISC_INFO.AllocationLength[1] = CDROM_TOC_SIZE & 0xFF;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioHitachiDeviceControl: READ_TOC error (0x%08lX)\n",
|
|
status ));
|
|
|
|
|
|
if (SRB_STATUS(srb.SrbStatus)!=SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "CdAudioHitachiDeviceControl: SRB ERROR (0x%lX)\n",
|
|
srb.SrbStatus ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// added for cdrom101 and 102 to correspondence
|
|
//
|
|
|
|
if( deviceExtension->Active == CDAUDIO_HITACHI ) {
|
|
|
|
//
|
|
// Translate data into our format
|
|
//
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.Read.Length > sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) : currentIrpStack->Parameters.Read.Length;
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(bytesTransfered >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(bytesTransfered & 0xFF);
|
|
cdaudioDataOut->FirstTrack = Toc[2];
|
|
cdaudioDataOut->LastTrack = Toc[3];
|
|
|
|
for( i=0;
|
|
i<=( (ULONG)cdaudioDataOut->LastTrack -
|
|
(ULONG)cdaudioDataOut->FirstTrack );
|
|
i++
|
|
) {
|
|
|
|
//
|
|
// Grab Information for each track
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control =
|
|
((Toc[(i*4)+8] & 0x0F) << 4) | (Toc[(i*4)+8] >> 4);
|
|
cdaudioDataOut->TrackData[i].TrackNumber =
|
|
(UCHAR)((i + cdaudioDataOut->FirstTrack));
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[(i*4)+9];
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[(i*4)+10];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[(i*4)+11];
|
|
|
|
}
|
|
|
|
//
|
|
// Fake "lead out track" info
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = 0x10;
|
|
cdaudioDataOut->TrackData[i].TrackNumber = 0xaa;
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[5];
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[6];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[7];
|
|
|
|
//
|
|
// Clear out device extension data
|
|
//
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
} else {
|
|
|
|
//
|
|
// added for cdrom101 and 102 to correspondence
|
|
//
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.Read.Length > sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) : currentIrpStack->Parameters.Read.Length;
|
|
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
cdaudioDataOut->Length[0] = (UCHAR)(bytesTransfered >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(bytesTransfered & 0xFF);
|
|
cdaudioDataOut->FirstTrack = Toc[1];
|
|
cdaudioDataOut->LastTrack = Toc[2];
|
|
|
|
for( i=0 ;i<=( (ULONG)cdaudioDataOut->LastTrack -
|
|
(ULONG)cdaudioDataOut->FirstTrack ); i++ ) {
|
|
|
|
//
|
|
// Grab Information for each track
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
|
|
if(Toc[(i*3)+6] & 0x80)
|
|
cdaudioDataOut->TrackData[i].Control = 0x04;
|
|
else
|
|
cdaudioDataOut->TrackData[i].Control = 0;
|
|
|
|
cdaudioDataOut->TrackData[i].Adr = 0;
|
|
|
|
cdaudioDataOut->TrackData[i].TrackNumber =
|
|
(UCHAR)((i + cdaudioDataOut->FirstTrack));
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[(i*3)+6] & 0x7f;
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[(i*3)+7];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[(i*3)+8];
|
|
|
|
}
|
|
|
|
//
|
|
// Fake "lead out track" info
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = 0x10;
|
|
cdaudioDataOut->TrackData[i].TrackNumber = 0xaa;
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[3];
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[4];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[5];
|
|
|
|
}
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( Toc );
|
|
break;
|
|
|
|
case IOCTL_CDROM_STOP_AUDIO:
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Kill any current play operation
|
|
//
|
|
|
|
CdAudioHitachiSendPauseCommand( DeviceObject );
|
|
|
|
//
|
|
// Same as scsi-2 spec, so just send to default driver
|
|
//
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_PLAY_AUDIO_MSF inputBuffer =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: IOCTL_CDROM_PLAY_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// Kill any current play operation
|
|
//
|
|
|
|
CdAudioHitachiSendPauseCommand( DeviceObject );
|
|
|
|
//
|
|
// Fill in CDB for PLAY operation
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PLAY_AUDIO.OperationCode = HITACHI_PLAY_AUDIO_MSF_CODE;
|
|
cdb->PLAY_AUDIO.Immediate = 1;
|
|
cdb->PLAY_AUDIO.StartingM = inputBuffer->StartingM;
|
|
cdb->PLAY_AUDIO.StartingS = inputBuffer->StartingS;
|
|
cdb->PLAY_AUDIO.StartingF = inputBuffer->StartingF;
|
|
cdb->PLAY_AUDIO.EndingM = inputBuffer->EndingM;
|
|
cdb->PLAY_AUDIO.EndingS = inputBuffer->EndingS;
|
|
cdb->PLAY_AUDIO.EndingF = inputBuffer->EndingF;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
//
|
|
// Indicate the play actition is active.
|
|
//
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
|
|
//
|
|
// Set last play ending address for next pause command
|
|
//
|
|
|
|
deviceExtension->PausedM = inputBuffer->StartingM;
|
|
deviceExtension->PausedS = inputBuffer->StartingS;
|
|
deviceExtension->PausedF = inputBuffer->StartingF;
|
|
deviceExtension->LastEndM = inputBuffer->EndingM;
|
|
deviceExtension->LastEndS = inputBuffer->EndingS;
|
|
deviceExtension->LastEndF = inputBuffer->EndingF;
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: PLAY failed (0x%08lX)\n",
|
|
status ));
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: IOCTL_CDROM_SEEK_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// Kill any current play operation
|
|
//
|
|
|
|
CdAudioHitachiSendPauseCommand( DeviceObject );
|
|
|
|
//
|
|
// seek to MSF and enter pause (still) mode.
|
|
//
|
|
|
|
//
|
|
// Fill in CDB for PLAY operation
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PLAY_AUDIO.OperationCode = HITACHI_PLAY_AUDIO_MSF_CODE;
|
|
cdb->PLAY_AUDIO.Immediate = 1;
|
|
cdb->PLAY_AUDIO.StartingM = inputBuffer->M;
|
|
cdb->PLAY_AUDIO.StartingS = inputBuffer->S;
|
|
cdb->PLAY_AUDIO.StartingF = inputBuffer->F;
|
|
cdb->PLAY_AUDIO.EndingM = inputBuffer->M;
|
|
cdb->PLAY_AUDIO.EndingS = inputBuffer->S;
|
|
cdb->PLAY_AUDIO.EndingF = inputBuffer->F;
|
|
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->PausedM = inputBuffer->M;
|
|
deviceExtension->PausedS = inputBuffer->S;
|
|
deviceExtension->PausedF = inputBuffer->F;
|
|
deviceExtension->LastEndM = inputBuffer->M;
|
|
deviceExtension->LastEndS = inputBuffer->S;
|
|
deviceExtension->LastEndF = inputBuffer->F;
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: SEEK failed (0x%08lX)\n",
|
|
status ));
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO:
|
|
{
|
|
|
|
PUCHAR PausePos = ExAllocatePool( NonPagedPoolCacheAligned, 3 );
|
|
if (PausePos==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
RtlZeroMemory( PausePos, 3 );
|
|
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: IOCTL_CDROM_PAUSE_AUDIO received.\n"
|
|
));
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PAUSE_AUDIO.OperationCode = HITACHI_PAUSE_AUDIO_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
PausePos,
|
|
3,
|
|
FALSE
|
|
);
|
|
|
|
deviceExtension->Paused = CDAUDIO_PAUSED;
|
|
deviceExtension->PausedM = PausePos[0];
|
|
deviceExtension->PausedS = PausePos[1];
|
|
deviceExtension->PausedF = PausePos[2];
|
|
|
|
ExFreePool( PausePos );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_RESUME_AUDIO:
|
|
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: IOCTL_CDROM_RESUME_AUDIO received.\n"
|
|
));
|
|
|
|
//
|
|
// Kill any current play operation
|
|
//
|
|
|
|
CdAudioHitachiSendPauseCommand( DeviceObject );
|
|
|
|
//
|
|
// Resume play
|
|
//
|
|
|
|
//
|
|
// Fill in CDB for PLAY operation
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PLAY_AUDIO.OperationCode = HITACHI_PLAY_AUDIO_MSF_CODE;
|
|
cdb->PLAY_AUDIO.Immediate = 1;
|
|
cdb->PLAY_AUDIO.StartingM = deviceExtension->PausedM;
|
|
cdb->PLAY_AUDIO.StartingS = deviceExtension->PausedS;
|
|
cdb->PLAY_AUDIO.StartingF = deviceExtension->PausedF;
|
|
cdb->PLAY_AUDIO.EndingM = deviceExtension->LastEndM;
|
|
cdb->PLAY_AUDIO.EndingS = deviceExtension->LastEndS;
|
|
cdb->PLAY_AUDIO.EndingF = deviceExtension->LastEndF;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
{
|
|
|
|
PSUB_Q_CURRENT_POSITION userPtr =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
sizeof(SUB_Q_CHANNEL_DATA)
|
|
);
|
|
|
|
CdDump(( 5,
|
|
"CdAudioHitachiDeviceControl: IOCTL_CDROM_READ_Q_CHANNEL received.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioHitachiDeviceControl: READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioHitachiDeviceControl: READ_Q_CHANNEL, illegal Format (%d)\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
//
|
|
// Set up to read Q Channel
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->AUDIO_STATUS.OperationCode = HITACHI_READ_SUB_Q_CHANNEL_CODE;
|
|
|
|
Retry:
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CHANNEL_DATA),
|
|
FALSE
|
|
);
|
|
if ((NT_SUCCESS(status)) || (status==STATUS_DATA_OVERRUN)) {
|
|
|
|
//
|
|
// While playing one track of Japanese music CDs on "CD player",
|
|
// track number is incremented unexpectedly.
|
|
// Some of SubQ data in Japanese music cd does not contain current position
|
|
// information. This is distinguished by lower 4 bits of SubQPtr[1]. If this
|
|
// data is needless, retry READ_SUB_Q_CHANNEL_CODE command until required
|
|
// information will be got.
|
|
//
|
|
|
|
if((SubQPtr[1] & 0x0F) != 1)
|
|
goto Retry;
|
|
|
|
userPtr->Header.Reserved = 0;
|
|
if (deviceExtension->Paused == CDAUDIO_PAUSED) {
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PAUSED;
|
|
} else {
|
|
if (SubQPtr[0]==0x01)
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_IN_PROGRESS;
|
|
else if (SubQPtr[0]==0x00){
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
} else {
|
|
deviceExtension->PlayActive = FALSE;
|
|
}
|
|
}
|
|
userPtr->Header.DataLength[0] = 0;
|
|
userPtr->Header.DataLength[0] = 12;
|
|
|
|
userPtr->FormatCode = 0x01;
|
|
userPtr->Control = ((SubQPtr[1] & 0xF0) >> 4);
|
|
userPtr->ADR = SubQPtr[1] & 0x0F;
|
|
userPtr->TrackNumber = SubQPtr[2];
|
|
userPtr->IndexNumber = SubQPtr[3];
|
|
userPtr->AbsoluteAddress[0] = 0;
|
|
userPtr->AbsoluteAddress[1] = SubQPtr[8];
|
|
userPtr->AbsoluteAddress[2] = SubQPtr[9];
|
|
userPtr->AbsoluteAddress[3] = SubQPtr[10];
|
|
userPtr->TrackRelativeAddress[0] = 0;
|
|
userPtr->TrackRelativeAddress[1] = SubQPtr[4];
|
|
userPtr->TrackRelativeAddress[2] = SubQPtr[5];
|
|
userPtr->TrackRelativeAddress[3] = SubQPtr[6];
|
|
Irp->IoStatus.Information = 16;
|
|
status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
CdDump(( 1,
|
|
"CdAudioHitachiDeviceControl: READ_Q_CHANNEL failed (0x%08lX)\n",
|
|
status
|
|
));
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
{
|
|
|
|
PUCHAR EjectStatus = ExAllocatePool( NonPagedPoolCacheAligned, 1 );
|
|
|
|
if (EjectStatus==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
CdDump(( 3,
|
|
"CdAudioHitachiDeviceControl: IOCTL_CDROM_EJECT_MEDIA received.\n"
|
|
));
|
|
|
|
//
|
|
// Set up to EJECT disc
|
|
//
|
|
|
|
srb.CdbLength = 12;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->EJECT.OperationCode = HITACHI_EJECT_CODE;
|
|
cdb->EJECT.Eject = 1; // Set Eject flag
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
EjectStatus,
|
|
1,
|
|
FALSE
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
}
|
|
|
|
ExFreePool( EjectStatus );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
|
|
CdDump(( 3, "CdAudioHitachieviceControl: Not Supported yet.\n" ));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((5,"CdAudioHitachiDeviceControl: Unsupported device IOCTL\n"));
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
} // end switch( IOCTL )
|
|
|
|
SetStatusAndReturn:
|
|
|
|
//
|
|
// set status code and return
|
|
//
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
//
|
|
// If the status is verified required and the this request
|
|
// should bypass verify required then retry the request.
|
|
//
|
|
|
|
if (currentIrpStack->Flags & SL_OVERRIDE_VERIFY_VOLUME) {
|
|
|
|
status = STATUS_IO_DEVICE_ERROR;
|
|
goto HitachiRestart;
|
|
|
|
}
|
|
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp,
|
|
deviceExtension->TargetDeviceObject
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudio535DeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by CdAudioDeviceControl to handle
|
|
audio IOCTLs sent to Chinon CDS-535 cdrom drive.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCDROM_TOC cdaudioDataOut = Irp->AssociatedIrp.SystemBuffer;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PREAD_CAPACITY_DATA lastSession;
|
|
PCDB cdb = (PCDB)srb.Cdb;
|
|
NTSTATUS status;
|
|
ULONG i,bytesTransfered;
|
|
PUCHAR Toc;
|
|
ULONG destblock;
|
|
|
|
|
|
//
|
|
// Clear out cdb
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// What IOCTL do we need to execute?
|
|
//
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_GET_LAST_SESSION:
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
if (currentIrpStack->Parameters.Read.Length <
|
|
(ULONG)(FIELD_OFFSET(CDROM_TOC, TrackData[1]))) {
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
break;
|
|
|
|
}
|
|
|
|
// Allocate storage to hold lastSession from disc
|
|
//
|
|
|
|
lastSession = ExAllocatePool( NonPagedPoolCacheAligned,
|
|
sizeof(READ_CAPACITY_DATA)
|
|
);
|
|
|
|
if (lastSession==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
|
|
RtlZeroMemory( lastSession, sizeof(READ_CAPACITY_DATA));
|
|
srb.CdbLength = 10;
|
|
|
|
//
|
|
// Fill in CDB
|
|
//
|
|
|
|
cdb->CDB10.OperationCode = CDS535_GET_LAST_SESSION;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = ScsiClassSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
lastSession,
|
|
sizeof(READ_CAPACITY_DATA),
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio535DeviceControl: READ_TOC error (0x%08lX)\n",
|
|
status ));
|
|
|
|
|
|
ExFreePool( lastSession );
|
|
goto SetStatusAndReturn;
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into our format.
|
|
//
|
|
|
|
bytesTransfered = FIELD_OFFSET(CDROM_TOC, TrackData[1]);
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
|
|
RtlZeroMemory(cdaudioDataOut, bytesTransfered);
|
|
|
|
cdaudioDataOut->Length[0] = (UCHAR)(bytesTransfered >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(bytesTransfered & 0xFF);
|
|
|
|
//
|
|
// Determine if this is a multisession cd.
|
|
//
|
|
|
|
if (lastSession->LogicalBlockAddress == 0) {
|
|
|
|
//
|
|
// This is a single session disk. Just return.
|
|
//
|
|
|
|
ExFreePool(lastSession);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Fake the session information.
|
|
//
|
|
|
|
cdaudioDataOut->FirstTrack = 1;
|
|
cdaudioDataOut->LastTrack = 2;
|
|
|
|
CdDump(( 4,
|
|
"CdAudio535DeviceControl: Tracks %d - %d, (0x%lX bytes)\n",
|
|
cdaudioDataOut->FirstTrack,
|
|
cdaudioDataOut->LastTrack,
|
|
bytesTransfered
|
|
));
|
|
|
|
|
|
//
|
|
// Grab Information for the last session.
|
|
//
|
|
|
|
*((ULONG *)&cdaudioDataOut->TrackData[0].Address[0]) =
|
|
lastSession->LogicalBlockAddress;
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( lastSession );
|
|
break;
|
|
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
CdDump(( 3,
|
|
"CdAudio535DeviceControl: IOCTL_CDROM_READ_TOC received.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
|
|
RtlZeroMemory( Toc, CDROM_TOC_SIZE );
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
cdb->CDB10.OperationCode = CDS535_READ_TOC_CODE;
|
|
cdb->CDB10.Reserved1 = 1; // MSF mode
|
|
cdb->CDB10.TransferBlocksMsb = (CDROM_TOC_SIZE >> 8);
|
|
cdb->CDB10.TransferBlocksLsb = (CDROM_TOC_SIZE & 0xFF);
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
srb.CdbLength = 10;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio535DeviceControl: READ_TOC error (0x%08lX)\n",
|
|
status ));
|
|
|
|
if (SRB_STATUS(srb.SrbStatus)!=SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "CdAudio535DeviceControl: SRB ERROR (0x%lX)\n",
|
|
srb.SrbStatus ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into SCSI-II format
|
|
// (track numbers, except 0xAA, must be converted from BCD)
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.Read.Length > sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) : currentIrpStack->Parameters.Read.Length;
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
|
|
cdaudioDataOut->Length[0] = Toc[0];
|
|
cdaudioDataOut->Length[1] = Toc[1];
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[2]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[3]);
|
|
|
|
for( i=0;
|
|
i<=( (ULONG)cdaudioDataOut->LastTrack -
|
|
(ULONG)cdaudioDataOut->FirstTrack );
|
|
i++
|
|
) {
|
|
|
|
//
|
|
// Grab Information for each track
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = Toc[(i*8)+4+1];
|
|
cdaudioDataOut->TrackData[i].TrackNumber = BCD_TO_DEC(Toc[(i*8)+4+2]);
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[(i*8)+4+5];
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[(i*8)+4+6];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[(i*8)+4+7];
|
|
|
|
}
|
|
|
|
//
|
|
// Copy "lead out track" info
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = Toc[(i*8)+4+1];
|
|
cdaudioDataOut->TrackData[i].TrackNumber = Toc[(i*8)+4+2]; /* leave as 0xAA */
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[(i*8)+4+5];
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[(i*8)+4+6];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[(i*8)+4+7];
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( Toc );
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
{
|
|
PSUB_Q_CURRENT_POSITION userPtr =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
sizeof(SUB_Q_CURRENT_POSITION)
|
|
);
|
|
|
|
CdDump(( 5,
|
|
"CdAudio535DeviceControl: IOCTL_CDROM_READ_Q_CHANNEL received.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio535DeviceControl: READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio535DeviceControl: READ_Q_CHANNEL, illegal Format (%d)\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
//
|
|
// Get Current Position
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->SUBCHANNEL.OperationCode = CDS535_READ_SUB_Q_CHANNEL_CODE;
|
|
cdb->SUBCHANNEL.Msf = 1;
|
|
cdb->SUBCHANNEL.SubQ = 1;
|
|
cdb->SUBCHANNEL.Format = 1;
|
|
cdb->SUBCHANNEL.AllocationLength[1] = sizeof(SUB_Q_CURRENT_POSITION);
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CURRENT_POSITION),
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// Copy current position, converting track and index from BCD
|
|
//
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
if(SubQPtr[1] == 0x11) deviceExtension->PlayActive = TRUE;
|
|
else deviceExtension->PlayActive = FALSE;
|
|
|
|
userPtr->Header.Reserved = 0;
|
|
userPtr->Header.AudioStatus = SubQPtr[1];
|
|
userPtr->Header.DataLength[0] = 0;
|
|
userPtr->Header.DataLength[1] = 12;
|
|
userPtr->FormatCode = 0x01;
|
|
userPtr->Control = SubQPtr[5];
|
|
userPtr->ADR = 0;
|
|
userPtr->TrackNumber = BCD_TO_DEC(SubQPtr[6]);
|
|
userPtr->IndexNumber = BCD_TO_DEC(SubQPtr[7]);
|
|
userPtr->AbsoluteAddress[0] = 0;
|
|
userPtr->AbsoluteAddress[1] = SubQPtr[9];
|
|
userPtr->AbsoluteAddress[2] = SubQPtr[10];
|
|
userPtr->AbsoluteAddress[3] = SubQPtr[11];
|
|
userPtr->TrackRelativeAddress[0] = 0;
|
|
userPtr->TrackRelativeAddress[1] = SubQPtr[13];
|
|
userPtr->TrackRelativeAddress[2] = SubQPtr[14];
|
|
userPtr->TrackRelativeAddress[3] = SubQPtr[15];
|
|
Irp->IoStatus.Information = 16;
|
|
} else {
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
CdDump(( 1,
|
|
"CdAudio535DeviceControl: READ_Q_CHANNEL failed (0x%08lX)\n",
|
|
status
|
|
));
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
|
|
{
|
|
PCDROM_PLAY_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// Play Audio MSF
|
|
//
|
|
|
|
DebugPrint((2,"ScsiCdRomDeviceControl: Play audio MSF\n"));
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(CDROM_PLAY_AUDIO_MSF)) {
|
|
|
|
//
|
|
// Indicate unsuccessful status.
|
|
//
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
if (inputBuffer->StartingM == inputBuffer->EndingM &&
|
|
inputBuffer->StartingS == inputBuffer->EndingS &&
|
|
inputBuffer->StartingF == inputBuffer->EndingF) {
|
|
|
|
cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
|
|
cdb->PAUSE_RESUME.Action = CDB_AUDIO_PAUSE;
|
|
|
|
} else {
|
|
cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_AUDIO_MSF;
|
|
|
|
cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
|
|
cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
|
|
cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;
|
|
|
|
cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
|
|
cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
|
|
cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;
|
|
|
|
}
|
|
|
|
srb.CdbLength = 10;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
|
|
status = ScsiClassSendSrbSynchronous(DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE);
|
|
|
|
if (NT_SUCCESS(status) &&
|
|
cdb->PLAY_AUDIO_MSF.OperationCode == SCSIOP_PLAY_AUDIO_MSF) {
|
|
deviceExtension->PlayActive = TRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudio535DeviceControl: IOCTL_CDROM_SEEK_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// Use the data seek command to move the pickup
|
|
// NOTE: This blithely assumes logical block size == 2048 bytes.
|
|
//
|
|
|
|
destblock = ((((ULONG)(inputBuffer->M) * 60)
|
|
+ (ULONG)(inputBuffer->S)) * 75)
|
|
+ (ULONG)(inputBuffer->F)
|
|
- 150;
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->SEEK.OperationCode = SCSIOP_SEEK;
|
|
cdb->SEEK.LogicalBlockAddress[0] = (UCHAR)(destblock >> 24) & 0xFF;
|
|
cdb->SEEK.LogicalBlockAddress[1] = (UCHAR)(destblock >> 16) & 0xFF;
|
|
cdb->SEEK.LogicalBlockAddress[2] = (UCHAR)(destblock >> 8) & 0xFF;
|
|
cdb->SEEK.LogicalBlockAddress[3] = (UCHAR)(destblock & 0xFF);
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 3,
|
|
"CdAudio535DeviceControl: SEEK failed (0x%08lX)\n",
|
|
status ));
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
//
|
|
// Build cdb to eject cartridge
|
|
//
|
|
|
|
CdDump(( 3,
|
|
"CdAudio535DeviceControl: IOCTL_CDROM_EJECT_MEDIA received.\n"
|
|
));
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS535_EJECT_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
CdDump(( 3, "CdAudio535DeviceControl: Not Supported yet.\n" ));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
CdAudioIsPlayActive(DeviceObject);
|
|
|
|
default:
|
|
|
|
CdDump((5,"ChCdAudio535DeviceControl: Unsupported device IOCTL\n"));
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
} // end switch( IOCTL )
|
|
|
|
SetStatusAndReturn:
|
|
//
|
|
// set status code and return
|
|
//
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp,
|
|
deviceExtension->TargetDeviceObject
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdAudio435DeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by CdAudioDeviceControl to handle
|
|
audio IOCTLs sent to Chinon CDS-435 cdrom drive.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PCDROM_TOC cdaudioDataOut = Irp->AssociatedIrp.SystemBuffer;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB cdb = (PCDB)srb.Cdb;
|
|
NTSTATUS status;
|
|
ULONG i,bytesTransfered;
|
|
PUCHAR Toc;
|
|
|
|
//
|
|
// Clear out cdb
|
|
//
|
|
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
|
|
//
|
|
// What IOCTL do we need to execute?
|
|
//
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_CDROM_READ_TOC:
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_READ_TOC received.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Allocate storage to hold TOC from disc
|
|
//
|
|
|
|
Toc = (PUCHAR)ExAllocatePool( NonPagedPoolCacheAligned,
|
|
CDROM_TOC_SIZE
|
|
);
|
|
|
|
if (Toc==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up defaults
|
|
//
|
|
|
|
RtlZeroMemory( Toc, CDROM_TOC_SIZE );
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
cdb->READ_TOC.OperationCode = CDS435_READ_TOC_CODE;
|
|
cdb->READ_TOC.Msf = 1;
|
|
cdb->READ_TOC.AllocationLength[0] = (CDROM_TOC_SIZE >> 8);
|
|
cdb->READ_TOC.AllocationLength[1] = (CDROM_TOC_SIZE & 0xFF);
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
srb.CdbLength = 10;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
Toc,
|
|
CDROM_TOC_SIZE,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status) && (status!=STATUS_DATA_OVERRUN)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio435DeviceControl: READ_TOC error (0x%08lX)\n",
|
|
status ));
|
|
|
|
if (SRB_STATUS(srb.SrbStatus)!=SRB_STATUS_DATA_OVERRUN) {
|
|
|
|
CdDump(( 1, "CdAudio435DeviceControl: SRB ERROR (0x%lX)\n",
|
|
srb.SrbStatus ));
|
|
ExFreePool( Toc );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
} else {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Translate data into SCSI-II format
|
|
// (track numbers, except 0xAA, must be converted from BCD)
|
|
|
|
bytesTransfered =
|
|
currentIrpStack->Parameters.Read.Length > sizeof(CDROM_TOC) ?
|
|
sizeof(CDROM_TOC) : currentIrpStack->Parameters.Read.Length;
|
|
Irp->IoStatus.Information = bytesTransfered;
|
|
|
|
cdaudioDataOut->Length[0] = Toc[0];
|
|
cdaudioDataOut->Length[1] = Toc[1];
|
|
cdaudioDataOut->FirstTrack = BCD_TO_DEC(Toc[2]);
|
|
cdaudioDataOut->LastTrack = BCD_TO_DEC(Toc[3]);
|
|
|
|
for( i=0;
|
|
i<=( (ULONG)cdaudioDataOut->LastTrack -
|
|
(ULONG)cdaudioDataOut->FirstTrack );
|
|
i++
|
|
) {
|
|
|
|
//
|
|
// Grab Information for each track
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = Toc[(i*8)+4+1];
|
|
cdaudioDataOut->TrackData[i].TrackNumber = BCD_TO_DEC(Toc[(i*8)+4+2]);
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[(i*8)+4+5];
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[(i*8)+4+6];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[(i*8)+4+7];
|
|
|
|
}
|
|
|
|
//
|
|
// Copy "lead out track" info
|
|
//
|
|
|
|
cdaudioDataOut->TrackData[i].Reserved = 0;
|
|
cdaudioDataOut->TrackData[i].Control = Toc[(i*8)+4+1];
|
|
cdaudioDataOut->TrackData[i].TrackNumber = Toc[(i*8)+4+2]; /* leave as 0xAA */
|
|
cdaudioDataOut->TrackData[i].Reserved1 = 0;
|
|
cdaudioDataOut->TrackData[i].Address[0] = 0;
|
|
cdaudioDataOut->TrackData[i].Address[1] = Toc[(i*8)+4+5];
|
|
cdaudioDataOut->TrackData[i].Address[2] = Toc[(i*8)+4+6];
|
|
cdaudioDataOut->TrackData[i].Address[3] = Toc[(i*8)+4+7];
|
|
|
|
//
|
|
// Clear out deviceExtension data
|
|
//
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( Toc );
|
|
break;
|
|
|
|
case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
case IOCTL_CDROM_STOP_AUDIO:
|
|
{
|
|
|
|
PCDROM_PLAY_AUDIO_MSF inputBuffer =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS435_STOP_AUDIO_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: STOP failed (0x%08lX)\n",
|
|
status ));
|
|
|
|
}
|
|
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_CDROM_STOP_AUDIO
|
|
) {
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_STOP_AUDIO received.\n"
|
|
));
|
|
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_PLAY_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PLAY_AUDIO_MSF.OperationCode = CDS435_PLAY_AUDIO_EXTENDED_CODE;
|
|
cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->StartingM;
|
|
cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->StartingS;
|
|
cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->StartingF;
|
|
cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->EndingM;
|
|
cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->EndingS;
|
|
cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->EndingF;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
//
|
|
// Set last play ending address for next pause command
|
|
//
|
|
|
|
deviceExtension->LastEndM = inputBuffer->EndingM;
|
|
deviceExtension->LastEndS = inputBuffer->EndingS;
|
|
deviceExtension->LastEndF = inputBuffer->EndingF;
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: PLAY ==> BcdLastEnd set to (%x %x %x)\n",
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF ));
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: PLAY failed (0x%08lX)\n",
|
|
status ));
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
{
|
|
|
|
PCDROM_SEEK_AUDIO_MSF inputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_SEEK_AUDIO_MSF received.\n"
|
|
));
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS435_PLAY_AUDIO_EXTENDED_CODE;
|
|
cdb->PLAY_AUDIO_MSF.StartingM = inputBuffer->M;
|
|
cdb->PLAY_AUDIO_MSF.StartingS = inputBuffer->S;
|
|
cdb->PLAY_AUDIO_MSF.StartingF = inputBuffer->F;
|
|
cdb->PLAY_AUDIO_MSF.EndingM = inputBuffer->M;
|
|
cdb->PLAY_AUDIO_MSF.EndingS = inputBuffer->S;
|
|
cdb->PLAY_AUDIO_MSF.EndingF = inputBuffer->F;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->Paused = CDAUDIO_PAUSED;
|
|
deviceExtension->PausedM = inputBuffer->M;
|
|
deviceExtension->PausedS = inputBuffer->S;
|
|
deviceExtension->PausedF = inputBuffer->F;
|
|
deviceExtension->LastEndM = inputBuffer->M;
|
|
deviceExtension->LastEndS = inputBuffer->S;
|
|
deviceExtension->LastEndF = inputBuffer->F;
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: SEEK, Paused (%x %x %x) LastEnd (%x %x %x)\n",
|
|
deviceExtension->PausedM,
|
|
deviceExtension->PausedS,
|
|
deviceExtension->PausedF,
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF ));
|
|
|
|
} else {
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: SEEK failed (0x%08lX)\n",
|
|
status ));
|
|
|
|
//
|
|
// The CDS-435 drive returns STATUS_INVALID_DEVICE_REQUEST
|
|
// when we ask to play an invalid address, so we need
|
|
// to map to STATUS_NONEXISTENT_SECTOR in order to be
|
|
// consistent with the other drives.
|
|
//
|
|
|
|
if (status==STATUS_INVALID_DEVICE_REQUEST) {
|
|
|
|
status = STATUS_NONEXISTENT_SECTOR;
|
|
}
|
|
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_PAUSE_AUDIO:
|
|
{
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
sizeof(SUB_Q_CHANNEL_DATA)
|
|
);
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_PAUSE_AUDIO received.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Enter pause (still ) mode
|
|
//
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_PAUSED) {
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: PAUSE: Already Paused!\n"
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
status = STATUS_SUCCESS;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Since the CDS-435 doesn't have a pause mode,
|
|
// we'll just record the current position and
|
|
// stop the drive.
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->SUBCHANNEL.OperationCode = CDS435_READ_SUB_Q_CHANNEL_CODE;
|
|
cdb->SUBCHANNEL.Msf = 1;
|
|
cdb->SUBCHANNEL.SubQ = 1;
|
|
cdb->SUBCHANNEL.AllocationLength[1] = sizeof(SUB_Q_CHANNEL_DATA);
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CHANNEL_DATA),
|
|
FALSE
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio435DeviceControl: Pause, Read Q Channel failed (0x%lx)\n",
|
|
status ));
|
|
ExFreePool( SubQPtr );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
deviceExtension->PausedM = SubQPtr[9];
|
|
deviceExtension->PausedS = SubQPtr[10];
|
|
deviceExtension->PausedF = SubQPtr[11];
|
|
|
|
//
|
|
// now stop audio
|
|
//
|
|
RtlZeroMemory( cdb, MAXIMUM_CDB_SIZE );
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS435_STOP_AUDIO_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio435DeviceControl: PAUSE, StopAudio failed! (0x%08lX)\n",
|
|
status ));
|
|
ExFreePool( SubQPtr );
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
deviceExtension->Paused = CDAUDIO_PAUSED;
|
|
deviceExtension->PausedM = SubQPtr[9];
|
|
deviceExtension->PausedS = SubQPtr[10];
|
|
deviceExtension->PausedF = SubQPtr[11];
|
|
|
|
CdDump((3,
|
|
"CdAudio435DeviceControl: PAUSE ==> Paused set to (%x %x %x)\n",
|
|
deviceExtension->PausedM,
|
|
deviceExtension->PausedS,
|
|
deviceExtension->PausedF ));
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_RESUME_AUDIO:
|
|
|
|
//
|
|
// Resume cdrom
|
|
//
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_RESUME_AUDIO received.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// Since the CDS-435 doesn't have a resume IOCTL,
|
|
// we'll just start playing (if paused) from the
|
|
// last recored paused position to the last recorded
|
|
// "end of play" position.
|
|
//
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_NOT_PAUSED) {
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
//
|
|
// Fill in cdb for this operation
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->PLAY_AUDIO_MSF.OperationCode = CDS435_PLAY_AUDIO_EXTENDED_CODE;
|
|
cdb->PLAY_AUDIO_MSF.StartingM = deviceExtension->PausedM;
|
|
cdb->PLAY_AUDIO_MSF.StartingS = deviceExtension->PausedS;
|
|
cdb->PLAY_AUDIO_MSF.StartingF = deviceExtension->PausedF;
|
|
cdb->PLAY_AUDIO_MSF.EndingM = deviceExtension->LastEndM;
|
|
cdb->PLAY_AUDIO_MSF.EndingS = deviceExtension->LastEndS;
|
|
cdb->PLAY_AUDIO_MSF.EndingF = deviceExtension->LastEndF;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
|
|
} else {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio435DeviceControl: RESUME (%x %x %x) - (%x %x %x) failed (0x%08lX)\n",
|
|
deviceExtension->PausedM,
|
|
deviceExtension->PausedS,
|
|
deviceExtension->PausedF,
|
|
deviceExtension->LastEndM,
|
|
deviceExtension->LastEndS,
|
|
deviceExtension->LastEndF,
|
|
status ));
|
|
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
{
|
|
PSUB_Q_CURRENT_POSITION userPtr =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
PUCHAR SubQPtr =
|
|
ExAllocatePool( NonPagedPoolCacheAligned,
|
|
sizeof(SUB_Q_CHANNEL_DATA)
|
|
);
|
|
|
|
CdDump(( 5,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_READ_Q_CHANNEL received.\n"
|
|
));
|
|
|
|
if (SubQPtr==NULL) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio435DeviceControl: READ_Q_CHANNEL, SubQPtr==NULL!\n"
|
|
));
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto SetStatusAndReturn;
|
|
|
|
}
|
|
|
|
if ( ((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format!=
|
|
IOCTL_CDROM_CURRENT_POSITION) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudio435DeviceControl: READ_Q_CHANNEL, illegal Format (%d)\n",
|
|
((PCDROM_SUB_Q_DATA_FORMAT)userPtr)->Format
|
|
));
|
|
|
|
ExFreePool( SubQPtr );
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
status = STATUS_UNSUCCESSFUL;
|
|
goto SetStatusAndReturn;
|
|
}
|
|
|
|
//
|
|
// Read audio play status
|
|
//
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->SUBCHANNEL.OperationCode = CDS435_READ_SUB_Q_CHANNEL_CODE;
|
|
cdb->SUBCHANNEL.Msf = 1;
|
|
cdb->SUBCHANNEL.SubQ = 1;
|
|
cdb->SUBCHANNEL.AllocationLength[1] = sizeof(SUB_Q_CHANNEL_DATA);
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
SubQPtr,
|
|
sizeof(SUB_Q_CHANNEL_DATA),
|
|
FALSE
|
|
);
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
userPtr->Header.Reserved = 0;
|
|
|
|
if (deviceExtension->Paused==CDAUDIO_PAUSED) {
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PAUSED;
|
|
|
|
} else {
|
|
|
|
if (SubQPtr[1] == 0x11) {
|
|
|
|
deviceExtension->PlayActive = TRUE;
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_IN_PROGRESS;
|
|
|
|
} else {
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
userPtr->Header.AudioStatus = AUDIO_STATUS_PLAY_COMPLETE;
|
|
|
|
}
|
|
}
|
|
|
|
userPtr->Header.DataLength[0] = 0;
|
|
userPtr->Header.DataLength[1] = 12;
|
|
|
|
userPtr->FormatCode = 0x01;
|
|
userPtr->Control = SubQPtr[5];
|
|
userPtr->ADR = 0;
|
|
userPtr->TrackNumber = BCD_TO_DEC(SubQPtr[6]);
|
|
userPtr->IndexNumber = BCD_TO_DEC(SubQPtr[7]);
|
|
userPtr->AbsoluteAddress[0] = 0;
|
|
userPtr->AbsoluteAddress[1] = SubQPtr[9];
|
|
userPtr->AbsoluteAddress[2] = SubQPtr[10];
|
|
userPtr->AbsoluteAddress[3] = SubQPtr[11];
|
|
userPtr->TrackRelativeAddress[0] = 0;
|
|
userPtr->TrackRelativeAddress[1] = SubQPtr[13];
|
|
userPtr->TrackRelativeAddress[2] = SubQPtr[14];
|
|
userPtr->TrackRelativeAddress[3] = SubQPtr[15];
|
|
Irp->IoStatus.Information = 16;
|
|
|
|
} else {
|
|
|
|
RtlZeroMemory( userPtr, sizeof(SUB_Q_CURRENT_POSITION) );
|
|
CdDump(( 1,
|
|
"CdAudio435DeviceControl: READ_Q_CHANNEL failed (0x%08lX)\n",
|
|
status
|
|
));
|
|
|
|
|
|
}
|
|
|
|
ExFreePool( SubQPtr );
|
|
}
|
|
break;
|
|
|
|
case IOCTL_CDROM_EJECT_MEDIA:
|
|
|
|
//
|
|
// Build cdb to eject cartridge
|
|
//
|
|
|
|
CdDump(( 3,
|
|
"CdAudio435DeviceControl: IOCTL_CDROM_EJECT_MEDIA received.\n"
|
|
));
|
|
|
|
srb.CdbLength = 10;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
cdb->CDB10.OperationCode = CDS435_EJECT_CODE;
|
|
status = ScsiClassSendSrbSynchronous( deviceExtension->DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
deviceExtension->Paused = CDAUDIO_NOT_PAUSED;
|
|
deviceExtension->PausedM = 0;
|
|
deviceExtension->PausedS = 0;
|
|
deviceExtension->PausedF = 0;
|
|
deviceExtension->LastEndM = 0;
|
|
deviceExtension->LastEndS = 0;
|
|
deviceExtension->LastEndF = 0;
|
|
|
|
break;
|
|
|
|
case IOCTL_CDROM_GET_CONTROL:
|
|
case IOCTL_CDROM_GET_VOLUME:
|
|
case IOCTL_CDROM_SET_VOLUME:
|
|
CdDump(( 3, "CdAudio435DeviceControl: Not Supported yet.\n" ));
|
|
status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
|
|
CdDump(( 3, "CdAudio435DeviceControl: IOCTL_CDROM_CHECK_VERIFY received.\n"
|
|
));
|
|
|
|
|
|
//
|
|
// Update the play active flag.
|
|
//
|
|
|
|
|
|
if(CdAudioIsPlayActive(DeviceObject) == TRUE) {
|
|
deviceExtension->PlayActive = TRUE;
|
|
status = STATUS_SUCCESS; // media must be in place if audio
|
|
goto SetStatusAndReturn; // is playing
|
|
}
|
|
else {
|
|
deviceExtension->PlayActive = FALSE;
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
CdDump((5,"CdAudio435DeviceControl: Unsupported device IOCTL\n"));
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
|
|
} // end switch( IOCTL )
|
|
|
|
SetStatusAndReturn:
|
|
//
|
|
// set status code and return
|
|
//
|
|
|
|
if (status == STATUS_VERIFY_REQUIRED) {
|
|
|
|
IoSetHardErrorOrVerifyDevice( Irp,
|
|
deviceExtension->TargetDeviceObject
|
|
);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
CdAudioPan533DeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PCDROM_TOC cdaudioDataOut = Irp->AssociatedIrp.SystemBuffer;
|
|
PREAD_CAPACITY_DATA lastSession;
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB lastSessionCdb = (PCDB)srb.Cdb;
|
|
ULONG bytesTransferred;
|
|
|
|
CdDump ((3,"CdAudioPan533DeviceControl: IoControl %x.\n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode));
|
|
|
|
//
|
|
// The Panasonic only needs cdaudio for GET_LAST_SESSION requests.
|
|
//
|
|
|
|
switch (currentIrpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_CDROM_GET_LAST_SESSION:
|
|
|
|
//
|
|
// If the cd is playing music then reject this request.
|
|
//
|
|
|
|
if (CdAudioIsPlayActive(DeviceObject)) {
|
|
status = STATUS_DEVICE_BUSY;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Verify that the request length is of correct size.
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.Read.Length <
|
|
sizeof(CDROM_TOC)) {
|
|
|
|
//
|
|
// Indicate status and no information transferred.
|
|
//
|
|
|
|
status = STATUS_INFO_LENGTH_MISMATCH;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Verify that user buffer is large enough.
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(CDROM_TOC)) {
|
|
|
|
//
|
|
// Indicate status and no information transferred.
|
|
//
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
}
|
|
|
|
// Allocate storage to hold lastSession from disc
|
|
//
|
|
|
|
lastSession = ExAllocatePool( NonPagedPoolCacheAligned,
|
|
sizeof(READ_CAPACITY_DATA)
|
|
);
|
|
|
|
if (lastSession==NULL) {
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Information = 0;
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
RtlZeroMemory( lastSession, sizeof(READ_CAPACITY_DATA));
|
|
srb.CdbLength = 10;
|
|
bytesTransferred = sizeof(READ_CAPACITY_DATA);
|
|
|
|
//
|
|
// Fill in CDB
|
|
//
|
|
|
|
RtlZeroMemory( lastSessionCdb, 10);
|
|
lastSessionCdb->READ_TOC.OperationCode = CR533_READ_LAST_SESSION;
|
|
lastSessionCdb->READ_TOC.AllocationLength[0] = (UCHAR) bytesTransferred >> 8;
|
|
lastSessionCdb->READ_TOC.AllocationLength[1] = (UCHAR) bytesTransferred & 0xFF;
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
status = ScsiClassSendSrbSynchronous(
|
|
deviceExtension->DeviceObject,
|
|
&srb,
|
|
lastSession,
|
|
bytesTransferred,
|
|
FALSE
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioPanCR533DeviceControl: READ_LAST_SESSION error (0x%08lX)\n",
|
|
status ));
|
|
|
|
|
|
ExFreePool( lastSession );
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Translate data into our format.
|
|
//
|
|
|
|
bytesTransferred = FIELD_OFFSET(CDROM_TOC, TrackData[1]);
|
|
Irp->IoStatus.Information = bytesTransferred;
|
|
|
|
RtlZeroMemory(cdaudioDataOut, bytesTransferred);
|
|
|
|
cdaudioDataOut->Length[0] = (UCHAR)(bytesTransferred >> 8);
|
|
cdaudioDataOut->Length[1] = (UCHAR)(bytesTransferred & 0xFF);
|
|
|
|
//
|
|
// Determine if this is a multisession cd.
|
|
//
|
|
|
|
if (!(lastSession->LogicalBlockAddress & CR533_MULTI_FLAG)) {
|
|
|
|
//
|
|
// This is a single session disk. Just return.
|
|
//
|
|
|
|
ExFreePool(lastSession);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Fake the session information.
|
|
//
|
|
|
|
cdaudioDataOut->FirstTrack = 1;
|
|
cdaudioDataOut->LastTrack = 2;
|
|
|
|
//
|
|
// Grab Information for the last session.
|
|
//
|
|
|
|
*((ULONG *)&cdaudioDataOut->TrackData[0].Address[0]) =
|
|
lastSession->BytesPerBlock;
|
|
|
|
//
|
|
// Free storage now that we've stored it elsewhere
|
|
//
|
|
|
|
ExFreePool( lastSession );
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioAtapiDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
SCSI_REQUEST_BLOCK srb;
|
|
PCDB12 cdb = (PCDB12)srb.Cdb;
|
|
|
|
CdDump ((3,"CdAudioAtapiDeviceControl: IoControl %x.\n",
|
|
currentIrpStack->Parameters.DeviceIoControl.IoControlCode));
|
|
|
|
//
|
|
// The Atapi devices supported only need remapping of IOCTL_CDROM_STOP_AUDIO
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_STOP_AUDIO) {
|
|
|
|
deviceExtension->PlayActive = FALSE;
|
|
|
|
//
|
|
// Zero and fill in new Srb
|
|
//
|
|
|
|
RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
//
|
|
// Issue the Atapi STOP_PLAY command.
|
|
//
|
|
|
|
cdb->STOP_PLAY.OperationCode = 0x4E;
|
|
|
|
srb.CdbLength = 12;
|
|
|
|
//
|
|
// Set timeout value.
|
|
//
|
|
|
|
srb.TimeOutValue = AUDIO_TIMEOUT;
|
|
|
|
status = ScsiClassSendSrbSynchronous(DeviceObject,
|
|
&srb,
|
|
NULL,
|
|
0,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
CdDump(( 1,
|
|
"CdAudioAtapiDeviceControl: STOP_AUDIO error (0x%08lX)\n",
|
|
status ));
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return status;
|
|
|
|
}
|
|
|
|
VOID
|
|
HpCdrProcessLastSession(
|
|
IN PCDROM_TOC Toc
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fixes up the multi session table of contents when the
|
|
session data is returned for HP CDR 4020i drives.
|
|
|
|
Arguments:
|
|
|
|
Toc - the table of contents buffer returned from the drive.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG index;
|
|
PUCHAR cp;
|
|
|
|
index = Toc->FirstTrack;
|
|
|
|
if (index) {
|
|
index--;
|
|
|
|
//
|
|
// Fix up the TOC information from the HP method to how it is
|
|
// interpreted by the file systems.
|
|
//
|
|
|
|
Toc->FirstTrack = Toc->TrackData[0].Reserved;
|
|
Toc->LastTrack = Toc->TrackData[index].Reserved;
|
|
Toc->TrackData[0] = Toc->TrackData[index];
|
|
} else {
|
|
Toc->FirstTrack = Toc->LastTrack = 0;
|
|
}
|
|
|
|
DebugPrint((2, "HP TOC data for last session\n"));
|
|
for (cp = (PUCHAR) Toc, index = 0; index < 12; index++, cp++) {
|
|
DebugPrint((2, "%2x ", *cp));
|
|
}
|
|
DebugPrint((2, "\n"));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
HPCdrCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the I/O request has completed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - SimBad device object.
|
|
Irp - Completed request.
|
|
Context - not used. Set up to also be a pointer to the DeviceObject.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(Context);
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
if (Irp->PendingReturned) {
|
|
|
|
IoMarkIrpPending( Irp );
|
|
|
|
}
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
HpCdrProcessLastSession((PCDROM_TOC)Irp->AssociatedIrp.SystemBuffer);
|
|
}
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CdAudioHPCdrDeviceControl(
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by CdAudioDeviceControl to handle
|
|
audio IOCTLs sent to the HPCdr device - this specifically handles
|
|
session data for multi session support.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject
|
|
Irp
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|
PCD_DEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Is this a GET_LAST_SESSION request
|
|
//
|
|
|
|
if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode == IOCTL_CDROM_GET_LAST_SESSION) {
|
|
|
|
//
|
|
// Copy stack parameters to next stack.
|
|
//
|
|
|
|
RtlMoveMemory(nextIrpStack,
|
|
currentIrpStack,
|
|
sizeof(IO_STACK_LOCATION));
|
|
|
|
//
|
|
// Set IRP so IoComplete calls our completion routine.
|
|
//
|
|
|
|
IoSetCompletionRoutine(Irp,
|
|
HPCdrCompletion,
|
|
deviceExtension,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
//
|
|
// Send this to next driver layer and process on completion.
|
|
//
|
|
|
|
return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
|
|
|
|
} else {
|
|
|
|
return CdAudioSendToNextDriver( DeviceObject, Irp );
|
|
|
|
}
|
|
|
|
//
|
|
// Cannot get here
|
|
//
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
#if DBG
|
|
VOID
|
|
CdAudioDebugPrint(
|
|
ULONG DebugPrintLevel,
|
|
PCCHAR DebugMessage,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Debug print for CdAudio driver
|
|
|
|
Arguments:
|
|
|
|
Debug print level between 0 and 3, with 3 being the most verbose.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
va_list ap;
|
|
|
|
va_start( ap, DebugMessage );
|
|
|
|
if (DebugPrintLevel <= CdAudioDebug) {
|
|
|
|
char buffer[128];
|
|
|
|
vsprintf(buffer, DebugMessage, ap);
|
|
DbgPrint(buffer);
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
} // end CdAudioDebugPrint
|
|
|
|
#endif // DBG
|